Image Generation API
Asynchronous image generation, editing (image-to-image & mask), and variations - create, poll, retrieve. Persistent r2_url plus one-time b64_json within a 30-minute task TTL.
Overview
Every image operation is an asynchronous task: a POST returns a task ID immediately, then you poll GET /images/generations/{id} until status becomes completed. Three task families share that one polling endpoint:
- Text-to-image —
POST /images/generations/async(prompt only) - Image editing (image-to-image & mask) —
POST /images/edits(source image + prompt, optional mask) - Image variation —
POST /images/variations(source image only)
Editing and variation take an input image. The recommended path uploads that image to R2 first via POST /uploads/presign and submits the returned upload_id as JSON, so large image bytes never traverse the gateway. A legacy multipart/form-data path is kept for compatibility.
Call GET /images/models for the live model list. Full schema and constraints are available in the interactive API explorer.
| Endpoint | Purpose |
|---|---|
GET https://api.alltoken.ai/v1/images/models | List available image models |
POST https://api.alltoken.ai/v1/uploads/presign | Presign an R2 upload for an input image / mask |
POST https://api.alltoken.ai/v1/images/generations/async | Create a text-to-image task |
POST https://api.alltoken.ai/v1/images/edits | Create an image edit task (i_edit / mask_edit) |
POST https://api.alltoken.ai/v1/images/variations | Create an image variation task |
GET https://api.alltoken.ai/v1/images/generations/{id} | Poll task status / retrieve result |
Prefer r2_url over b64_json: a completed response returns both, but b64_json is deprecated and delivered only on the first read (the temporary file is deleted right after). r2_url is a public URL that stays valid for at least 30 days and survives re-polls. Tasks themselves expire after 30 minutes.
List Image Models
$GET https://api.alltoken.ai/v1/images/modelsReturns all available image generation models in OpenAI-compatible format.
| 1 | { |
| 2 | "object": "list", |
| 3 | "data": [ |
| 4 | { "id": "gpt-image-2", "object": "model", "owned_by": "openai" } |
| 5 | ] |
| 6 | } |
Create an Image Generation Task
$POST https://api.alltoken.ai/v1/images/generations/asyncHeaders:
Authorization: Bearer <API_KEY>- requiredIdempotency-Key: <uuid>- optional but recommended. Server dedupes within a short window; retries with the same key return the original task.
| 1 | { |
| 2 | "model": "gpt-image-2", |
| 3 | "prompt": "A clean product photo of a glass teapot on a walnut table, soft natural light", |
| 4 | "size": "1024x1024", |
| 5 | "quality": "high", |
| 6 | "output_format": "png", |
| 7 | "background": "auto", |
| 8 | "moderation": "auto" |
| 9 | } |
Response (task created, status queued):
| 1 | { |
| 2 | "id": "igen_a7b68c38c4b7832ee386a13e", |
| 3 | "status": "queued", |
| 4 | "model": "gpt-image-2", |
| 5 | "created_at": "2026-05-12T08:00:00Z" |
| 6 | } |
Request Parameters
These apply to the POST /images/generations/async body. Image editing (/images/edits) accepts the same n / size / quality / output_format / output_compression / background / moderation fields.
model(required) - Image model ID. Text-to-image acceptsgpt-image-2,wan2.7-image, orwan2.7-image-pro; callGET /images/modelsfor the live listprompt(required) - Text description of the desired image (max 32000 characters)n- Number of images to generate, 1-10 (default 1)size-autoorWIDTHxHEIGHT. Fromgpt-image-2on, arbitrary resolutions are supported (each side a multiple of 16, max 3840x2160, aspect ratio between 1:3 and 3:1), e.g.1024x1024/1536x1024/1024x1536quality-low/medium/high/auto; controls inference budget and per-image costoutput_format-png/jpeg/webpoutput_compression-0-100, applies tojpeg/webponly (upstream defaults to 100)background-auto/opaque/transparentmoderation-auto(default; recommended) /low(less restrictive; user accepts full content compliance responsibility)
Uploading Input Images
Image editing and variation need a source image (and optionally a mask). The recommended flow uploads each file straight to R2 with a presigned URL, then submits its upload_id — keeping large bytes off the gateway and the Cloudflare edge.
Step 1 — request a presigned URL:
$POST https://api.alltoken.ai/v1/uploads/presign| 1 | { |
| 2 | "purpose": "image_edit_source", |
| 3 | "content_type": "image/png", |
| 4 | "content_length": 524288, |
| 5 | "content_md5": "1B2M2Y8AsgTpgAmY7PhCfg==", |
| 6 | "checksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" |
| 7 | } |
purpose(required) -image_edit_source,image_edit_mask, orimage_variation_source. The purpose is re-validated when you submit the task; uploads cannot be reused across purposescontent_type(required) -image/png/image/jpeg/image/webp(must match the purpose allowlist; the server re-checks the real type on bind)content_length(required) - Byte length; each purpose has its own cap (echoed back asmax_content_length)content_md5(required) - Base64 MD5; you must send the same value asContent-MD5on the R2 PUTchecksum_sha256(required) - Lowercase hex SHA-256, used for the server-side consistency check on bind
| 1 | { |
| 2 | "upload_url": "https://r2.alltoken.ai/inputs/upl_2f8bb7d7f4a24c3a8f4f8a7e?signature=...", |
| 3 | "method": "PUT", |
| 4 | "required_headers": { |
| 5 | "Content-Type": "image/png", |
| 6 | "Content-MD5": "1B2M2Y8AsgTpgAmY7PhCfg==" |
| 7 | }, |
| 8 | "upload_id": "upl_2f8bb7d7f4a24c3a8f4f8a7e", |
| 9 | "expires_in": 300, |
| 10 | "claim_expires_at": "2026-05-15T05:00:00Z", |
| 11 | "max_content_length": 26214400 |
| 12 | } |
Step 2 — PUT the file to R2 at upload_url with the exact required_headers. The presigned URL is valid for expires_in seconds (currently 300):
$curl -X PUT "$UPLOAD_URL" \
-H "Content-Type: image/png" \
-H "Content-MD5: 1B2M2Y8AsgTpgAmY7PhCfg==" \
--data-binary @teapot.pngStep 3 — submit the upload_id to /images/edits or /images/variations (below). An unclaimed upload is deleted ~2 hours after presign; a bound input is deleted as soon as the task succeeds, fails, or is cancelled. Submit only upload_id — the debug-only public_url is not accepted as task input.
Edit an Image (image-to-image & mask)
Create an image-to-image task from a required source image plus a prompt. Omit mask for a whole-image edit (i_edit); include a mask to edit only the masked region (mask_edit) — the mask's transparent pixels mark the editable area.
$POST https://api.alltoken.ai/v1/images/editsRecommended — JSON with uploaded inputs (presign the source/mask first, see above):
| 1 | { |
| 2 | "model": "gpt-image-2", |
| 3 | "prompt": "Replace the sky with a dramatic sunset", |
| 4 | "image_upload_id": "upl_2f8bb7d7f4a24c3a8f4f8a7e", |
| 5 | "mask_upload_id": "upl_9c1aa0e2b7d3490f8e2c1a55", |
| 6 | "size": "1024x1024", |
| 7 | "quality": "high", |
| 8 | "output_format": "png" |
| 9 | } |
model(required) - e.g.gpt-image-2prompt(required) - Edit instruction (max 32000 characters)image_upload_id(required) -upload_idfrom apurpose=image_edit_sourcepresignmask_upload_id- Optional;upload_idfrom apurpose=image_edit_maskpresign. Its presence switches the task to mask_editn- Number of images, 1-10 (default 1)size,quality,output_format,output_compression,background,moderation- Same as text-to-image (see Request Parameters)
Legacy — multipart/form-data (kept for compatibility; large bodies may hit 413 / edge challenges). Send model, prompt, a binary image field, and an optional binary mask field. The source image and mask are each ≤ 25MB, MIME image/png / image/jpeg / image/webp.
Response (202, task created) - the task ID is prefixed igen_iedit_ (i_edit) or igen_mask_ (mask_edit); poll it on GET /images/generations/{id}:
| 1 | { |
| 2 | "id": "igen_iedit_a7b68c38c4b7832ee386a13e", |
| 3 | "status": "queued", |
| 4 | "model": "gpt-image-2", |
| 5 | "created_at": "2026-05-15T03:00:00Z" |
| 6 | } |
Image Variations
Create variations of a required source image. No prompt or mask is accepted. Variations only work on models that support them (e.g. dall-e-2); unsupported models return an upstream 4xx passed through the gateway.
$POST https://api.alltoken.ai/v1/images/variations| 1 | { |
| 2 | "model": "dall-e-2", |
| 3 | "image_upload_id": "upl_2f8bb7d7f4a24c3a8f4f8a7e", |
| 4 | "size": "1024x1024" |
| 5 | } |
model(required) - A variation-capable model, e.g.dall-e-2image_upload_id(required) -upload_idfrom apurpose=image_variation_sourcepresignsize-auto/1024x1024/1536x1024/1024x1536
The legacy multipart/form-data path sends a binary image field (≤ 25MB) instead of image_upload_id. The returned task ID is prefixed igen_ivar_; poll it on GET /images/generations/{id}.
Get Task Status
$GET https://api.alltoken.ai/v1/images/generations/{id}Returns task details. This single endpoint serves every task family (text-to-image, edits, variations). When status is completed, the data array carries each generated image as a persistent r2_url plus a one-time b64_json (base64-encoded PNG/JPEG/WebP per the requested output_format).
| 1 | { |
| 2 | "id": "igen_a7b68c38c4b7832ee386a13e", |
| 3 | "status": "completed", |
| 4 | "model": "gpt-image-2", |
| 5 | "created": 1778572818, |
| 6 | "completed_at": "2026-05-12T08:00:18Z", |
| 7 | "expires_at": "2026-05-12T08:30:18Z", |
| 8 | "size": "1024x1024", |
| 9 | "quality": "high", |
| 10 | "output_format": "png", |
| 11 | "data": [ |
| 12 | { |
| 13 | "r2_url": "https://r2.alltoken.ai/images/igen_a7b68c38.png", |
| 14 | "r2_url_expires_at": "2026-06-11T08:00:18Z", |
| 15 | "mime_type": "image/png", |
| 16 | "b64_json": "iVBORw0KGgo...", |
| 17 | "revised_prompt": "A clean studio product photo of a clear borosilicate glass teapot..." |
| 18 | } |
| 19 | ], |
| 20 | "usage": { |
| 21 | "input_tokens": 24, |
| 22 | "output_tokens": 1290, |
| 23 | "total_tokens": 1314 |
| 24 | } |
| 25 | } |
Prefer r2_url; b64_json is one-time: the first completed read returns both, but b64_json is deprecated and the temporary file is deleted right after. r2_url is a public URL valid for at least 30 days (until r2_url_expires_at) and is returned on every subsequent read. If R2 upload failed there is no r2_url — keep the first b64_json; re-reading with no R2 fallback returns 410 image_already_retrieved, and an expired r2_url returns 410 image_expired.
Task Status Values
queued- Enqueued, waiting for GPUprocessing- Model is generatingcompleted- Done;data[].b64_jsonavailable for one readfailed- Generation failed; details inerrorcancelled- Cancelled before completion
Recommended polling interval: 1-3 seconds. Most tasks complete in 8-25 seconds; high-quality 1536x1024 jobs may take 30-60 seconds. Expired or already-retrieved results are returned as 410 errors.
Error Responses
400- Bad request, such as an empty prompt or invalid size401- Invalid or missing API Key402- Insufficient API usage quota404- Task ID not found409- DuplicateIdempotency-Keyfor an already-active task, or another request is currently claiming the completed result410- Task expired (30-minute TTL elapsed) or result was already retrieved413- Uploaded file too large (source image or mask > 25MB on the multipart path)429- Rate limit exceeded; respect theRetry-Afterheader503- Gateway R2 storage is not configured, or upstream storage is unavailable