# Images API — Generate & edit images Source: https://ofox.io/docs/api/openai/images Two endpoints: **Generate** (text → image) and **Edit** (image + text → image). Both responses follow the OpenAI standard shape `data[0].b64_json`. | What you want to do | Endpoint | Supported models | |------|------|------| | Generate an image from text | `POST /v1/images/generations` | GPT Image, Nano Banana (Gemini), Seedream, Qwen-Image and MAI Image families | | Upload an image and edit it with a prompt | `POST /v1/images/edits` | GPT Image, Nano Banana (Gemini), MAI Image | Seedream and Qwen-Image do not support `/v1/images/edits`; for image-to-image they use the `generations` endpoint, but with different reference-image fields: **Seedream** uses `image`, **Qwen** uses `input_images` — don't mix them up. When you need to control Gemini's resolution (512 to 4K), use the [Gemini native protocol](https://ofox.io/docs/api/gemini/generate-content#generate--edit-images). Parameter differences for each family are covered on its family page. ## Quickstart A minimal runnable request: generate one 1024×1024 image and save it as `output.png`. - base_url: `https://api.ofox.io/v1` - Generate endpoint: `https://api.ofox.io/v1/images/generations` - Edit endpoint: `https://api.ofox.io/v1/images/edits` - Model ID: `openai/gpt-image-2.5-flare` **Python** ```python filename="gen.py" import base64 from openai import OpenAI client = OpenAI( api_key="YOUR_OFOX_API_KEY", base_url="https://api.ofox.io/v1", timeout=600, # the images API is synchronous and can be slow ) resp = client.images.generate( model="openai/gpt-image-2.5-flare", prompt="A red apple on a white table", size="1024x1024", quality="low", ) with open("output.png", "wb") as f: f.write(base64.b64decode(resp.data[0].b64_json)) ``` **cURL** ```bash filename="Terminal" curl https://api.ofox.io/v1/images/generations \ --max-time 600 \ -H "Authorization: Bearer $OFOX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "openai/gpt-image-2.5-flare", "prompt": "A red apple on a white table", "size": "1024x1024", "quality": "low" }' | jq -r '.data[0].b64_json' | base64 --decode > output.png ``` **The API is synchronous — set your client timeout to 600 seconds.** High-quality, large-size and editing requests may take several minutes; see [measured latency](https://ofox.io/docs/develop/models/gpt-image#timeouts). All families are called the same way — only `model` changes. Accepted values and family-specific parameters differ; see each parameter below. ## Generate images ```http POST https://api.ofox.io/v1/images/generations ``` ### Request parameters Values and defaults below **apply to the GPT Image family** (`gpt-image-2.5-flare`, `gpt-image-2.5-sunburst`, `gpt-image-2`). Where other families differ, it says so. Measured details: [GPT Image family](https://ofox.io/docs/develop/models/gpt-image). **Required** **model** `string` · required Model ID; **case-sensitive** — a wrong ID returns `404 model_not_found`. Supported models are listed in the table at the top of this page. Use the full ID with its vendor prefix, e.g. `openai/gpt-image-2.5-flare`. **prompt** `string` · required Describes the image to generate. **Common** **size** `string` · default `auto` · values `WIDTHxHEIGHT` / `auto` Output size. Custom sizes must meet four rules, or you get `400 Invalid size`. The four rules: - Width and height both divisible by 16 - Longest edge ≤ 3840 - Aspect ratio between 1:3 and 3:1 - Total pixels ≥ 655,360 (`768x768` is rejected, `1024x768` is fine) Omitted or `auto`, the model decides — we measured 1254×1254. Pass a size explicitly if you need a fixed one. Other families have different size rules; see their family pages. Details: [GPT Image · size](https://ofox.io/docs/develop/models/gpt-image#size). **quality** `string` · values `low` / `medium` / `high` / `xhigh` / `max` / `auto` Quality tier; **it directly drives the price**. `gpt-image-2` has no `xhigh` / `max`. - `gpt-image-2` has no `xhigh` / `max` — they return 400. - The DALL·E-era `standard` / `hd` are not accepted — they return 400. - Omitting it is not the same as `medium`: the model picks a tier (we measured `low`). Set it explicitly for stable quality and cost. Most other families have no tiers; see their family pages. Details: [GPT Image · quality](https://ofox.io/docs/develop/models/gpt-image#quality). **n** `integer` · default `1` · values `1–10` Number of images; each is billed separately. Not supported by Gemini models — do not send it. **output_format** `string` · default `png` · values `png` / `jpeg` / `webp` Output file format. **Advanced** **output_compression** `integer` · values `0–100` Compression level; `jpeg` / `webp` only. **background** `string` · default `opaque` · values `opaque` / `transparent` / `auto` Background. `transparent` needs a format with an alpha channel (`png` / `webp`). **moderation** `string` · values `auto` / `low` Moderation strictness. **stream** `boolean` · values `true` / `false` Stream the response. With `partial_images` you receive intermediate images during generation. **partial_images** `integer` · values `0–3` Number of intermediate images to stream; requires `stream: true`. Per OpenAI's docs, each partial image adds 100 output tokens. **input_images** `string[]` · values `1–3 images` Array of reference images (URL or base64), **Qwen family only**, 1–3 images. Other families accept it without an error but ignore it; Seedream's reference-image field is `image`. **extra_body.provider.type** `string` · values `azure_foundry` / `openai` Selects a provider. **Optional and usually unnecessary** — the gateway routes automatically. Consider it when you have content moderation requirements; once a provider is selected, requests will not fall back to another provider if it is unavailable. The header `X-OfoxAI-Provider-Type` is equivalent. See [Advanced: pinning a provider](#provider). ### Response ```json filename="200 OK (b64_json truncated)" { "created": 1777385517, "data": [ { "b64_json": "iVBORw0KGgoAAAANSUhEUgAA…", "index": 0 } ], "model": "openai/gpt-image-2.5-flare", "size": "1024x1024", "quality": "low", "usage": { "input_tokens": 13, "input_tokens_details": { "text_tokens": 13 }, "output_tokens": 196, "output_tokens_details": { "image_tokens": 196 }, "total_tokens": 209 } } ``` **data[].b64_json** `string` The image as base64, **without** a `data:image/png;base64,` prefix. Decode it and write the file; to display it in a browser, add the prefix yourself. There is no `url` field in the response. **data[].index** `integer` Image index; distinguishes images when `n > 1`. **size** `string` The actual output size. When `size` was omitted or `auto`, read the real size here. **quality** `string` The tier actually used. When `quality` was omitted, this tells you which tier was used. **background · output_format** `string` The background and format actually used. **Not always present** — do not depend on them. **usage** `object` Billing usage. `output_tokens` is the output image tokens; `input_tokens_details.text_tokens` is the prompt tokens. Compute the cost of a call from these numbers. ## Edit images ```http POST https://api.ofox.io/v1/images/edits ``` This endpoint supports GPT Image, Nano Banana (Gemini) and MAI Image. Seedream and Qwen-Image return `400 endpoint_not_supported` here; use the reference-image field on the generations endpoint instead — see [Seedream](https://ofox.io/docs/develop/models/seedream#edit) and [Qwen-Image](https://ofox.io/docs/develop/models/qwen-image#edit). ### Request parameters Sent as `multipart/form-data` form fields, with an uploaded image file. Values and defaults again apply to the GPT Image family. **Required** **model** `string` · form · required Model ID; same rules as [generations](#generations-model). Supported models are listed in the table at the top of this page. **image** `file` · form · required · values `PNG` / `JPEG` Reference image as a **local file** (`@` prefix in cURL), not a URL. For several, repeat the field as `image[]`. ≤ 15 MB each, ≤ 50 MB request body; the gateway sets no count limit, 16 or fewer recommended. **prompt** `string` · form · required The edit instruction. **Common** **mask** `file` · form · values `PNG with alpha` Inpainting mask: a PNG with an alpha channel, the same size as `image`; transparent area = what gets repainted. **Only effective on this endpoint** — silently ignored on generations. How to build one and the measured effect: [GPT Image · mask inpainting](https://ofox.io/docs/develop/models/gpt-image/inpainting#mask). **size** `string` · form · default `auto` · values `WIDTHxHEIGHT` / `auto` Output size; same rules as [generations](#generations-size). Omitted or `auto`, the model decides — **not guaranteed to match the input**. Pass it explicitly to keep the original size. **quality** `string` · form · values `low` / `medium` / `high` / `xhigh` / `max` / `auto` Quality tier; same rules as [generations](#generations-quality) — `gpt-image-2` has no `xhigh` / `max`. **n** `integer` · form · default `1` · values `1–10` Number of images; each is billed separately. **Advanced** **X-OfoxAI-Provider-Type** `string` · header · values `azure_foundry` / `openai` Selects a provider. **Optional and usually unnecessary** — the gateway routes automatically. Consider it when you have content moderation requirements; once a provider is selected, requests will not fall back to another provider if it is unavailable. This endpoint is a multipart upload, so to pin a provider **only the header works** — an `extra_body` form field is silently ignored. See [Advanced: pinning a provider](#provider). ### Response Same shape as generations; `usage` adds input tokens for the reference images: ```json filename="200 OK (b64_json truncated)" { "created": 1777385669, "data": [ { "b64_json": "iVBORw0KGgoAAAANSUhEUgAA…", "index": 0 } ], "model": "openai/gpt-image-2", "size": "auto", "quality": "low", "usage": { "input_tokens": 1041, "input_tokens_details": { "image_tokens": 1024, "text_tokens": 17 }, "num_input_images": 1, "output_tokens": 358, "total_tokens": 1399 } } ``` **usage.input_tokens_details.image_tokens** `integer` Input tokens charged for the reference images. We measured 1,024 for one 1024×1024 reference image. This is the main reason an edit costs more than same-size text to image. **usage.num_input_images** `integer` Number of reference images; use it to confirm the model actually read them. Other fields are the same as the [generations response](#generations-response). See supported models and pricing in the [Model Catalog](https://ofox.io/models). ### Call **Python** ```python filename="edit.py" import base64 from openai import OpenAI client = OpenAI(api_key="YOUR_OFOX_API_KEY", base_url="https://api.ofox.io/v1", timeout=600) with open("apple.png", "rb") as f: resp = client.images.edit( model="openai/gpt-image-2", image=f, prompt="Make the apple green, keep everything else the same", size="auto", quality="low", ) with open("apple_edited.png", "wb") as out: out.write(base64.b64decode(resp.data[0].b64_json)) ``` **TypeScript** ```typescript filename="edit.ts" import OpenAI, { toFile } from 'openai' import fs from 'node:fs' const client = new OpenAI({ apiKey: 'YOUR_OFOX_API_KEY', baseURL: 'https://api.ofox.io/v1', timeout: 600_000, // milliseconds }) const resp = await client.images.edit({ model: 'openai/gpt-image-2', image: await toFile(fs.createReadStream('apple.png'), 'apple.png'), prompt: 'Make the apple green, keep everything else the same', size: 'auto', quality: 'low', }) fs.writeFileSync('apple_edited.png', Buffer.from(resp.data[0].b64_json!, 'base64')) ``` **cURL** ```bash filename="Terminal" curl -X POST 'https://api.ofox.io/v1/images/edits' \ --max-time 600 \ -H 'Authorization: Bearer YOUR_OFOX_API_KEY' \ -F 'model="openai/gpt-image-2"' \ -F 'prompt="Make the apple green, keep everything else the same"' \ -F 'image=@/path/to/apple.png' \ -F 'size="auto"' \ -F 'quality="low"' ``` The `image` field takes a **local file path** (with the `@` prefix in cURL), not a URL. Measured before / after: | Original | Edited | |------|--------| | ![Original red apple](https://ofox.io/screenshots/images-api/example-gpt-image-2-generated.webp) | ![Edited green apple](https://ofox.io/screenshots/images-api/example-gpt-image-2-edited.webp) | ## Errors Errors come back with an HTTP error status and this body (measured): ```json filename="400 Bad Request" { "error": { "code": null, "message": "The model 'gpt-image-2' does not support quality 'xhigh'.", "param": null, "request_id": "fbe4f24c-779d-41a0-8b3a-c62886cdd1ae", "type": "image_generation_user_error" } } ``` `code` can be `null`. Branch on the HTTP status and `message`. | Status | Error | Cause | Fix | |------|------|------|------| | 400 | `Invalid size '…'` | Size breaks one of the four rules | Adjust per [size](#generations-size) | | 400 | `does not support quality '…'` or invalid value | `xhigh` / `max` sent to `gpt-image-2`, or `standard` / `hd` sent | Use a value listed under [quality](#generations-quality) | | 400 | `Unknown parameter: '…'` | Unsupported field, e.g. DALL·E's `style` | Remove the field | | 400 | `Invalid image file or mode for image 1` | Reference image isn't PNG / JPEG, or the mask isn't a PNG with alpha / doesn't match the image size | Check [image](#edits-image) and [mask](#edits-mask) | | 400 | `provider_type_unavailable` | The pinned provider doesn't serve this model | Drop the provider pin and let the gateway route | | 404 | `model_not_found` | Model ID misspelled or wrong case | Copy the full ID from the table on this page | | 429 | `rate_limit_exceeded` | Over 100 RPM per team; more keys don't raise it | Lower concurrency, back off and retry | When contacting support, include the `request_id` and the error details (HTTP status and `error.message`) from the response. We recommend logging them. ## Advanced: pinning a provider **Optional and usually unnecessary.** All three GPT Image models (`gpt-image-2.5-flare`, `gpt-image-2.5-sunburst`, `gpt-image-2`) are served by both `azure_foundry` and `openai`, for generations and edits alike. Without a pin, the gateway routes automatically. Select a provider when you have content moderation requirements. Requests then go only to that provider; if it is unavailable, they **will not fall back** to another provider automatically. Naming a provider that does not serve the model returns `400 provider_type_unavailable`. | Provider | Description | Moderation (`gpt-image-2`) | |------|------|------| | `azure_foundry` | Hosted on Microsoft Azure | Stricter | | `openai` | OpenAI's own API | More permissive | The two providers of `openai/gpt-image-2` do not moderate to the same standard. **If a prompt keeps getting rejected, you can pin `openai` explicitly.** Without a pin, requests are split by weight and may land on `azure_foundry`. ### Text to image `/v1/images/generations` JSON body, so either the body field `extra_body.provider.type` or the header `X-OfoxAI-Provider-Type` works. **Python** ```python filename="gen_pinned.py" resp = client.images.generate( model="openai/gpt-image-2", prompt="A simple red apple on a white table", size="1024x1024", extra_body={"extra_body": {"provider": {"type": "openai"}}}, ) ``` **TypeScript** ```typescript filename="gen_pinned.ts" const resp = await client.images.generate({ model: 'openai/gpt-image-2', prompt: 'A simple red apple on a white table', size: '1024x1024', extra_body: { provider: { type: 'openai' } }, }) ``` **cURL** ```bash filename="Terminal" curl https://api.ofox.io/v1/images/generations \ -H "Authorization: Bearer $OFOX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "openai/gpt-image-2", "prompt": "...", "n": 1, "size": "1024x1024", "extra_body": { "provider": { "type": "openai" } } }' ``` With the official OpenAI SDKs, `extra_body` must be present as a literal key in the request body. The TypeScript SDK sends it as written in the params object. The Python SDK's `extra_body=` argument merges its contents into the top level of the body, so nest it one level deeper, or use the header instead. ### Image to image `/v1/images/edits` A multipart upload has no JSON body to nest `extra_body` in, so **only the header works**. ```bash filename="Terminal" curl https://api.ofox.io/v1/images/edits \ -H "Authorization: Bearer $OFOX_API_KEY" \ -H "X-OfoxAI-Provider-Type: openai" \ -F "model=openai/gpt-image-2" \ -F "prompt=..." \ -F "image=@input.png" ``` With the SDKs: in Python pass `extra_headers={"X-OfoxAI-Provider-Type": "openai"}`; in TypeScript pass `{ headers: { 'X-OfoxAI-Provider-Type': 'openai' } }` as the second argument. On `/v1/images/edits`, `extra_body` sent as a form field is **silently ignored**: the image is generated as usual, but the constraint does not apply. Use `X-OfoxAI-Provider-Type` instead. Full reference: [API · Provider routing](https://ofox.io/docs/api/provider-routing). ## Official docs & further reading The official docs describe calling OpenAI directly. Where they differ from OfoxAI's measured behavior, the measured behavior on this site takes precedence. **OpenAI** - [Image generation guide · Generate images](https://developers.openai.com/api/docs/guides/image-generation#generate-images) - [Image generation guide · Edit images](https://developers.openai.com/api/docs/guides/image-generation#edit-images) - [Image generation guide · Edit an image using a mask](https://developers.openai.com/api/docs/guides/image-generation#edit-an-image-using-a-mask) - [Image generation guide · Size and quality options](https://developers.openai.com/api/docs/guides/image-generation#size-and-quality-options) - [Image generation guide · Calculating costs](https://developers.openai.com/api/docs/guides/image-generation#calculating-costs) - [Images API reference](https://developers.openai.com/api/reference/resources/images) **OfoxAI family pages** - [GPT Image family overview](https://ofox.io/docs/develop/models/gpt-image): choosing a model, size, quality, latency, errors - [Text to image](https://ofox.io/docs/develop/models/gpt-image/text-to-image) - [Image editing & multi-image](https://ofox.io/docs/develop/models/gpt-image/image-edit) - [Inpainting](https://ofox.io/docs/develop/models/gpt-image/inpainting) - [Nano Banana family](https://ofox.io/docs/develop/models/nano-banana) (Gemini Image) and [troubleshooting errors](https://ofox.io/docs/develop/models/nano-banana/errors) - [Seedream family](https://ofox.io/docs/develop/models/seedream) - [Qwen-Image family](https://ofox.io/docs/develop/models/qwen-image) - [MAI Image family](https://ofox.io/docs/develop/models/mai-image)