GPT Image 2.5 API: Generate and Edit Images in Python
One POST creates the task and one GET reads it back. Here is the request code, the sizes we ran on October 29, 2026, and one edit shown before and after.
On this page
- Which 2.5 model id to use
- The request code
- The sizes we ran, and what came back
- An edit keeps the scene and changes one thing
- Errors and limits
- GPT Image 2.5 or GPT Image 2 for API work?
- FAQ
- Which model id should I use, gpt-image-2.5-flare or gpt-image-2.5-sunburst?
- Can I ask for a landscape size such as 1536x1024?
- Why did my edit come back in a different size from the one I asked for?
- Do I need a User-Agent header?
- How long do the image links last?
Send one POST to https://api.omniakey.com/v1/media/tasks with model set to gpt-image-2.5-flare, poll the task id that comes back, and download assets[0].url once the status reads succeeded. On October 29, 2026 we generated at 1K, 2K and 1536x1024 and edited one café photo, then opened every file and read the real pixel size from it: 1024×1024, 2048×2048, 1536×1024 and 1024×682. The 1K generation took 29.5 s and the 2K one 45.2 s.
Which 2.5 model id to use
Use gpt-image-2.5-flare. That is the id we called in every run below. The GPT-Image 2.5 Flare model page (checked October 29, 2026) names OpenAI as the provider, lists text to image and image edit, and carries the note "Guide updated 2026-10-27". The same page gives a second 2.5 id, gpt-image-2.5-sunburst.
Flare handles both jobs. Generate sends a prompt; edit sends a prompt plus one source image. Quality takes low, medium or high, and the same page says the level does not change the price. Every run here used medium. We did not run sunburst, quality low or high, 4K or 3840x2160, masks, or multiple reference images, so plan around Flare at medium with a single source image until you test the rest yourself.
The request code
The script below is the one we ran, unchanged: two helpers, then one generate call and one edit call. It uses only the Python standard library, reads your key from OMNIAKEY_API_KEY, and writes each PNG into the folder you run it from.
import base64
import json
import os
import time
import urllib.request
import uuid
API = "https://api.omniakey.com/v1/media/tasks"
KEY = os.environ["OMNIAKEY_API_KEY"]
MODEL = "gpt-image-2.5-flare"
def request(method, url, body=None, extra_headers=None):
headers = {"Authorization": f"Bearer {KEY}", "User-Agent": "omniakey-example/1"}
data = None
if body is not None:
headers["Content-Type"] = "application/json"
data = json.dumps(body).encode()
headers.update(extra_headers or {})
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=120) as resp:
return json.load(resp)
def run_task(body, out_path):
task = request("POST", API, body, {"Idempotency-Key": str(uuid.uuid4())})["id"]
for _ in range(150):
time.sleep(4)
state = request("GET", f"{API}/{task}")
if state["status"] not in ("queued", "processing"):
break
if state["status"] != "succeeded":
raise RuntimeError(state.get("error") or state["status"])
asset_url = state["assets"][0]["url"] # signed link: valid for about an hour, so download now
req = urllib.request.Request(asset_url, headers={"User-Agent": "omniakey-example/1"})
with urllib.request.urlopen(req, timeout=120) as resp, open(out_path, "wb") as f:
f.write(resp.read())
return task
def generate_image(prompt, out_path, size="1K", quality="medium"):
body = {
"model": MODEL,
"type": "image",
"operation": "generate",
"input": {"prompt": prompt},
"parameters": {"size": size, "quality": quality},
}
return run_task(body, out_path)
def edit_image(source_path, prompt, out_path, size="1K", quality="medium"):
with open(source_path, "rb") as f:
image = base64.b64encode(f.read()).decode()
body = {
"model": MODEL,
"type": "image",
"operation": "edit",
"input": {"prompt": prompt, "image": {"data": image, "mime_type": "image/jpeg"}},
"parameters": {"size": size, "quality": quality},
}
return run_task(body, out_path)
if __name__ == "__main__":
task = generate_image(
"A small glass greenhouse on a hillside at sunrise, morning fog in the valley, "
"soft watercolor style, a wooden bench outside the door, no people, no text.",
"greenhouse.png",
)
print("generate task", task)
task = edit_image(
"cafe-base.jpg",
"Change the red bicycle to matte black. Keep everything else exactly the same: "
"the table, the cup, the window frame and the daylight. Photorealistic, no text, no logos.",
"cafe-edited.png",
)
print("edit task", task)
Authorization: Bearer <key> goes on every call, and the create call also needs an Idempotency-Key, which the script fills with a fresh UUID. The body sets type to "image", operation to "generate" or "edit", the words under input.prompt, and the size and quality under parameters. The API also accepts n, and only 1 is valid, so the script leaves it out. A create returns HTTP 202 with a task id, the status starts as queued, and the loop waits 4 s between checks until it leaves the two working states.
An edit adds the JPEG under input.image as an inline Base64 object with its mime type. Request bodies, Base64 included, are capped at 1 MiB, so the script sends a 1024-pixel JPEG of about 230 KB.
The sizes we ran, and what came back
Four runs, all at quality medium, on October 29, 2026. Three came back at the size we asked for. The 1K edit did not, in one of its two runs.
- 1K, generate — Pixel size we read from the file: 1024×1024; Date: October 29, 2026
- 2K, generate — Pixel size we read from the file: 2048×2048; Date: October 29, 2026
- 1536x1024, generate — Pixel size we read from the file: 1536×1024; Date: October 29, 2026
- 1K, edit — Pixel size we read from the file: 1024×682; Date: October 29, 2026
The greenhouse below is a 1K generation from the same request body as the code above.

