gpt-image-2 Image Generation and Editing
OpenAI-compatible gpt-image-2 text-to-image + image-edit API: drop in by switching base_url, multi-region endpoints, unified billing with your QCode key
Need an API key? Sign up →Endpoints & base URLs →
On This Page
QCode.cc exposes a gpt-image-2 service whose request and response formats are compatible with the OpenAI Images API (a few parameter behaviors differ — see Differences vs the official OpenAI API), covering two endpoints:
- Text-to-image
POST /v1/images/generations— JSON request, generate images from text alone - Image edit
POST /v1/images/edits— multipart upload of 1–8 source images (optional mask), repaint / modify / composite per the prompt (launched May 2026)
gpt-image-2 is OpenAI's latest image model (released April 2026) and currently has the strongest in-image text rendering of any public model — it can reliably render English and Chinese characters inside generated images.
ℹ️ OpenAI released gpt-image-2.5 on 2026-09-08 (API model names
gpt-image-2.5-flare/gpt-image-2.5-sunburst). This service acceptsgpt-image-2.5,gpt-image-2.5-flareandgpt-image-2.5-sunburstas compatibility model names, so clients already configured for the new names work as-is. All of these names work, with identical billing; thexhigh/maxquality tiers are treated ashigh, and 2.5-only capabilities such as 4K / arbitrary resolutions are not supported yet.
What QCode.cc adds:
- Works with the OpenAI SDK — change
base_urland you are done; request and response shapes match OpenAI (including the multipart edits endpoint); a few parameter behaviors differ, see the differences table at the end - Multi-region access: global smart routing (api) / Asia (Korea / Taiwan / Hong Kong) / North America & Europe (Los Angeles) — pick the entry closest to your network
- Single API key: reuse your existing QCode.cc
cr_key — same quota account as Claude Code and Codex - Unified usage view: image calls and chat calls are merged in your dashboard, plus a dedicated self-service usage page
- Shared quota across endpoints:
generationsandeditsshare the same per-key 100 images/day cap and 2-concurrency limit
Common use cases: poster generation, illustrations, product imagery, UI mockups, social-media assets, image editing (local inpainting / multi-image composition / background swap / high-fidelity restoration).
Quick start¶
Text-to-image (generations)¶
Three lines of Python for your first image:
from openai import OpenAI
import base64
client = OpenAI(
base_url="https://api.qcode.cc/qcode-img/v1",
api_key="cr_YOUR_QCODE_API_KEY",
timeout=180.0, # typically 20-80s per image, occasionally over 100s; set at least 180s
)
result = client.images.generate(
model="gpt-image-2",
prompt="A cyberpunk Tokyo street at night, neon reflecting in rain puddles",
size="1024x1024",
quality="low",
n=1,
)
with open("output.png", "wb") as f:
f.write(base64.b64decode(result.data[0].b64_json))
Image edit (edits)¶
Upload an existing image + prompt, optionally with a mask for local inpainting:
from openai import OpenAI
import base64
client = OpenAI(
base_url="https://api.qcode.cc/qcode-img/v1",
api_key="cr_YOUR_QCODE_API_KEY",
timeout=180.0,
)
result = client.images.edit(
model="gpt-image-2",
image=open("cat.png", "rb"),
mask=open("mask.png", "rb"), # optional: PNG alpha; transparent area = repaint region
prompt="put a tiny crown on the cat",
size="1024x1024",
quality="high",
)
with open("edited.png", "wb") as f:
f.write(base64.b64decode(result.data[0].b64_json))
⚠️ Always set timeout ≥ 180 s explicitly: the OpenAI SDK default is too short.
gpt-image-2inference is significantly slower than legacy text-to-image (see Latency & timeout below).
Endpoints¶
The gpt-image-2 service (both generations and edits) is available on every QCode.cc entry. Just append /qcode-img/v1 to the base:
| Your location | Recommended base_url | Protocol | Notes |
|---|---|---|---|
| China | https://api.qcode.cc/qcode-img/v1 |
HTTPS | Recommended by default |
| HK / SEA | https://asia.qcode.cc/qcode-img/v1 |
HTTPS | Asia node (Korea / Taiwan / Hong Kong, nearest) |
| North America / Europe | https://us.qcode.cc/qcode-img/v1 |
HTTPS | Los Angeles node |
All entries route to the same service and billing system — usage and quota are unified. No entry caps a single request at 100 seconds; the server waits up to 180 seconds per request.
qcode-imgis the path prefix dedicated to image generation and editing, parallel to/api(Anthropic) and/openai/v1(OpenAI chat).
API reference¶
Common request headers¶
| Header | Required | Value |
|---|---|---|
Authorization |
✓ | Bearer cr_xxxxxxxxxxxxxxxx (your QCode.cc API key) |
Content-Type |
✓ | application/json (generations)multipart/form-data (edits) |
Generations endpoint /v1/images/generations¶
Endpoint: POST {base_url}/images/generations
Content-Type: application/json
Request body:
{
"model": "gpt-image-2",
"prompt": "A small ceramic vase with sunflower, photorealistic",
"size": "1024x1024",
"quality": "low",
"n": 1
}
Fields:
| Field | Type | Required | Default | Values |
|---|---|---|---|---|
model |
string | ✗ | gpt-image-2 |
gpt-image-2 (also accepts gpt-image-2-low / -medium / -high, equivalent to setting quality; and the compatibility names gpt-image-2.5 / gpt-image-2.5-flare / gpt-image-2.5-sunburst, all usable with identical billing); any other model name returns 400 unsupported_model |
prompt |
string | ✓ | — | Image description, multilingual (incl. English & Chinese) |
size |
string | ✗ | 1024x1024 |
auto / 1024x1024 / 1024x1536 / 1536x1024 / 2048x2048; invalid values return 422. The output size usually matches the requested size; in some cases the returned size differs (e.g. 1254×1254) — go by the actual size of the returned image |
quality |
string | ✗ | medium |
low / medium / high (xhigh / max are treated as high); it does not affect the price (see Billing) |
n |
integer | ✗ | 1 | Images per call (1 – 4); > 4 returns 400 (no silent truncation) |
background |
string | ✗ | auto |
auto / opaque; transparent returns 400 (the model cannot produce an alpha channel) |
response_format |
string | ✗ | b64_json |
b64_json only; passing url returns 400 |
output_compression |
integer | ✗ | — | 0 – 100, applies to JPEG / WebP; out-of-range values return 400 |
stream / partial_images |
— | ✗ | — | Not supported yet; passing them returns 400 |
Edits endpoint /v1/images/edits¶
Endpoint: POST {base_url}/images/edits
Content-Type: multipart/form-data (file uploads require form-data, not JSON)
Fields:
| Field | Type | Required | Default | Values / notes |
|---|---|---|---|---|
model |
string | ✗ | gpt-image-2 |
May be omitted; the edits endpoint always uses gpt-image-2 |
image |
file (× N) | ✓ | — | Source image (PNG / JPEG / WebP); 1 – 8 images supported for multi-image composition (repeat the same field name: -F image=@a.png -F image=@b.png) |
mask |
file | ✗ | — | PNG alpha mask; transparent area = region to repaint; mask resolution must match image (scaled automatically); applies to single-image edits |
prompt |
string | ✓ | — | Edit instruction (multilingual) |
size |
string | ✗ | auto |
auto / 1024x1024 / 1024x1536 / 1536x1024 / 2048x2048. The output size usually matches the requested size; in some cases the returned size differs (e.g. 1254×1254) — go by the actual size of the returned image. Invalid values return 422 |
quality |
string | ✗ | auto |
auto / low / medium / high (xhigh / max are treated as high); does not affect the price |
n |
integer | ✗ | 1 | Images per call (1 – 4); > 4 returns 400 (no silent truncation) |
background |
string | ✗ | auto |
auto / opaque. transparent is not supported by gpt-image-2 (the model cannot produce an alpha channel; passing it returns 400) |
output_format |
string | ✗ | png |
png / jpeg / webp |
output_compression |
integer | ✗ | — | 0 – 100, applies to JPEG / WebP (ignored for PNG); out-of-range values return 400 |
input_fidelity |
string | ✗ | — | No need to pass it: the model always processes input images at high fidelity; passing low / high is ignored, any other value returns 400 |
response_format |
string | ✗ | b64_json |
Only b64_json is accepted (passing url returns 400) |
user |
string | ✗ | — | End-user identifier passed through (for abuse detection) |
Upload size limits: single file ≤ 25 MB; total across files ≤ 50 MB (over the limit returns 413 image_too_large).
Response (same for both endpoints)¶
{
"created": 1777135432,
"data": [
{
"b64_json": "iVBORw0KGgo...(base64 PNG/JPEG/WebP, 1-3 MB)",
"revised_prompt": "A small ceramic vase with sunflower..."
}
]
}
b64_json: base64-encoded image data; render directly with<img src="data:image/<format>;base64,...">revised_prompt: the model's improved version of your original prompt (optional to display)
Error responses¶
Errors follow the standard OpenAI schema:
{
"error": {
"type": "rate_limit_error",
"code": "image_daily_limit",
"message": "Daily image generation count limit reached..."
}
}
| HTTP | code | Meaning | Billed |
|---|---|---|---|
| 400 | invalid_json / missing_prompt |
Request body is not valid JSON / prompt is missing |
No |
| 400 | unsupported_model |
Model name not supported (gpt-image-2 and its compatibility names gpt-image-2.5 / -flare / -sunburst are supported; gpt-image-1, dall-e-3 etc. return this error) |
No |
| 400 | stream_not_supported |
stream=true or partial_images was passed |
No |
| 400 | unsupported_background / unsupported_response_format / unsupported_n / unsupported_output_compression |
Parameter value not supported (see each endpoint's parameter table) | No |
| 400 | invalid_multipart / invalid_content_type |
Malformed form on the edits endpoint (must be multipart/form-data) |
No |
| 400 | content_policy_violation and similar |
Content moderation rejected the request; change the prompt or the image | No |
| 401 | invalid_api_key / key_disabled / key_expired |
API key invalid / disabled / expired | No |
| 413 | image_too_large |
Single file > 25 MB, or total across files > 50 MB | No |
| 422 | unsupported_size |
size value not supported (valid values are in each endpoint's parameter table) |
No |
| 429 | crs_daily_exhausted / crs_total_exhausted |
Your account's daily / lifetime budget is used up | No |
| 429 | image_daily_limit |
Per-key daily 100-image cap reached (shared between generations and edits; raise on request) |
No |
| 429 | concurrency_exhausted |
Per-key concurrency cap of 2 reached (raise on request) | No |
| 501 | endpoint_not_implemented |
The edits endpoint is temporarily closed for maintenance | No |
| 503 | image_provider_unavailable |
The image service is temporarily unavailable; the response carries Retry-After — handle it per Timeout and retry |
No |
| 503 | service_overloaded / billing_backend_unavailable |
Service busy / billing backend temporarily unavailable, retry later | No |
| 504 | upstream_timeout |
An edit request did not complete within 180 seconds | No |
Code examples¶
Python (OpenAI SDK, recommended)¶
Text-to-image:
from openai import OpenAI
import base64
client = OpenAI(
base_url="https://api.qcode.cc/qcode-img/v1",
api_key="cr_YOUR_QCODE_API_KEY",
timeout=180.0,
)
result = client.images.generate(
model="gpt-image-2",
prompt="A cyberpunk Tokyo street at night, neon reflecting in rain puddles",
size="1024x1024",
quality="low",
n=1,
)
img_bytes = base64.b64decode(result.data[0].b64_json)
with open("output.png", "wb") as f:
f.write(img_bytes)
print("Saved output.png")
Image edit (single image + mask inpainting):
from openai import OpenAI
import base64
client = OpenAI(
base_url="https://api.qcode.cc/qcode-img/v1",
api_key="cr_YOUR_QCODE_API_KEY",
timeout=180.0,
)
result = client.images.edit(
model="gpt-image-2",
image=open("cat.png", "rb"),
mask=open("mask.png", "rb"),
prompt="put a tiny crown on the cat",
size="1024x1024",
quality="high",
extra_body={"output_format": "png"},
)
img = base64.b64decode(result.data[0].b64_json)
with open("edited.png", "wb") as f:
f.write(img)
Image edit (multi-image composition, 2 – 8 images):
result = client.images.edit(
model="gpt-image-2",
image=[
open("scene.png", "rb"), # 1st image: scene background
open("product.png", "rb"), # 2nd image: product to place
],
prompt="Place the product naturally into the scene, match the lighting and shadows.",
size="1536x1024",
quality="high",
)
curl¶
Text-to-image:
curl https://api.qcode.cc/qcode-img/v1/images/generations \
-H "Authorization: Bearer cr_YOUR_QCODE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-image-2",
"prompt": "A cyberpunk Tokyo street at night",
"size": "1024x1024",
"quality": "low",
"n": 1
}' \
| jq -r ".data[0].b64_json" | base64 -d > output.png
Image edit:
curl https://api.qcode.cc/qcode-img/v1/images/edits \
-H "Authorization: Bearer cr_YOUR_QCODE_API_KEY" \
-F "model=gpt-image-2" \
-F "image=@cat.png" \
-F "mask=@mask.png" \
-F "prompt=put a tiny crown on the cat" \
-F "size=1024x1024" \
-F "quality=high" \
-F "output_format=png" \
| jq -r ".data[0].b64_json" | base64 -d > edited.png
Multi-image composition (just repeat -F image=@):
curl https://api.qcode.cc/qcode-img/v1/images/edits \
-H "Authorization: Bearer cr_YOUR_QCODE_API_KEY" \
-F "model=gpt-image-2" \
-F "image=@scene.png" \
-F "image=@product.png" \
-F "prompt=Place the product naturally into the scene" \
-F "size=1536x1024" \
-F "quality=high"
JavaScript / Node.js / browser¶
Text-to-image:
const r = await fetch("https://api.qcode.cc/qcode-img/v1/images/generations", {
method: "POST",
headers: {
"Authorization": "Bearer cr_YOUR_QCODE_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "gpt-image-2",
prompt: "A cyberpunk Tokyo street at night",
size: "1024x1024",
quality: "low",
n: 1,
}),
});
const json = await r.json();
const dataUrl = "data:image/png;base64," + json.data[0].b64_json;
document.querySelector("img").src = dataUrl;
Image edit (browser FormData):
const fd = new FormData();
fd.append("model", "gpt-image-2");
fd.append("image", imageFile); // File object from <input type="file">
fd.append("mask", maskFile); // optional
fd.append("prompt", "put a tiny crown on the cat");
fd.append("size", "1024x1024");
fd.append("quality", "high");
fd.append("output_format", "png");
const r = await fetch("https://api.qcode.cc/qcode-img/v1/images/edits", {
method: "POST",
headers: { "Authorization": "Bearer cr_YOUR_QCODE_API_KEY" },
// Don't set Content-Type manually — FormData sets multipart/form-data with the boundary
body: fd,
});
const json = await r.json();
const dataUrl = "data:image/png;base64," + json.data[0].b64_json;
document.querySelector("img").src = dataUrl;
Quotas¶
Defaults¶
| Dimension | Default | Notes |
|---|---|---|
| Daily image count | 100 / day / key | Resets at 00:00 Beijing time; shared between generations and edits |
| Concurrency | 2 in flight | Returns 429 concurrency_exhausted; shared across both endpoints |
| Upload size (edits only) | 25 MB single file / 50 MB total | Returns 413 image_too_large |
| Account budget | Shared with your QCode.cc account dailyCostLimit / totalCostLimit |
Returns 429 crs_daily_exhausted |
Defaults cover the vast majority of users. Contact support to raise.
Usage lookup¶
- Dashboard — shows chat + image calls together (last activity, today's cost, lifetime cost, model distribution)
- Self-service page: https://api.qcode.cc/qcode-img/usage — enter your API key to see the last 30 days of usage, detailed call list, ECharts trend (key is kept locally in your browser, never uploaded)
Billing¶
How billing works¶
generations and edits are billed the same way, per output image:
| Billable item | Unit price |
|---|---|
| Per output image | $0.30 |
- $0.30 per output image: generating
nimages costsn × $0.30 size/quality/ the number of input images /mask/ prompt length do not affect the pricegpt-image-2,gpt-image-2-low/-medium/-high, and the compatibility namesgpt-image-2.5/-flare/-sunburstall share the same price- As of 2026-10-07, the price is $0.30 per image (it was $0.15 per image from 2026-10-06; before that, billing was per-token)
Failures don't charge¶
Any failed request is never billed: 4xx (including content-moderation rejections) / 5xx, timeouts and client disconnects (closed connections) are not billed.
Currency¶
Costs are denominated in USD and settled per your QCode.cc account's currency policy (CNY / USD).
Latency & timeout settings¶
gpt-image-2 is an inference-based model and takes markedly longer than legacy text-to-image models. A single image usually takes 20–80 s, occasionally over 100 s, and in rare cases close to 180 s.
Practical advice:
- The OpenAI Python SDK's default timeout is too short — always set
timeout=180.0or higher explicitly - Browser
fetchhas no default timeout; if you useAbortController, give it at least 180 s - If the request goes through your own reverse proxy / gateway, raise its read timeout to ≥ 180 s
Timeout and retry¶
- 503
image_provider_unavailable: the image service is temporarily unavailable, and nothing is billed. Wait as instructed by theRetry-Afterresponse header (seconds), then retry with exponential backoff (30 s → 60 s → 120 s) for at most 3 attempts; if it keeps failing, contact support - 504
upstream_timeout/ client-side timeout: a single generation exceeded 180 seconds. Retrying directly is fine; if it keeps timing out, simplify the prompt or reduce the number of input images - 429
crs_daily_exhausted/crs_total_exhausted: the account quota is used up, handled the same way as on the chat endpoints: a personal plan resets at 00:00 Beijing time (or buy a booster pack), a Pay-as-you-go Pack is renewed; see the error reference - 429
image_daily_limit/concurrency_exhausted: wait per theRetry-Afterheader; if it keeps happening, contact support - Failed requests are not billed, so retrying never double-charges
Prompting tips¶
Text-to-image (generations)¶
- Multilingual: Chinese, English, or mixed prompts all work
- More detail is better: environment, composition, lighting, style, lens distance / focal length / angle
- Avoid brand names / well-known people: the model may refuse or return blurry results (OpenAI content policy)
- In-image text:
gpt-image-2is exceptional at rendering English / Chinese text inside images (poster titles, slogans, signage) — just put the literal text in the prompt, no special syntax - Content moderation: a prompt that violates the content policy returns 400 (e.g.
content_policy_violation) and is not billed
Example:
A vintage poster in Bauhaus style, bold black text "MORNING COFFEE" centered,
warm orange and cream color palette, geometric shapes, slightly textured paper background
Image edit (edits)¶
- Mask says "where", not "what": transparent area = region to repaint, opaque area is preserved. The prompt describes what to paint. Mask resolution must match
image(system rescales). - For multi-image inputs, name each image's role in the prompt — e.g. "First image is the scene, second image is the product to place into it" —
gpt-image-2reads inputs in order. - No need to pass
input_fidelity: gpt-image-2 always processes input images at high fidelity, automatically preserving faces / logos / text / product details (passinglow/highis ignored, any other value returns 400). - Without a mask, the whole image is repainted per the prompt (the semantic structure is preserved, but every region may change).
Example:
# Local inpaint: add a crown, keep the rest unchanged
prompt = "Put a tiny golden crown on the cat's head. Keep everything else unchanged."
mask = (alpha PNG with only the head area transparent)
Differences vs the official OpenAI API¶
| Dimension | OpenAI official | QCode.cc |
|---|---|---|
| SDK compatibility | — | ✅ Request / response formats are compatible, changing base_url is enough; the rows below list the behavioral differences |
| Billing | Per token | $0.30 per output image, n images = n × $0.30 |
/v1/images/generations (text-to-image) |
✅ | ✅ |
/v1/images/edits (image edit) |
✅ | ✅ Supports 1 – 8 image composition and mask inpainting |
size |
— | Output size usually matches size; sometimes it differs — go by the returned image |
quality |
— | Does not affect the price |
background=transparent |
— | ❌ Returns 400 |
response_format=url |
— | ❌ b64_json only |
stream + partial_images (incremental return) |
✅ | ⏳ Not supported yet, passing them returns 400 |
/v1/images/variations (pure variants) |
✅ (DALL·E endpoint) | ⏳ Not supported yet (approximate it with edits plus a descriptive prompt) |
gpt-image-2.5 (flare / sunburst) |
✅ (released 2026-09-08) | ✅ Accepts the compatibility model names, all usable with identical billing; xhigh / max are treated as high, 2.5-only capabilities (4K etc.) are not supported yet |
Online Playground¶
https://api.qcode.cc/qcode-img/ — try in your browser:
- Both Text-to-image and Image-edit modes are wired up visually (upload, draw mask, pick background / output_format, etc.)
- Enter your API key + prompt to generate immediately
- English / Chinese UI toggle
- Defaults to
lowquality - Includes full API reference inline (curl / Python / JavaScript tabs + parameter table + error codes)
- One-click download as PNG / JPEG / WebP
FAQ¶
Q: What do I do about 503 image_provider_unavailable?
A: The image service is temporarily unavailable and failed requests are not billed. Wait as instructed by the Retry-After header and retry (exponential backoff starting at 30 seconds, at most 3 attempts). If it keeps failing, contact support.
Q: Do you support gpt-image-2.5 (flare / sunburst)?
A: You can use the model names gpt-image-2.5, gpt-image-2.5-flare and gpt-image-2.5-sunburst directly — all of these names work, with identical billing; the xhigh / max quality tiers are treated as high, and 2.5-only capabilities such as 4K are not supported yet.
Q: Why is the output size different from the size I sent?
A: The output size usually matches the requested size; in some cases the returned size differs (e.g. 1254×1254), so go by the actual size of the returned image. If you need an exact size, crop or scale the image locally.
Q: Does quality affect the price?
A: quality does not affect the price — whether low or high, it is $0.30 per output image.
Q: Are failed requests charged? A: No. 4xx / 5xx, timeouts, content-moderation rejections and client disconnects are not billed.
Related docs¶
- Endpoints & API paths — full list of QCode.cc entries, protocol paths, curl smoke-test
- Billing — plans, quotas, cost rules
Get started with QCode
One API key for Claude Code, Codex and more — billed per token, per-model prices on qcode.cc/models