Image generation (OpenAI Images API)
Synchronous image generation in the OpenAI Images API format: parameters, a wait of up to 300 seconds, billing only for a finished image, Cherry Studio setup. Editing goes through the async /api/media/generate.
POST /v1/images/generations generates images in the OpenAI Images API format: you send a request and get the image back in the same response. It is an adapter over the same queue as POST /api/media/generate: models, price, the balance check, supplier failover and the charge are shared, so the same generation costs the same on both addresses.
curl https://getvibeapi.com/v1/images/generations \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"nano-banana","prompt":"a red bicycle on a bridge","n":1,"size":"1024x1024"}'Response (HTTP 200):
{
"created": 1792706400,
"data": [{ "url": "https://getvibeapi.com/media/r/…/0.png" }]
}Pass the key as Authorization: Bearer sk-pv-… or x-api-key. The official OpenAI SDK works as is:
from openai import OpenAI
client = OpenAI(base_url="https://getvibeapi.com/v1", api_key="sk-pv-...", timeout=600)
image = client.images.generate(model="nano-banana", prompt="a red bicycle on a bridge", size="1024x1024")
print(image.data[0].url)Body parameters
| Parameter | Type | Description |
|---|---|---|
modelreq. | string | An image model id from /api/media-catalog (kind: "image"), e.g. nano-banana, gpt-image-2, flux-2. A vendor-prefixed id (google/nano-banana) and a pinned variant (gpt-image-2:network) are accepted too. Video and audio models are refused here with 400. |
promptreq. | string | Description of the image. |
n | number | Only 1: each generation is a separate job with its own price. For several images, send several requests. |
size | string | Honoured as an aspect ratio: 1024x1024 → 1:1, 1792x1024 → 16:9, 1024x1792 → 9:16, 1536x1024 → 3:2, 1024x1536 → 2:3; other values are reduced exactly (1920x1080 → 16:9). A ratio the model does not offer is refused with the list of supported sizes — there is no nearest-match substitution. "auto" or no field means the model default. |
quality | string | Only for models with a quality parameter in the catalog; an unsupported value is refused. "auto" omits it. |
response_format | "url" | "b64_json" | url by default. b64_json returns the file in the response body (up to 20 MB). |
user | string | Accepted and ignored. |
Unknown fields, and background, style, output_format, output_compression, moderation, stream and partial_images are refused with 400 by name rather than silently dropped — otherwise you would pay for an image you did not ask for. Models that need a source image (editing, upscaling, inpainting) do not work on this address, because there is no way to pass an image here; use POST /api/media/generate.
Waiting and timeout
The request is held open until the job finishes, for up to 300 seconds. The median time is 15 to 85 seconds depending on the model; individual generations on the slowest models take 3–4 minutes. Set your client timeout to at least 300 seconds (the OpenAI SDK default is 600).
- Every response, including errors after the job was created, carries the
x-pv-media-job-idheader with the job id. - If the job did not finish within 300 seconds, the answer is
504withcause: image_generation_timeoutand the job id in the message. The generation keeps running: collect it fromGET /api/media/jobs/{id}. - If the connection drops (app timeout, network), the job is still taken to completion.
GET /v1/images/generations?limit=20(up to 100) lists this key's recent images with their status, links and the charged amountcost_rub.
Billing
Nothing is charged at submission. Only a successfully completed generation is charged, at the same price as in /api/media-catalog and in the POST /api/media/price quote. A failed generation returns 502 saying "Nothing was charged" and costs nothing. The charged amount is returned in the x-pv-cost-rub header. If the balance cannot cover the generation, the answer is 402 before any job starts.
data[].url point to our domain and live for 7 days after the generation completes, then answer 410 media_result_expired. Save the files on your side.Errors
| Parameter | Type | Description |
|---|---|---|
400 | invalid_request_error | Invalid body: unknown_parameter, unsupported_parameter, unsupported_n, unsupported_size, unsupported_quality, unsupported_model_kind. Fix the request. |
401 / 403 | authentication_error / permission_error | Invalid key, or the calling IP is not on the key allowlist. |
402 | insufficient_quota | Not enough balance for this generation. No job was created. |
404 | not_found_error | model_not_found — no such image model. See /api/media-catalog. |
429 | rate_limit_error | Media rate limit; retry after Retry-After. |
502 | upstream_error | The generation failed. Nothing was charged; you can retry. |
503 | upstream_error | The model is unavailable at every supplier right now. No job was created; retry later or pick another model. |
504 | upstream_error | image_generation_timeout — the job is still running; collect it by the id from x-pv-media-job-id. |
Desktop apps (Cherry Studio and others)
Any app that can generate images through an OpenAI-compatible provider works. Using Cherry Studio as the example:
- Add a provider of type OpenAI with the API address
https://getvibeapi.com(the app appends/v1itself) and yoursk-pv-…key. - Add the model by hand, using an id from
https://getvibeapi.com/api/media-catalog(entries withkind: "image"only), e.g.nano-bananaorgpt-image-2. The app's model list shows text models; media models are not in it. - Generate from the app's painting / image generation feature, not from ordinary chat. Chat sends the request to
/v1/chat/completions, where image models do not work. - If the app lets you set a request timeout, set it to at least 300 seconds. An image the app gave up on is listed at
GET /v1/images/generations. Retrying from the app creates a new generation that is charged separately.
The mode that edits an uploaded picture in such apps calls /v1/images/edits, which is served as well (see below).
Editing: /v1/images/edits
POST /v1/images/edits takes the OpenAI SDK request (multipart/form-data: the file in image, or several in image[], with prompt, model, size, quality and response_format) or JSON with images: [{"image_url": "…"}]. It works like generation above: the same response, the same wait, the same x-pv-media-job-id and the same price as POST /api/media/generate with a source image; a failed edit is not charged. The model's edit mode is picked from the catalog. A mask is refused with 400 unsupported_parameter, and a model that takes no source image with model_cannot_edit. For a masked edit use POST /api/media/generate and poll GET /api/media/jobs/{id} — see the async generation guide. Video is not generated synchronously: /v1/video/generations and /v1/videos answer 410 as well (details).
Nano Banana Pro id
In the catalog and new code use nano-banana-pro. For migrating existing async clients, the value gemini-3-pro-image is still accepted only in the model field of the /api/media/generate request and resolves to the same public model. It is not published in discovery or pricing.