Two things to take from the list. First, read the size off the file you downloaded. The 1K edit asked for a square and came back at 1024×682, the shape of the 1024×683 JPEG we sent, and another run of that same edit at 1K came back at 1024×1024. Second, the size list in the image API docs (checked October 29, 2026) offers auto, 1K, 2K, 4K and 3840x2160 for this model, and says it refuses off-list sizes before any charge; our 1536x1024 call on October 29, 2026 was not refused and returned 1536×1024. Check the real file before you build a layout around one size.
An edit keeps the scene and changes one thing
An edit answers two questions: what changes, and what stays. We asked for one change and named the things that had to survive.
Prompt: "Change the red bicycle to matte black. Keep everything else exactly the same: the table, the cup, the window frame and the daylight. Photorealistic, no text, no logos."

The bicycle came back dark, and the table, the cup, the plant and the window frame were still in the frame. The source was a 1024×683 JPEG, the output measured 1024×682, and the run took 38.0 s. Name what changes and what stays in every prompt. For prompt structure in general, see our GPT Image 2 prompt guide.
Errors and limits
Two headers do the work before any pixels exist: the bearer token and a unique Idempotency-Key. Get them right and the rest of the call is simple to retry.
| What you send | What comes back |
|---|---|
| Create call with no key | 400 missing_idempotency_key |
| Same key, same body, twice | 200 with the existing task, nothing new charged |
| Same key, changed body | 409 idempotency_key_reused |
The key can be up to 255 characters, per the docs checked October 29, 2026. A reuse with the same body is the safe retry: you get the existing task and nothing new is charged. Keep the body under 1 MiB including Base64, so resize or compress the source before you send it.
When a task succeeds, assets[0].url is a signed link that lasts about an hour, per the docs, so download at once or poll the same task again for a fresh link. Task records stay for 24 hours.
One header surprised us on October 29, 2026: a POST sent with Python's default urllib User-Agent returned HTTP 403 with the body "error code: 1010" from Cloudflare. The same POST with our own User-Agent: omniakey-example/1 header reached the API, which answered 400 invalid_request for the empty body we sent, and nothing was charged. The docs name only Authorization and Idempotency-Key, so set a User-Agent in your client.
GPT Image 2.5 or GPT Image 2 for API work?
Flare is the 2.5 id we tested. GPT Image 2, on its GPT Image 2 model page, uses the id gpt-image-2. If a script already produces what you need on that id, there is no reason to change it.
For a photo that must keep its scene while one object changes, Flare at medium is what our October 29, 2026 runs cover: three generated sizes and the 1024×682 edit. The GPT Image 2.5 vs GPT Image 2 comparison has sizes and same-prompt results for both models, so you can pick with numbers in front of you. Current rates are on the pricing page.
FAQ
Which model id should I use, gpt-image-2.5-flare or gpt-image-2.5-sunburst?
We ran Flare in every run above, and its edit kept the scene we asked to keep. The model page lists Sunburst as the second 2.5 id: start with Flare, and put Sunburst through your own hardest edit before you switch.
Can I ask for a landscape size such as 1536x1024?
On October 29, 2026 a 1536x1024 generation came back at 1536×1024. The size list in the image API docs does not include that value for this model, so read the file and check the docs before you rely on it in production.
Why did my edit come back in a different size from the one I asked for?
The same 1K edit came back 1024×682 in one run and 1024×1024 in another, so treat the size you ask for as a request, not a promise. Open the PNG and read its width and height before you place it.
Do I need a User-Agent header?
On October 29, 2026 a request with Python's default User-Agent got HTTP 403 with Cloudflare error 1010. The same request with our own User-Agent header reached the API, so send one.
How long do the image links last?
The docs say the signed link lasts about an hour. Poll the task again for a fresh link, and download the file as soon as the task succeeds.
Models in this article
Related articles
GPT Image 2 Prompt Guide: Structure, Examples and Real Images
Eight prompts, nine real images: what GPT Image 2 got exactly right, what it added and how to fix it.
GPT-Image 2.5 vs GPT-Image 2
GPT-Image 2 costs $0.02 / $0.03 / $0.05 and the 2.5 ids $0.025 / $0.035 / $0.055 per image at 1K / 2K / 4K; quality does not change the price. The 2.5 ids add no working quality tier, and requests need an explicit WIDTHxHEIGHT.
Virtual Try-On API: Swap Outfits With GPT Image 2
One photo and one prompt can change the outfit. Here is the request code, three swaps we ran on October 28, 2026, and where the edit drifts.

