Asynchronous image tasks
Submit an image task, poll its status, and download the temporary result.
APIPool runs supported image-model requests as asynchronous tasks. The submission request returns a task ID immediately; your application then polls that task until it finishes.
Image models and protocols
All image families below use asynchronous tasks. Your key's group determines availability and pricing; do not send a group in the request body.
| Family and public model IDs | Groups | Generation / editing | Examples |
|---|---|---|---|
gpt-image-2 | Official, Discount | JSON generation; file-upload edits | Official, Discount |
gpt-image-2.5-flare, gpt-image-2.5-sunburst | Official | JSON generation; URL references or file-upload edits | GPT Image 2.5 |
gemini-2.5-flash-image-preview, gemini-3.1-flash-image-preview, gemini-3-pro-image-preview | Official, Discount | JSON generation; URL references or file-upload edits | Gemini images |
seedream-4-0, seedream-4-5, seedream-5-0-lite, seedream-5-0-pro | Official | JSON generation; URL references or file-upload edits | Seedream |
qwen-image-2.0, qwen-image-2.0-pro, qwen-image-3.0, qwen-image-3.0-pro | Official | JSON generation; URL references or file-upload edits | Qwen Image |
z-image-turbo | Official | JSON text-to-image only | Alibaba images |
wan2.7-image, wan2.7-image-pro | Official | JSON generation or image_urls references; no multipart edits | Alibaba images |
Use the Images request format + APIPool asynchronous tasks, not the synchronous OpenAI Images response format. Do not substitute these standalone image IDs into text requests. Chat Completions / Responses image-generation compatibility and native Gemini endpoints are not enabled for these listings. Image-generation tools called by GPT text models through Responses are a separate integration and are outside this guide.
Submit a task
Both image endpoints use the same task flow:
| Operation | Request |
|---|---|
| Generate an image | POST /v1/images/generations with JSON |
| Edit an uploaded image | POST /v1/images/edits with multipart/form-data |
A successful submission returns 202 Accepted. Save the id from the body, or
the task URL from the Location header.
HTTP/1.1 202 Accepted
Location: /v1/tasks/imgtask_01...
X-APIPool-Task-Id: imgtask_01...
Content-Type: application/json{
"id": "imgtask_01...",
"object": "image_generation.task",
"status": "submitted",
"model": "gpt-image-2",
"created_at": 1787068800
}submission_unknown means the submission has not yet been confirmed. Keep the
task ID and continue polling; do not resubmit the same request.
Seedream parameters
See Seedream image API for generation and editing examples.
Pass Seedream reference images through image_urls on the generation endpoint.
For example, a multi-reference Seedream 5.0 Pro request is:
{
"model": "seedream-5-0-pro",
"prompt": "Place the person from image 1 into the scene from image 2 and unify the dusk lighting",
"image_urls": [
"https://example.com/person.jpg",
"https://example.com/scene.jpg"
],
"resolution": "1.5K",
"size": "16:9"
}| Model | Outputs and references | Resolution |
|---|---|---|
seedream-4-0 / seedream-4-5 | n=1..15; references plus n must not exceed 15; without references, n=1 | Use the model's supported range |
seedream-5-0-lite | Native n=1..15; references plus n must not exceed 15 | Use the model's supported range |
seedream-5-0-pro | Fixed n=1; up to 10 references | 1K, 1.5K, 2K, or valid exact WIDTHxHEIGHT pixels; defaults to 1K |
The first Seedream 5.0 Pro reference image has no surcharge; each reference from
the second onward adds a charge shown on Models & pricing. Seedream
does not accept masks, stream: true, tools, or explicit sequential-generation
parameters. layer_decomposition: true is not supported.
Query a task
Send GET to the returned Location on the same base URL:
APIPOOL_ENDPOINT="https://app.apipool.dev"
TASK_ID="imgtask_01..."
curl "$APIPOOL_ENDPOINT/v1/tasks/$TASK_ID" \
-H "Authorization: Bearer $APIPOOL_API_KEY"Use the same API key that submitted the task. A different key cannot read
the task, even when both keys belong to the same account, and receives 404.
Status values
| Status | Meaning | Client action |
|---|---|---|
submission_unknown | Submission is still being confirmed | Keep polling |
submitted | Task was accepted | Keep polling |
processing | The image is being generated or edited | Keep polling |
meter_pending | Output exists; billing data is still being confirmed | Keep polling |
completed | Results are ready | Read and download data |
failed | Task failed | Read error; this task will not produce results |
Poll unfinished tasks with a bounded delay, for example every 2 seconds, and
increase the delay gradually for long-running tasks. The image endpoints do not
stream task progress, so stream: true is not supported here.
Python: submit and poll
This standalone example uses the Python 3 standard library, without an SDK.
Set APIPOOL_API_KEY before running; it submits a real generation request.
If you already have a task, set APIPOOL_TASK_ID to its saved ID to query only.
After submitting with curl, do not run this example in its default submission
mode to retrieve that result: doing so would create a second task.
import json
import os
import time
from urllib.parse import quote
from urllib.request import Request, urlopen
base = "https://app.apipool.dev"
key = os.environ["APIPOOL_API_KEY"]
def request(path, payload=None):
headers = {
"Authorization": f"Bearer {key}",
"User-Agent": "APIPool-Image-Example/1.0",
}
data = None
if payload is not None:
headers["Content-Type"] = "application/json"
data = json.dumps(payload).encode()
req = Request(base + path, headers=headers, data=data)
with urlopen(req, timeout=60) as response:
return response.status, json.load(response)
task_id = os.environ.get("APIPOOL_TASK_ID")
if not task_id:
status, task = request("/v1/images/generations", {
"model": "gemini-3.1-flash-image-preview",
"prompt": "A blue ceramic cup on a white table",
"size": "1:1",
"resolution": "1K",
"n": 1,
})
if status != 202 or not task.get("id"):
raise SystemExit(f"Unexpected submission response: {status} {task}")
task_id = task["id"]
print(f"APIPOOL_TASK_ID={task_id}", flush=True)
pending = {"submission_unknown", "submitted", "processing", "meter_pending"}
for _ in range(120):
_, task = request("/v1/tasks/" + quote(task_id, safe=""))
state = task.get("status")
if state == "completed":
for item in task["data"]:
print(item["url"])
break
if state == "failed":
raise SystemExit(json.dumps(task.get("error"), ensure_ascii=False))
if state not in pending:
raise SystemExit(f"Unknown task state: {state}; keep {task_id}")
time.sleep(2)
else:
raise SystemExit(f"Still pending. Resume with APIPOOL_TASK_ID={task_id}")This example polls at most 120 times. Exiting does not cancel the task. Keep the printed ID and resume with the same key later. After a query network error or retryable HTTP response, resume querying that task rather than generating again. If submission times out before returning an ID, do not automatically replay it; check your task records or contact support. When changing models, also adapt the parameters to that model's guide.
Completed result
{
"id": "imgtask_01...",
"object": "image_generation.task",
"status": "completed",
"model": "gpt-image-2",
"created_at": 1787068800,
"completed_at": 1787068842,
"data": [
{
"url": "https://objects.example/results/0.png?...",
"expires_at": 1787072442
}
],
"result_expires_at": 1787155242
}Each data[].url is a temporary signed download URL, and expires_at is its
Unix expiration time — download the image before then. The result itself is
retained until result_expires_at; when a download URL is near expiry, query the
completed task again to get a refreshed one.
Token-priced image models include standardized usage in the completed body.
Per-image models settle from the number of images successfully delivered and the
specifications selected at submission, without depending on token usage.
Failed result
{
"id": "imgtask_01...",
"object": "image_generation.task",
"status": "failed",
"model": "gpt-image-2",
"created_at": 1787068800,
"completed_at": 1787068842,
"error": {
"code": "task_failed",
"message": "Image generation task failed."
}
}Retryable responses
503 upstream_unavailablemeans the service is not ready. Wait for the response'sRetry-Afterdelay before retrying. If you already have a task ID, continue querying that task.- A completed task query can temporarily receive
503 result_url_refresh_failedwhen its signed result URL cannot be refreshed. Retry the query usingRetry-After; do not resubmit the image request. 429 concurrency_limit_exceededmeans the account has too many in-flight requests. Retry after the response'sRetry-Afterdelay.
Request parameters and billing, per group: