# GPT Image 2.5 API in Python: a working example

By SeedRouter · Published 2026-09-23 · Updated 2026-09-23

To call the GPT Image 2.5 API, send a POST to `https://api.seedrouter.ai/v1/images/generations` with a model ID such as `gpt-image-2.5-flare` and a prompt, keep the task `id` from the response, and poll `GET /v1/tasks/{id}` until the status is `completed`. The finished task holds URLs to your images. The same endpoint handles text-to-image, reference edits and masked edits.

This guide is a complete, runnable path in Python, with the JavaScript equivalent, followed by the errors people hit most often and what each one means.

## What do you need before the first request?

Two things: an API key and a model ID.

Create a key in [API keys](https://seedrouter.ai/apikeys) and keep it in an environment variable on your server, never in browser code:

```bash
export SEEDROUTER_API_KEY="your-key"
```

Then choose one of the four GPT Image 2.5 model IDs. Copy them exactly; there is no bare `gpt-image-2.5` ID.

| Model ID                          | Model    | Billing              |
| --------------------------------- | -------- | -------------------- |
| `gpt-image-2.5-flare`             | Flare    | Flat price per image |
| `gpt-image-2.5-sunburst`          | Sunburst | Flat price per image |
| `gpt-image-2.5-flare-official`    | Flare    | Token usage          |
| `gpt-image-2.5-sunburst-official` | Sunburst | Token usage          |

If you are unsure which model to start with, use Flare; [Flare vs Sunburst](https://seedrouter.ai/blog/gpt-image-2-5-flare-vs-sunburst) explains when Sunburst is worth it.

## How do you generate an image with Python?

Submitting returns immediately. The response is a task reference, not the image.

```python
import os
import requests

response = requests.post(
    "https://api.seedrouter.ai/v1/images/generations",
    headers={"Authorization": f"Bearer {os.environ['SEEDROUTER_API_KEY']}"},
    json={
        "model": "gpt-image-2.5-flare",
        "prompt": "An amber glass bottle on a cream background, studio lighting",
        "size": "1024x1024",
        "quality": "low",
    },
    timeout=60,
)
response.raise_for_status()
task_id = response.json()["id"]
```

Save `task_id` before doing anything else. It is the only handle on the work you just paid for, and it is how you recover if your process restarts while the image renders.

## How do you get the image back?

Poll the task every few seconds until it finishes. This loop waits up to ten minutes; reaching that deadline stops your loop, not the task.

```python
import time

print(f"Task ID: {task_id}")
deadline = time.monotonic() + 600
while time.monotonic() < deadline:
    result = requests.get(
        f"https://api.seedrouter.ai/v1/tasks/{task_id}",
        headers={"Authorization": f"Bearer {os.environ['SEEDROUTER_API_KEY']}"},
        timeout=30,
    )
    result.raise_for_status()
    task = result.json()
    if task["status"] == "completed":
        for image in task["output"]["data"]:
            print(image["url"])
        break
    if task["status"] == "failed":
        raise RuntimeError(task["error"]["message"])
    time.sleep(3)
else:
    raise TimeoutError(f"Still waiting. Resume polling task {task_id}.")
```

Download the URLs you want to keep and store them yourself. Result URLs are a delivery handoff, not long-term storage.

## What does the same call look like in JavaScript?

The request is identical; only the HTTP client changes. Run it on your server so the key never reaches a browser.

```javascript
const response = await fetch('https://api.seedrouter.ai/v1/images/generations', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.SEEDROUTER_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: 'gpt-image-2.5-flare',
    prompt: 'An amber glass bottle on a cream background, studio lighting',
    size: '1024x1024',
    quality: 'low',
  }),
});
if (!response.ok) throw new Error(`Request failed: ${response.status}`);
const { id: taskId } = await response.json();
```

Poll `GET https://api.seedrouter.ai/v1/tasks/${taskId}` with the same header, exactly as in the Python loop.

## How do you edit an existing image?

Add reference images to the same request. There is no separate edit endpoint and no mode field: sending `images` makes it an edit, and adding a `mask` limits the change to one region.

```json
{
  "model": "gpt-image-2.5-sunburst",
  "prompt": "Make the bottle blue. Preserve the composition and lighting.",
  "images": [{"image_url": "https://example.com/reference.png"}],
  "mask": {"image_url": "https://example.com/mask.png"}
}
```

Inputs must be public HTTPS URLs. You can send up to 16 reference images as PNG, JPEG or WebP under 50 MB each. The mask is a PNG under 4 MB, the same size as the first reference image, and its transparent area marks what to change. Base64 strings, `data:` URLs and file uploads are rejected, so upload files to your own storage first and send the URLs.

## Why does the API say the model is not available?

Error code `20002` with HTTP 400 ("The requested model is not available.") means the `model` value is not an ID the API serves. The usual cause is a near miss: `gpt-image-2.5` without a tier, `gpt-image-2-5-flare` with a dash instead of the dot, or a typo in `sunburst`. Copy an ID from the table above.

Parameter errors are reported before the model is checked. If a request also has an invalid field, you get `20001` with a message that names the field, for example `quality`. Fix that first; the model error appears on the next attempt if the ID is still wrong.

| Error code | HTTP | What to do                            |
| ---------- | ---- | ------------------------------------- |
| `20001`    | 400  | Fix the field named in the message    |
| `20002`    | 400  | Use one of the four model IDs exactly |
| `10001`    | 401  | Check the `Authorization` header      |

A task can also fail after it was accepted. That query still returns HTTP 200, with `status: "failed"` and an `error` object such as code `60001` (content policy) or `60002` (generation failed). Failed tasks are not charged. The [error catalog](https://seedrouter.ai/docs/api/errors) lists every code, including balance and rate-limit errors, with the next step for each.

## Frequently asked questions

### Is there an official Python SDK call that returns the image directly?

Not on this API. Delivery is asynchronous: you always submit, keep the task ID, and poll. `stream` and `partial_images` are not supported.

### Can I request several images at once?

Yes. Set `n` from 1 to 10. The finished task lists one URL per delivered image, and you are billed for the images delivered.

### How do I get a transparent PNG?

Set `background` to `transparent` and `output_format` to `png`. JPEG has no alpha channel, so that combination is rejected before it runs.

## Ship the integration around the task ID

Store the task ID the moment you receive it, poll with a deadline, and treat a polling timeout as "still running" rather than "failed." Everything else, including every field and limit, is in the [GPT Image 2.5 API reference](https://seedrouter.ai/docs/gpt-image-2-5), and you can try a request without code in the [Playground](https://seedrouter.ai/models/gpt-image-2-5#playground).
