Image Generation Guide
API examples for GPT Image 2.5 Sunburst / Flare, gpt-image-2-count, gpt-image-2 and Gemini image models, plus the recommended asynchronous submission flow.
modelsok offers several AI image models. The base URL is https://modelsok.com, authenticated with Authorization: Bearer <API Key>. This page explains how to call each model and shares practical tips.
Available models
| Model | Notes | Endpoint |
|---|---|---|
gpt-image-2.5-sunburst | Text-to-image; verified 1024×1024 PNG output | OpenAI-compatible |
gpt-image-2.5-flare | Text-to-image; verified 1024×1024 PNG output | OpenAI-compatible |
gpt-image-2-count | Text-to-image / image editing, billed per call; output is fixed at about 1.5MP and size only sets the aspect ratio | OpenAI-compatible |
gpt-image-2 | OpenAI image model, output size matches the request exactly, up to 3840×2160 | OpenAI-compatible |
gemini-2.5-flash-image-preview | Gemini fast image | OpenAI-compatible / Gemini native |
gemini-2.5-flash-image | Gemini fast image | Gemini native |
gemini-3.1-flash-image-preview | Gemini fast image | Gemini native |
gemini-3-pro-image-preview | Gemini high-quality image | Gemini native |
Asynchronous submission (recommended)
A single image usually takes from tens of seconds to over two minutes. A synchronous call keeps the connection open until the image is ready, and cross-region networks or the CDN may drop it midway (clients typically see ReadError, a timeout or HTTP 524) even though generation may already have succeeded and been billed. Use asynchronous submission for any request that may take a long time: you get a task ID immediately and poll for the result.
Asynchronous submission works for both /v1/images/generations (text-to-image) and /v1/images/edits (image editing), with either JSON or multipart file-upload requests. The only difference from a synchronous call is the ?async=true suffix on the request URL; the request body stays the same.
Submit a task
Text-to-image (JSON):
curl "https://modelsok.com/v1/images/generations?async=true" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-image-2.5-sunburst",
"prompt": "An orange ceramic robot holding a sunflower against a blue background, studio product photography",
"n": 1,
"size": "1024x1024"
}'Image editing (multipart upload of a local reference image):
curl "https://modelsok.com/v1/images/edits?async=true" \
-H "Authorization: Bearer $API_KEY" \
-F "model=gpt-image-2-count" \
-F "prompt=make the mug matte black, keep the composition unchanged" \
-F "image=@reference.png" \
-F "size=1024x1536"Do not set Content-Type manually for multipart requests; let curl or your HTTP client generate the header with the boundary.
A successful submission returns task information within about one second. id is the task ID:
{
"id": "task_xxxxxxxx",
"state": "queued",
"progress": 0,
"create_time": 1794211200,
"update_time": 1794211200,
"action": "generate",
"data": { "images": null }
}Poll for the result
Query the task with the same API key used for submission:
curl "https://modelsok.com/v1/tasks/$TASK_ID" \
-H "Authorization: Bearer $API_KEY"Poll every 3–5 seconds. Possible state values:
state | Meaning | What to do |
|---|---|---|
queued | Submitted and waiting | Keep polling |
processing | Generating | Keep polling (see "Tasks that need manual review" below) |
succeeded | Generation succeeded | Read data.images |
failed | Generation failed | Read the top-level error; the charge is refunded automatically |
Successful response example:
{
"id": "task_xxxxxxxx",
"state": "succeeded",
"progress": 100,
"create_time": 1794211200,
"update_time": 1794211239,
"action": "generate",
"data": {
"images": [{ "url": "/api/relay-temp-images/xxxxxxxx.png" }]
},
"usage": {
"total_tokens": 430,
"input_tokens": 232,
"output_tokens": 198
}
}About the result fields:
- Asynchronous results always return image links in
data.images[].urland never returnb64_json. This differs from the synchronousdata[].b64_jsonstructure. - Where the image is stored depends on whether you have enabled Bring Your Own Storage:
- Enabled: the result is transferred to your own OSS / R2 bucket,
urlpoints to your bucket, and every query for the same task returns the same link. - Not enabled:
urlis a temporary platform link, usually a relative path (for example,/api/relay-temp-images/…png). Resolve it againsthttps://modelsok.combefore downloading. The link is valid for about 48 hours and each query may return a different link; all of them work. Save images promptly. - If transferring to your bucket fails (for example, an invalid key or a read-only bucket), the result falls back to a temporary platform link, so you still get the image.
- Enabled: the result is transferred to your own OSS / R2 bucket,
- Image-editing tasks also show
actionasgenerate. This is expected and does not mean the request was treated as text-to-image. - Each task is charged only once. If a query times out or the network drops, keep polling the original task ID. Do not resubmit, or a new billable task will be created.
Failures and refunds
When generation fails, state is failed, the error details are in the top-level error.code / error.message (not inside data), and data.images is empty. Any amount charged for the task is refunded automatically; you do not need to contact support.
{
"id": "task_xxxxxxxx",
"state": "failed",
"progress": 100,
"create_time": 1794211200,
"update_time": 1794211230,
"action": "generate",
"data": { "images": null },
"error": {
"code": "error_code",
"message": "error description"
}
}Adjust the request based on error.message (for example, the prompt, reference image or size) and submit again.
Tasks that need manual review
In rare cases a task stays in processing while the response also contains an error field with error.code set to post_dispatch_ambiguous. This means the task result needs manual review:
- Do not poll indefinitely; the task only reaches a final result after manual handling.
- Do not resubmit, to avoid being charged twice.
- Keep the task ID and contact support.
Known limitation
An API key with model restrictions enabled may receive a 403 model-access error while polling, even if generation succeeded. Use synchronous calls for these keys for now. Do not disable production key restrictions or resubmit an already-created task to work around this error. This is not a model-generation failure.
For synchronous calls, a 300-second client timeout does not extend the CDN timeout. Switch to asynchronous submission when you encounter HTTP 524.
GPT Image 2.5: Sunburst / Flare
Both models use the same endpoint and authentication. Switch models by changing only model. Text-to-image requests were verified for both models on September 9, 2026. The example below uses the verified minimal parameters. Check your account's model catalog for availability and pricing.
curl --max-time 300 https://modelsok.com/v1/images/generations \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-image-2.5-sunburst",
"prompt": "An orange ceramic robot holding a sunflower against a blue background, studio product photography",
"n": 1,
"size": "1024x1024"
}'For Flare, change model to gpt-image-2.5-flare. The synchronous response contains Base64 image data in data[0].b64_json; decode it to save a PNG. The example omits response_format and quality. To avoid long-connection timeouts, append ?async=true to the URL and submit asynchronously, as described in "Asynchronous submission (recommended)" above.
For these two models, verification currently covers one 1024×1024 text-to-image output per request. Other resolutions, quality tiers and performance differences between the two models have not been verified for this guide. Do not infer their limits, default quality or price multipliers from the gpt-image-2 section below.
gpt-image-2-count
A per-call billed image model that supports text-to-image (/v1/images/generations) and image editing (/v1/images/edits). Request parameters follow the OpenAI image API.
- Output size: fixed at about 1.5MP (1.5 million pixels). The pixel count in
sizeis ignored and only its aspect ratio is kept; for example,1024x1536and2048x3072both produce a 2:3 image of about 1.5MP. - Image editing: you can upload a local reference image directly via multipart (field name
image). See "Asynchronous submission (recommended)" above for an example. - Latency: usually 30–140 seconds per image. Always submit with
?async=trueto keep the connection from being dropped on cross-region networks. - Billing: a fixed amount per generation regardless of output size; failed asynchronous tasks are refunded automatically.
gpt-image-2
Size rules
The output size exactly matches the requested size. That size must satisfy all three conditions below, otherwise the API returns an explicit error (it never silently falls back to a different size):
| Condition | Error when violated |
|---|---|
| Width and height must both be multiples of 16 | Width and height must both be divisible by 16 |
| Total pixels between 655,360 and 8,294,400 | below / exceeds the current pixel budget |
| Longest edge ≤ 3840 | The longest edge must be less than or equal to 3840 |
Common sizes:
| Aspect ratio | Valid size |
|---|---|
| 1:1 | 1024x1024, 2048x2048 |
| 4:3 / 3:4 | 1024x768, 2048x1536 / 768x1024, 1536x2048 |
| 3:2 / 2:3 | 1536x1024 / 1024x1536 |
| 5:4 / 4:5 | 1280x1024 / 1024x1280 |
| 16:9 / 9:16 | 2048x1152, 3840x2160 / 1152x2048, 2160x3840 |
| 21:9 | 2688x1152 |
4096x4096 is rejected because it exceeds the longest-edge limit. Use 3840x2160 for 4K.
Quality tiers
The optional quality parameter accepts low / medium / high and defaults to low. It affects quality, latency and price at the same time, so specify it explicitly based on your use case.
| quality | Relative price | Latency (1024×1024) | Suggested use |
|---|---|---|---|
low (default) | 1× | ~26s | Drafts, batch previews |
medium | ~9× | ~43s | Everyday production |
high | ~36× | ~109s | Final deliverables |
Text to image
curl https://modelsok.com/v1/images/generations \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-image-2",
"prompt": "a red panda eating bamboo, flat vector illustration",
"size": "1024x1024",
"quality": "medium"
}'Image to image / editing
/v1/images/edits accepts reference images in two ways: send JSON with a publicly reachable image URL in the image field (pass an array for multiple reference images, up to 16; example below), or upload a local file with multipart (see the example in "Asynchronous submission (recommended)" above). For long-running requests, append ?async=true to the URL and submit asynchronously; see "Asynchronous submission (recommended)" above.
curl https://modelsok.com/v1/images/edits \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-image-2",
"prompt": "make the mug matte black, keep the composition unchanged",
"image": "https://your-cdn/reference.png",
"size": "3840x2160",
"quality": "medium"
}'gpt-image-2 does not accept the response_format parameter — sending it returns
400 Unknown parameter: 'response_format'. Results are always returned as b64_json (Base64 image data).
Response example:
{
"created": 1780888000,
"data": [{ "b64_json": "iVBORw0KGgoAAAANSUhEUgAA..." }]
}Python example
import base64, requests
resp = requests.post(
"https://modelsok.com/v1/images/edits",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"model": "gpt-image-2",
"prompt": "a red panda eating bamboo, flat vector illustration",
"image": "https://your-cdn/reference.png",
"size": "2048x2048",
"quality": "medium",
},
timeout=300,
)
data = resp.json()["data"][0]["b64_json"]
open("out.png", "wb").write(base64.b64decode(data))A single image-to-image call typically takes 20–110 seconds (longer at higher
quality). For synchronous calls, set your client timeout to at least 300 seconds; asynchronous submission is preferred. If a query times out, keep polling the original task instead of resubmitting.
Gemini models
Option 1: OpenAI-compatible endpoint
Works for gemini-2.5-flash-image-preview.
curl https://modelsok.com/v1/images/generations \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-2.5-flash-image-preview",
"prompt": "a red panda eating bamboo, flat vector illustration",
"n": 1,
"size": "1024x1024",
"response_format": "b64_json"
}'Prefer
response_format: "b64_json". The defaulturlreturns a temporary object-storage link that may expire and occasionally times out on download. Usingb64_jsongives you the image data directly and is more robust to integrate. This parameter is available for Gemini models only —gpt-image-2does not support it.
Option 2: Gemini native endpoint
Works for all Gemini image models. Note the trailing slash in the path.
curl "https://modelsok.com/v1beta/models/gemini-2.5-flash-image:generateContent/" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contents": [{ "parts": [{ "text": "a blue cartoon cat, white background" }] }]
}'Note: the Gemini native endpoint usually returns the image as a Markdown image link embedded in text (under
candidates[0].content.parts[].text, like) rather than asinlineData. Extract the URL from the text and download it. To get standard image data directly, usegemini-2.5-flash-image-previewvia the OpenAI-compatible endpoint instead.
Response example:
{
"candidates": [{
"content": {
"role": "model",
"parts": [{ "text": "" }]
}
}]
}Billing
gpt-image-2 is billed by token usage — the cost depends on the output size (size), the quality tier (quality) and the reference image size. Larger outputs and higher tiers cost more. gpt-image-2-count is billed per call (a fixed amount per generation); other image models are billed either per call or by usage, as shown in the Model Marketplace. When an asynchronous task fails, the charge is refunded automatically.
See the Model Marketplace for exact prices, which vary by account tier.