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 IDsGroupsGeneration / editingExamples
gpt-image-2Official, DiscountJSON generation; file-upload editsOfficial, Discount
gpt-image-2.5-flare, gpt-image-2.5-sunburstOfficialJSON generation; URL references or file-upload editsGPT Image 2.5
gemini-2.5-flash-image-preview, gemini-3.1-flash-image-preview, gemini-3-pro-image-previewOfficial, DiscountJSON generation; URL references or file-upload editsGemini images
seedream-4-0, seedream-4-5, seedream-5-0-lite, seedream-5-0-proOfficialJSON generation; URL references or file-upload editsSeedream
qwen-image-2.0, qwen-image-2.0-pro, qwen-image-3.0, qwen-image-3.0-proOfficialJSON generation; URL references or file-upload editsQwen Image
z-image-turboOfficialJSON text-to-image onlyAlibaba images
wan2.7-image, wan2.7-image-proOfficialJSON generation or image_urls references; no multipart editsAlibaba 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:

OperationRequest
Generate an imagePOST /v1/images/generations with JSON
Edit an uploaded imagePOST /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"
}
ModelOutputs and referencesResolution
seedream-4-0 / seedream-4-5n=1..15; references plus n must not exceed 15; without references, n=1Use the model's supported range
seedream-5-0-liteNative n=1..15; references plus n must not exceed 15Use the model's supported range
seedream-5-0-proFixed n=1; up to 10 references1K, 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

StatusMeaningClient action
submission_unknownSubmission is still being confirmedKeep polling
submittedTask was acceptedKeep polling
processingThe image is being generated or editedKeep polling
meter_pendingOutput exists; billing data is still being confirmedKeep polling
completedResults are readyRead and download data
failedTask failedRead 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_unavailable means the service is not ready. Wait for the response's Retry-After delay before retrying. If you already have a task ID, continue querying that task.
  • A completed task query can temporarily receive 503 result_url_refresh_failed when its signed result URL cannot be refreshed. Retry the query using Retry-After; do not resubmit the image request.
  • 429 concurrency_limit_exceeded means the account has too many in-flight requests. Retry after the response's Retry-After delay.

Request parameters and billing, per group: