Image generation with GPT Image 2.6 Sunburst, the style branch of OpenAI's November 2026 image model with heavier grain and brushwork, for pictures that need texture and physical presence. Same capabilities as Flare, as async jobs; 1K / 2K / 4K tiers and 7 aspect ratios, the original image as generated, billed per image, nothing charged on failure.
Try in Playground →Quick start
Three steps: submit a job, poll it, take the image link. Replace YOUR_API_KEY with your key and the commands below run as they are.
Step 1 · Submit (returns a job id at once, HTTP 202)
curl https://kovapi.com/v1/images/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "gpt-image-2.6-sunburst", "prompt": "An oil painting of a harbour at dusk, visible brushstrokes, warm palette", "size": "16:9", "resolution": "2K"}'Response (HTTP 202)
{
"id": "img_7f3c9a1e2b4d6f8a0c1e3b5d",
"object": "image.generation.job",
"status": "queued",
"created": 1791281400,
"model": "gpt-image-2.6-sunburst"
}Step 2 · Poll every 2–3 seconds until status is succeeded or failed
curl https://kovapi.com/v1/images/jobs/img_7f3c9a1e2b4d6f8a0c1e3b5d \ -H "Authorization: Bearer YOUR_API_KEY"
Response when it succeeds
{
"id": "img_7f3c9a1e2b4d6f8a0c1e3b5d",
"object": "image.generation.job",
"status": "succeeded",
"created": 1791281400,
"model": "gpt-image-2.6-sunburst",
"data": [
{"url": "https://img.kovapi.com/i/2be8cd16d37f3a4b9c0e1f2a3b4c5d6e.png"}
],
"usage": {"images": 1, "resolution": "2K", "model": "gpt-image-2.6-sunburst"}
}Step 3 · data[0].url is the image (PNG). It is kept for 15 days; download it to your own storage to keep it longer.
Complete example (Python: submit, wait, save to disk)
import time
import requests
BASE = "https://kovapi.com/v1"
HEADERS = {"Authorization": "Bearer YOUR_API_KEY"}
# 1. submit
job = requests.post(f"{BASE}/images/generations", headers=HEADERS, json={
"model": "gpt-image-2.6-sunburst",
"prompt": "An oil painting of a harbour at dusk, visible brushstrokes, warm palette",
"size": "16:9",
"resolution": "2K",
}).json()
if "error" in job:
raise SystemExit(job["error"]["message"])
# 2. poll every 3 seconds until it finishes
while job["status"] not in ("succeeded", "failed"):
time.sleep(3)
job = requests.get(f"{BASE}/images/jobs/{job['id']}", headers=HEADERS).json()
# 3. download the image
if job["status"] == "succeeded":
url = job["data"][0]["url"]
with open("gpt-image-2.6-sunburst.png", "wb") as f:
f.write(requests.get(url).content)
print("saved", url)
else:
print("failed:", job["error"]["code"], job["error"]["message"])Authentication
Authorization: Bearer YOUR_API_KEY Content-Type: application/json
Create an API Key in the console to start.
Request parameters
The JSON body of POST /v1/images/generations:
| Parameter | Type | Required | Description |
|---|---|---|---|
| model | string | Yes | The model id: gpt-image-2.6-sunburst. |
| prompt | string | Yes | What to draw. For image-to-image, say what to change. |
| size | string | No | Aspect ratio such as 16:9 (recommended), or a pixel size such as 1536x1024. Without it you get a 1:1 square. |
| resolution | string | No | Tier: 1K (default), 2K or 4K. |
| quality | string | No | Detail level: low, medium (default) or high, at 1K, 2K and 4K alike, without changing the price; auto counts as medium. xhigh and max are not available yet: they are refused at submit with a 400 and nothing is charged. |
| image / images | string / array | No | Reference images; sending any makes it image-to-image: one in image, several in images (up to 8). Each can be a public image URL, a data: URI or base64. |
| n | integer | No | Number of images. Best left out: for several images submit several jobs at once. With n, n images are held up front and the images actually delivered are billed. |
| callback_url | string | No | Where to push the result when the job ends; see Webhooks below. |
background can be transparent (pair it with PNG or WebP output); output_format can be png (default), jpeg or webp, and the delivered file is in that format. n must be 1: for several images submit several jobs at once. Other parameters (response_format, style and the like) do not change the price and are not guaranteed to take effect; results come back as a url.
Price
Billed per image by tier only: aspect ratio, text-to-image or image-to-image make no difference. The tier price is held when you submit and refunded in full if the job fails. 1 credit = $0.005.
| Tier | Credits per image | USD per image |
|---|---|---|
| 1K | 3 | $0.015 |
| 2K | 6 | $0.03 |
| 4K | 10 | $0.05 |
Aspect ratio, resolution and output size
Put the ratio in size and the tier in resolution. You get the original image as generated, neither cropped nor resized, at the sizes below: both sides are multiples of 16, and 4K stays within 3840 px a side and about 8.3 megapixels.
| Aspect ratio | 1K | 2K | 4K |
|---|---|---|---|
| 1:1 | 1024x1024 | 2048x2048 | 2880x2880 |
| 3:4 | 768x1024 | 1536x2048 | 2480x3312 |
| 2:3 | 672x1024 | 1360x2048 | 2336x3520 |
| 9:16 | 608x1088 | 1152x2048 | 2160x3840 |
| 4:3 | 1024x768 | 2048x1536 | 3312x2480 |
| 3:2 | 1024x672 | 2048x1360 | 3520x2336 |
| 16:9 | 1088x608 | 2048x1152 | 3840x2160 |
- Only the 7 ratios in the table are available; any other ratio is refused at submit with a 400 and nothing is charged. At 1K, 16:9 and 9:16 would fall below the minimum pixel count of the model, so they come out at 1088×608 and 608×1088.
sizecan also be a pixel size (e.g.1536x1024) in one of the 7 ratios: both sides are rounded down to multiples of 16, scaled down when over the tier limit, and scaled up to the minimum of the model (about 655,000 pixels) when too small. The tier comes fromresolution; without it the longest side decides: up to 1536 is 1K, up to 2048 is 2K, larger is 4K. Longest side allowed: 512–1536 at 1K, 2048–2304 at 2K, 2049–4096 at 4K; anything else is refused at submit with a 400 and nothing is charged.
Quality and native resolution
- Without
qualitythe image is drawn at medium.low,mediumandhighwork at 1K, 2K and 4K, for text-to-image and image-to-image alike, and the price depends only on the resolution tier;autocounts as medium.xhighandmaxare not available yet: they are refused at submit with a 400 and nothing is charged. - If
qualityholds1K,2Kor4Kandresolutionis not given, it is read as the resolution tier (size and price follow that tier). - Tiers that are drawn natively are always drawn natively: when capacity is busy the job waits in the queue, and if it still cannot be drawn it fails with a full refund — it is never replaced by an enlarged 1K image.
Image-to-image and reference images
- Send
image(one picture) orimages(several, up to 5) and the job runs image-to-image; say in the prompt what to change. More than 5 is refused at submit with a 400 and nothing is charged.POST /v1/images/editstakes the same parameters. - Both endpoints take a JSON body only, not a multipart file upload, so the OpenAI SDK's
images.edit()cannot be used as is: pass links or base64 as in the example below. - Each reference can be a public image URL (http or https), a data: URI or plain base64, up to 16MB each, in PNG or JPEG; a reference in any other format makes the job fail with a full refund.
- A URL has to download the image itself: not a web page, and not behind a login or hotlink protection. A reference we cannot read is refused at submit with a 400 that says which one and why; no job is created and nothing is charged.
- The output ratio comes from
size, not from the reference. To keep the original ratio, setsizeto it. - We host the references for you and keep them for 7 days.
Image-to-image with two references
curl https://kovapi.com/v1/images/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "gpt-image-2.6-sunburst", "prompt": "Keep the people, change the background to a beach at night with warm string lights", "images": ["https://example.com/people.jpg", "https://example.com/beach.jpg"], "size": "3:4", "resolution": "2K"}'Job status and fields
GET /v1/images/jobs/{id} reports these statuses; every field is described in Async jobs. Poll every 2–3 seconds, or use a webhook.
| Status | Meaning |
|---|---|
| queued | Waiting in line. Carries queue_position (jobs ahead of yours) and eta_seconds (expected wait). |
| processing | Rendering. |
| succeeded | Done. The result is in data[0].url, the billing detail in usage. |
| failed | Failed. The reason is in error; the credits held for the job are refunded in full. |
One account runs up to 50 jobs at a time; further jobs wait in submission order, and a job that has not started after 15 minutes fails and is refunded in full. Job records are kept for 3 days and image links for 15 days.
How long it takes
Usually 20–60 seconds from submit to result; 4K, edits with several references and busy periods take longer, and an occasional job takes a minute or two.
Errors and refunds
An error at submit comes back at once as HTTP 4xx / 5xx: no job is created and nothing is charged. A failure after the job was accepted is reported by the job endpoint, and the hold is refunded in full. Every code is listed under Error codes.
Errors at submit
| HTTP | code | Cause | What to do |
|---|---|---|---|
| 400 | missing_prompt | /v1/images/generations has no prompt. | Add a prompt. |
| 400 | invalid_request | A reference image cannot be read (dead link, not an image, over 16MB...; message says which one and why), /v1/images/edits has no reference image, the body is not a JSON object, or n is not an integer. | Fix what message says and submit again. |
| 400 | model_not_found | The model id is misspelled, model is missing, or the model cannot be used on this endpoint (a chat model on the image endpoint, for example). | Use a model id from GET /v1/models or the API reference. |
| 401 | invalid_api_key | The key does not exist, was deleted or is disabled, or the account is disabled. | Check the key on the API Keys page of the console. |
| 402 | insufficient_quota | Not enough balance to hold this request. The gateway holds the highest possible cost of the request first, so a burst of concurrent calls can trigger it too. | After a burst, wait a few seconds and retry; otherwise top up. |
| 429 | rate_limit_exceeded | Over the requests-per-minute limit set on this key (counted over the last 60 seconds). No Retry-After header. | Wait a few seconds and retry, or raise the key's limit in the console. |
| 503 | model_maintenance | The model is under maintenance and takes no requests; no other model answers in its place meanwhile. | Retry when maintenance ends; see Status. |
| 503 | service_unavailable | A temporary failure on our side, such as storing a reference file; /v1/responses also answers 503, with Retry-After, when it cannot take requests for a moment. | Retry shortly. |
Failed jobs (status failed)
| code | When you see it | What to do |
|---|---|---|
| moderation_blocked | Image: the model provider's safety system refused the prompt or a reference image (the code may also be content_policy_violation). | Rewrite the prompt or replace the reference; resubmitting it unchanged is usually refused again. |
| upstream_error | Any other failure: the automatic retries did not help, the job waited 15 minutes without starting, the upstream refused without a reason, and so on; message says which. | Submitting again usually works. |
Webhooks instead of polling
Add callback_url when you submit and the moment the job ends, success or failure, the gateway pushes exactly what the poll endpoint returns to that URL, signed and retried on failure. You can also set an account-wide address under Settings → Webhook in the console. Webhook docs →
curl https://kovapi.com/v1/images/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "gpt-image-2.6-sunburst", "prompt": "An oil painting of a harbour at dusk, visible brushstrokes, warm palette", "size": "1:1", "callback_url": "https://your-domain.com/webhook"}'