GPT Image
Generate and edit images with GPT Image: parameters, responses, and caveats.
| Item | Value |
|---|---|
| Base URL | https://api.hop-base.com/v1 |
| Generate images | POST /v1/images/generations |
| Edit images | POST /v1/images/edits |
| Query async tasks | GET /v1/images/tasks?task_id=… |
| Key group | GPT Image (all models) |
Requests are synchronous by default and return Base64 images; add Prefer: respond-async to run an async task. Keys from chat groups get 404 for gpt-image-*.
Available models
| Model | Model ID | quality tiers | Official price |
|---|---|---|---|
| GPT Image 2.5 Flare | gpt-image-2.5-flare | low / medium / high / xhigh / max | $5 / $30/ 1M tokens |
| GPT Image 2.5 Sunburst | gpt-image-2.5-sunburst | low / medium / high / xhigh / max | $5 / $30/ 1M tokens |
| GPT Image 2 | gpt-image-2 | low / medium / high | $5 / $30/ 1M tokens |
Which to pick: Flare is the everyday default; Sunburst has higher fidelity and is slower than Flare at the same settings; gpt-image-2 is general-purpose, and its ID is gpt-image-2, not gpt-image-2.0. Reference timings for one 1024x1024 image: about 14 seconds at low on either 2.5 model; at high, Flare takes about 19 seconds and Sunburst about 37.
Request parameters
Generate
POST /v1/images/generations with a JSON body.
| Parameter | Required | Type and limits | Default | Notes |
|---|---|---|---|---|
model | Required | string | — | One of the three IDs above |
prompt | Required | string, ≤ 32,000 characters | — | Must be non-empty after trimming |
size | Optional | auto or WIDTHxHEIGHT | — | Rules below the table |
quality | Optional | auto / low / medium / high | — | 2.5 adds xhigh / max |
n | Optional | integer, 1–10 | 1 | Some groups accept only 1 |
background | Optional | auto / opaque / transparent | — | Transparent needs png / webp |
output_format | Optional | png / jpeg / webp | png | Format of the decoded image |
output_compression | Optional | integer, 0–100 | 100 | jpeg / webp only |
moderation | Optional | auto / low | — | Does not turn off safety checks |
user | Optional | string | — | End-user identifier |
stream | Optional | boolean | false | true switches to Images SSE |
response_format | Optional | string | — | Omit it |
input_fidelity | Optional | low / high | — | Compatibility field; omit it |
For size, WIDTHxHEIGHT sides must be multiples of 16, each side ≤ 3840, aspect ratio ≤ 3:1, and total pixels 655,360–8,294,400. The 1K / 2K / 4K shorthands are not accepted; an invalid size returns 400 before generation and is not billed.
Higher quality tiers emit more output tokens and cost more. GPT Image always returns b64_json whatever response_format says; user is not a HopBase account ID and does not change who is billed.
Edit
POST /v1/images/edits accepts every parameter above, plus references and a mask. Prefer multipart/form-data for local files; JSON with URL / Data URL references also works.
| Parameter | Required | Type and limits | Default | Notes |
|---|---|---|---|---|
image | Required | 1–16 images | — | Multipart: image or repeated image[] |
mask | Optional | PNG with an alpha channel | — | Transparent pixels mark the edit area |
JSON image can be a URL / Data URL string, a string array, or {"url": …} objects. images is not read, and bare base64 or file_id is not accepted.
A remote URL must be ≤ 25 MiB with an image/* Content-Type, and must not point to an internal address; it may be compressed to 4 MiB before forwarding. The whole request body is capped at 60 MB; larger bodies return 413.
Async
Add the HTTP header Prefer: respond-async to a generation or edit request; the body stays the same. It is a header, not a JSON parameter.
Async tasks keep only model, prompt, n, size, quality, background, output_format, input_fidelity, and edit images / mask; other fields are dropped.
Response
Sync
| Field | Type | Notes |
|---|---|---|
created | integer | Unix seconds |
data[].b64_json | string | Base64 image; decode and save in the output_format format |
usage.input_tokens | integer | Input tokens (may be returned) |
usage.output_tokens | integer | Output tokens (may be returned) |
usage.total_tokens | integer | Total (may be returned) |
error | object | On failure: message, type, code |
{
"created": 1760000000,
"data": [
{
"b64_json": "iVBORw0KGgoAAA..."
}
],
"usage": {
"input_tokens": 42,
"output_tokens": 1760,
"total_tokens": 1802
}
}After about 40 seconds, the server returns HTTP 200 and keeps writing whitespace at the start of the body to hold the connection; the full JSON follows later. From then on the status stays 200 even if generation fails, so check the body for error and set the read timeout to at least 300 seconds.
Streaming (stream: true)
The response switches to Images SSE: while waiting, the stream sends keepalive comment lines starting with a colon, and the last data: event holds the complete Images JSON, followed by [DONE]. These are not OpenAI's native progressive-image events.
: hopbase-keepalive
data: {"created":1760000000,"data":[{"b64_json":"iVBORw0KGgoAAA..."}]}
data: [DONE]Async tasks
Submitting returns 202 Accepted right away; the Location response header points to the same query URL:
{
"object": "image.task",
"task_id": "your-task-id",
"status": "pending",
"status_url": "/v1/images/tasks?task_id=your-task-id"
}Query only with GET /v1/images/tasks?task_id=…; putting the task ID in the path is not supported. pending, processing, and retrying mean in progress; completed and failed are terminal.
| Field | Type | Notes |
|---|---|---|
task_id | string | Task ID |
status | string | Task status |
result_content | string | When completed: Markdown, one line per image |
error | string | Only when failed: English reason, no code |
usage.cost | number | Amount actually deducted for this task |
usage.currency | string | Ledger currency, currently CNY |
usage.cost_cny | number | Amount in CNY, for reconciliation |
usage.cost_usd | number | Amount in USD, for reconciliation |
{
"task_id": "your-task-id",
"status": "completed",
"result_content": "",
"usage": {
"cost": 1.36,
"currency": "CNY",
"cost_cny": 1.36,
"cost_usd": 0.2
}
}result_content holds relative paths; prepend https://api.hop-base.com to download. usage appears once the task is completed or failed, and only the key that created the task sees it.
Examples
Generate
curl https://api.hop-base.com/v1/images/generations \
-H "Authorization: Bearer sk-your-key" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-image-2.5-flare",
"prompt": "A shiba inu under a cherry tree, Japanese watercolor",
"size": "1024x1024",
"quality": "medium",
"output_format": "png"
}' \
| jq -r '.data[0].b64_json' | base64 --decode > result.pngThe curl example needs jq installed.
Edit (multiple references + mask)
curl https://api.hop-base.com/v1/images/edits \
-H "Authorization: Bearer sk-your-key" \
-F "model=gpt-image-2.5-flare" \
-F "prompt=Fill the masked area with a vase styled like the second image" \
-F "image[][email protected]" \
-F "image[][email protected]" \
-F "[email protected]" \
-F "size=1024x1024" \
-F "quality=high" \
-F "output_format=png"For JSON, replace the files with URLs: "image": ["https://example.com/scene.png", "https://example.com/style.png"] and "mask": "https://example.com/mask.png".
Async
# 1. Submit and receive 202 + task_id
curl -i https://api.hop-base.com/v1/images/generations \
-H "Authorization: Bearer sk-your-key" \
-H "Content-Type: application/json" \
-H "Prefer: respond-async" \
-d '{
"model": "gpt-image-2",
"prompt": "A cinematic futuristic city at night",
"size": "2048x2048"
}'
# 2. Poll with the task_id from the first response
curl "https://api.hop-base.com/v1/images/tasks?task_id=your-task-id" \
-H "Authorization: Bearer sk-your-key"Caveats
- For 2K / 4K images, prefer async so long requests are not cut off by CDN timeouts.
- Set the client read timeout for sync requests to at least 300 seconds.
sizeaccepts onlyautoorWIDTHxHEIGHT; tier shorthands return 400.xhigh/maxexist only on the two 2.5 models.background: "transparent"requirespngorwebp; transparency ongpt-image-2is a preview capability.- The official streaming field
partial_images(0–3) is not supported; omit it. - With official SDKs, keep
streamat its defaultfalse. - The gateway resizes the mask to the first reference, and pixel-perfect preservation outside it is not guaranteed.
- The
result_contentURL opens without a key, so do not share it publicly and download it to your own storage soon.
- Standard values follow the OpenAI Images reference and the official edit reference.
output_compression,moderation,user, andresponse_formatpass through only sync JSON generations and sync multipart edits; JSON edits and async tasks drop these fields.- In groups that accept only 1, a larger
nreturns 400;n≤ 0 is treated as 1. input_fidelityis a compatibility field; GPT Image 2 processes references at high fidelity by default.- The reference count is checked officially: the gateway does not count images, and a request over the limit may be refused or have some images ignored.
- The official reference states no aggregate MB limit for all references; one JSON URL / Data URL string can be up to 20,971,520 characters.
- 4 MiB is a compression target before forwarding, not an upload rejection threshold; Base64 adds roughly one third to the data size.
- Data URL example:
data:image/png;base64,iVBORw0KGgo…; send the complete encoding with the correct MIME type. - Submitting an async task only checks that your balance is above 0; no amount is reserved.
Common errors
Invalid parameters return 400 before generation and are not billed. Content-safety rejections also return 400 and are not billed, with error.code set to safety_rejected.
| Error | Fix |
|---|---|
size must be WIDTHxHEIGHT or auto and other size errors | Follow the size rules above |
prompt must not be empty | Send a non-empty prompt |
n must be 1 for this model in the current group | Split into separate requests |
/v1/images/edits requires at least one image | Put references in image, not images |
image download returned HTTP 404 | Use a public URL the server can fetch |
Your request was rejected by the safety system. | Rephrase the prompt or change the reference |
Request body exceeds the size limit (60 MB) (413) | Compress the images, or send URLs |
HTTP 200 with error in the body | Failed after keepalive started; act on error.message |
# size: follow the size rules above
size must be WIDTHxHEIGHT or auto
size side length exceeds 3840px (4096x2048)
size width and height must be multiples of 16 (1000x1000)
size aspect ratio must not exceed 3:1 (3840x1024)
size total pixel count must be at least 655360 (512x512=262144)
size total pixel count must not exceed 8294400 (3840x3840=14745600)
# empty prompt
prompt must not be empty
# edits JSON: put references in "image" as strings or {"url": ...}
# ("images" is not read)
/v1/images/edits requires at least one image
image object is missing the url field
image must be a data URL or an http(s) URL
# remote references: a public URL that returns image/*, at most 25 MiB
image download returned HTTP 404
image is too large
image Content-Type is not image/*: text/html
reference image URL must not point to an internal address
image is too large, please compress it to under 4MB and retry
# content safety (error.code: safety_rejected)
Your request was rejected by the safety system.
# HTTP 413
Request body exceeds the size limit (60 MB)Billing
Billed by token; higher quality emits more output tokens. Measured at 1024x1024: about 200 at low, 1,760 at high, 3,120 at xhigh, and 7,020 at max. For async tasks, the actual charge is usage.cost in the query result (cost_cny / cost_usd use a fixed 1 USD = 6.8 CNY), and a failed task usually shows 0.
Prices are on the model cards above and in the signed-in model catalog.
Next steps
- Other image families and how to choose: Image overview
- Other families: Gemini images, Seedream, Grok Imagine images, Kling images, Midjourney
- Full fields in the API reference: Create image, Edit image, Get image task
- When something fails, check Troubleshooting