OpenAI shipped ChatGPT Images 2.5 on September 8, 2026, with two new API models: gpt-image-2.5-flare and gpt-image-2.5-sunburst. Both sit behind the same endpoints as gpt-image-2, so if you followed our gpt-image-2 API guide, most of your code survives a model ID swap. What changed is the quality ladder and how the Responses API lets you pick a model per tool call.
This guide covers the developer path only: generations, multipart edits with a reference image and mask, the Responses API tool, streaming, and reading usage for real cost. For what the release means for ChatGPT users, read our ChatGPT Images 2.5 overview; the OpenAI launch post has the product framing. Every number below comes from OpenAI’s docs, pricing page, or calculator as read on September 9, 2026.
gpt-image-2.5 API at a glance
| Item | Value (OpenAI docs) |
|---|---|
| Model IDs | gpt-image-2.5-flare, gpt-image-2.5-sunburst (snapshots -2026-09-08) |
| Endpoints | POST /v1/images/generations, POST /v1/images/edits, Responses API image_generation tool |
| Input / output | Text and image in, image only out |
| Quality | low, medium, high, xhigh, max, auto (default). xhigh and max are new |
| Sizes | 1024x1024, 1536x1024, 1024x1536 recommended; custom sizes in multiples of 16, aspect 1:3 to 3:1, up to 4K total pixels |
| Output | data[].b64_json; output_format png, jpeg, webp; background: "transparent" needs png or webp |
| Streaming | partial_images 0-3, each partial costs 100 extra output tokens |
| Price (both models) | $30 per 1M image output tokens, $8 per 1M image input tokens, $5 per 1M text input tokens |
Per-token rates match gpt-image-2; per-image cost still moves because token counts per quality level changed.
Prerequisites
- An OpenAI developer account on a paid usage tier. Image endpoints need Tier 1 or above, which means adding a payment method; a ChatGPT subscription doesn’t count. Our OpenAI API key walkthrough covers project-scoped keys.
- The official
openaiSDK for Python or Node. - A way to preview image responses. curl prints base64, painful for iteration; Apidog renders the decoded image inline, and the last section moves the workflow there.
Export the key once:
export OPENAI_API_KEY="sk-proj-..."
Generate an image with curl
Use Flare first; OpenAI’s model page calls it “the default choice for most applications”.
curl https://api.openai.com/v1/images/generations \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-image-2.5-flare",
"prompt": "Product photo of a matte black mechanical keyboard, studio lighting, no text",
"size": "1536x1024",
"quality": "medium",
"output_format": "webp",
"background": "transparent"
}'
The response holds a data array with one b64_json per image, plus a usage object with input_tokens and output_tokens. Keep usage; it’s the only accurate cost signal you get. Parameter notes from the image generation guide: output_format defaults to png and OpenAI says “Using jpeg is faster than png”; output_compression (0-100) applies to jpeg and webp only; background: "transparent" fails on jpeg.
Python: generate, then edit with a reference image
The SDK call mirrors the curl body. Decode b64_json and write the bytes.
import base64
from openai import OpenAI
client = OpenAI()
gen = client.images.generate(
model="gpt-image-2.5-flare",
prompt="Clean API analytics dashboard mockup, dark theme, latency chart top right",
size="1536x1024",
quality="high",
output_format="png",
)
open("dashboard.png", "wb").write(base64.b64decode(gen.data[0].b64_json))
print(gen.usage.output_tokens, "output tokens")
Edits are where the 2.5 models earn their keep; the launch post says they are “better at editing only what you’ve asked for, while keeping the rest of the details the same”, and OpenAI positions Sunburst for “tighter control across edits”. The edits endpoint is multipart: a reference image, an optional mask, and a prompt. Where the mask is transparent, the model repaints; everywhere else it keeps the original.
edit = client.images.edit(
model="gpt-image-2.5-sunburst",
image=open("dashboard.png", "rb"),
mask=open("chart-area-mask.png", "rb"),
prompt="Replace the latency chart with a bar chart of error rates per endpoint; keep everything else",
size="1536x1024",
quality="high",
)
open("dashboard-v2.png", "wb").write(base64.b64decode(edit.data[0].b64_json))
print(edit.usage.input_tokens, "input tokens (includes the reference image)")
Drop mask and the model decides what to change from the prompt alone. The reference image bills as image input tokens at $8 per 1M; OpenAI doesn’t publish a per-image input token count, so read usage.input_tokens.
Node and TypeScript: write b64_json to disk
import fs from "node:fs/promises";
import OpenAI from "openai";
const client = new OpenAI();
const res = await client.images.generate({
model: "gpt-image-2.5-flare",
prompt: "Hero image for API docs: floating JSON cards over a teal gradient, no text",
size: "1536x1024",
quality: "medium",
output_format: "jpeg",
output_compression: 80,
});
const b64 = res.data?.[0]?.b64_json;
if (!b64) throw new Error("no image returned");
await fs.writeFile("hero.jpg", Buffer.from(b64, "base64"));
Pin gpt-image-2.5-flare-2026-09-08 in production to keep output stable while the alias moves.
Responses API: image generation as a tool
Here a mainline model reads your prompt, revises it, and calls the image_generation tool. You pick the image model by setting model inside the tool definition; the top-level model must be a mainline model, and OpenAI’s tool docs use gpt-6-astra. Our Responses API guide covers the request shape. The action field takes auto (default), generate, or edit; set edit when you pass a reference image and want it modified, not reinterpreted.
import base64
with open("product.png", "rb") as f:
ref = base64.b64encode(f.read()).decode()
first = client.responses.create(
model="gpt-6-astra",
input=[{"role": "user", "content": [
{"type": "input_text", "text": "Put this bottle on a white marble surface with soft daylight"},
{"type": "input_image", "image_url": f"data:image/png;base64,{ref}"},
]}],
tools=[{"type": "image_generation", "model": "gpt-image-2.5-sunburst", "action": "edit"}],
)
calls = [o for o in first.output if o.type == "image_generation_call"]
open("bottle-marble.png", "wb").write(base64.b64decode(calls[0].result))
second = client.responses.create(
model="gpt-6-astra",
previous_response_id=first.id,
input="Same scene, but add a second bottle behind it, slightly out of focus",
tools=[{"type": "image_generation", "model": "gpt-image-2.5-sunburst", "action": "edit"}],
)
The previous_response_id follow-up keeps the first image in context, so “same scene” resolves without re-uploading the file. Mainline model tokens are billed on top of image tokens, and the prompt rewrite means you can’t reproduce output from prompt text alone.
Streaming partial images
Both APIs accept partial_images (0 to 3). Each partial costs 100 extra output tokens, so three add 300 tokens, or $0.009 per image. Worth it for a UI that shows progress; wasted in a batch job.
stream = client.images.generate(
model="gpt-image-2.5-flare",
prompt="Isometric illustration of an API gateway routing requests to three services",
size="1024x1024",
quality="medium",
stream=True,
partial_images=2,
)
for event in stream:
if event.type.endswith("partial_image"):
open(f"gateway-partial-{event.partial_image_index}.png", "wb").write(
base64.b64decode(event.b64_json))
elif event.type.endswith("completed"):
open("gateway.png", "wb").write(base64.b64decode(event.b64_json))
The exact event type strings are in the image generation guide; the suffix check keeps the loop working across both API variants. To inspect streamed events outside code, see our guide to testing SSE responses from AI APIs.
Read usage and turn tokens into dollars
OpenAI’s own caveat: “Equal token rates don’t mean equal cost per image: token consumption can differ by model and quality setting.” The calculator in the image generation guide gives these estimates for image output tokens alone, at the $30 per 1M rate on the pricing page:
| Quality | 1024x1024 | 1536x1024 |
|---|---|---|
low |
196 tokens, $0.0059 | 158 tokens, $0.0047 |
medium |
439 tokens, $0.0132 | 343 tokens, $0.0103 |
high |
1,756 tokens, $0.0527 | 1,372 tokens, $0.0412 |
xhigh |
3,122 tokens, $0.0937 | 2,459 tokens, $0.0738 |
max |
7,024 tokens, $0.2107 | 5,488 tokens, $0.1646 |
Notice the relabel. high on 2.5 uses 1,756 tokens, the old medium budget on gpt-image-2; max uses 7,024 tokens, the old high budget. Keep quality: "high" through a migration and each image gets about 4x cheaper at the old medium budget; for the old high budget, move to max. Our Flare vs Sunburst vs gpt-image-2 comparison runs the full monthly math.
Calculator numbers are estimates. Real cost comes from the response:
OUTPUT_RATE = 30 / 1_000_000 # dollars per image output token
usd = gen.usage.output_tokens * OUTPUT_RATE
print(f"{gen.usage.output_tokens} tokens = ${usd:.4f}")
Log it per request; per OpenAI, a larger non-square size can produce fewer tokens than a smaller square one. One open question: the pricing page’s Batch tab lists gpt-image-2 only, so treat Batch API support for 2.5 as unconfirmed.
Errors, rate limits, and timeouts
- 429 rate limit. Back off with jitter and honor
Retry-After. The 2.5 model pages don’t publish per-tier limits. For reference,gpt-image-2runs Tier 1 at 5 images per minute and 100k TPM, scaling to Tier 5 at 250 IPM and 8M TPM. insufficient_quota. No credits or still on the free tier. Add billing; don’t retry.- Moderation refusals. The prompt or reference image tripped the filter. Reword instead of retrying;
moderation: "low"relaxes the threshold. - Timeouts. OpenAI documents that “Complex prompts may take up to 2 minutes to process”. Set client timeouts above that; Sunburst runs longer than Flare by design.
Test Flare and Sunburst side by side in Apidog
Terminal iteration on image prompts is slow because you can’t see the output, and a wrong quality value costs real money on every send. Apidog is an API client and testing platform: it sends the calls and checks the responses; OpenAI’s servers do the rendering.
- Store the key once. Add
OPENAI_API_KEYas an environment variable and reference it asBearer {{OPENAI_API_KEY}}in the Authorization header; the key never lands in a saved request. - Two environments, one request. Create environments named
flareandsunburst, each with aMODELvariable, and set"model": "{{MODEL}}"in the body. Switch, resend, and compare images andusageside by side. For edits, use a form-data body withimageandmaskas file fields. - Decode
b64_jsonin a post-processor. A short script pullsdata[0].b64_json, decodes it, and saves the file, so every send yields a viewable image next to the raw JSON. - Assert on cost, then schedule it. Assert that
usage.output_tokensstays under a budget, say 2,000 for ahigh1536x1024 render, and run the request as a timed regression test. If someone bumps quality tomaxor a snapshot moves token counts, the test fails before the invoice does.
Download Apidog, point it at your OpenAI key, and you have a shared prompt library with cost guardrails.
FAQ
Do I need to change my gpt-image-2 code to use 2.5? Swap the model ID and re-check quality. Endpoints, auth, and response shape are unchanged, but high now maps to a smaller token budget. The gpt-image-2 API guide still covers the older model.
Flare or Sunburst for the API? Start with Flare. OpenAI positions it as the default with “50% lower latency” than gpt-image-2 at the same per-token price. Move to Sunburst when edit precision matters more than speed, such as product imagery built from reference photos. Both share the same calculator token counts, so the trade is time, not dollars.
Can I use these models in Chat Completions? No. Image generation lives on the Image API and the Responses API image_generation tool. Chat Completions doesn’t expose it.
Is there a free way to try 2.5 through the API? There’s no perpetual free API tier, and image endpoints need Tier 1. The cheapest real path is quality: "low" at 196 tokens, about $0.006 per 1024x1024 image. The consumer app is another matter; see how to use ChatGPT Images 2.5 for free.
Where to go next
Start with the curl call, confirm usage.output_tokens against the calculator table, then move the request into a client where you can see the image. Simon Willison’s write-up shows Sunburst keeping a chart intact while adding a subject; test that edit behavior on your own reference images before you commit.



