Skip to main content

Command Palette

Search for a command to run...

How to Poll Asynchronous OpenAI Image Tasks (A Practical Guide)

A builder-focused pattern for tracking callback-mode image jobs with task IDs, polling, and batch retrieval.

Updated
4 min readView as Markdown
How to Poll Asynchronous OpenAI Image Tasks (A Practical Guide)

When image generation takes longer than a normal HTTP request, the hard part is not starting the job—it is tracking it without making your application fragile.

A developer workflow for polling asynchronous image tasks

This guide shows a small, practical pattern for submitting an OpenAI image request in callback mode and checking its result later through Ace Data Cloud. The useful idea is simple: keep the returned task_id, poll a dedicated task endpoint, and treat “not finished yet” as a normal application state rather than an error.

What the Tasks API is for

The Tasks API exists for image requests submitted in callback mode. Add a callback_url to the original image generation or editing request, and the submission returns a task_id immediately. Your worker, scheduled job, or backend can then query that task independently.

There is one important boundary: task records are persisted only when the original image request includes callback_url. A synchronous request does not create a task that can later be queried through this interface.

The task endpoint is:

POST https://api.acedata.cloud/openai/tasks

Use these headers:

accept: application/json
authorization: Bearer {token}
content-type: application/json

The endpoint supports two actions:

  • retrieve fetches one task by id or trace_id.
  • retrieve_batch searches multiple tasks by IDs, trace IDs, application, user, type, or creation window.

For most integrations, id is the simplest choice because it is the task_id returned by the original submission. A custom trace_id is useful only when you want to associate a request with your own business identifier.

Query one task with retrieve

Suppose your application has already submitted an image request and stored the returned task ID. A single-task lookup needs only the action and that ID:

curl -X POST 'https://api.acedata.cloud/openai/tasks' \
  -H 'accept: application/json' \
  -H 'authorization: Bearer {token}' \
  -H 'content-type: application/json' \
  -d '{
    "action": "retrieve",
    "id": "7489df4c-ef03-4de0-b598-e9a590793434"
  }'

A matching task can contain its original request, final response, timestamps such as created_at, started_at, and finished_at, plus elapsed. The response also identifies the relevant application_id, user_id, and credential_id. If nothing matches, the API returns an empty object, so handle {} explicitly rather than assuming every HTTP success contains a task.

The final image URL lives inside the task's response after processing completes. Until that response is present, the task is still work in progress.

Build a polling loop that stays boring

Polling code should be predictable. Submit once, save the ID, then query the task at a measured interval. Do not resubmit the generation request merely because the result is not ready yet.

import os
import time
import requests

API = "https://api.acedata.cloud"
HEADERS = {
    "authorization": f"Bearer {os.environ['ACEDATA_API_KEY']}",
    "content-type": "application/json",
}

submit = requests.post(
    f"{API}/openai/images/generations",
    headers=HEADERS,
    json={
        "model": "gpt-image-1",
        "prompt": "A watercolor style cat sitting on a table",
        "callback_url": "https://webhook.site/your-uuid",
    },
).json()

task_id = submit["task_id"]

while True:
    task = requests.post(
        f"{API}/openai/tasks",
        headers=HEADERS,
        json={"action": "retrieve", "id": task_id},
    ).json()

    if task and task.get("response"):
        print(task["response"])
        break

    time.sleep(3)

In production, add a maximum wait time and store the task ID before polling. That way, a process restart does not lose the job. You can also let your callback handler update the same database row; polling then becomes a recovery path rather than the only completion mechanism.

Use retrieve_batch for operations and dashboards

Single retrieval works well for a request page, but operational tools often need a wider view. Set action to retrieve_batch and filter with one of ids, trace_ids, application_id, user_id, or a created_at_min / created_at_max window.

{
  "action": "retrieve_batch",
  "trace_ids": ["my-trace-001", "my-trace-002"]
}

The batch response contains items and count. You can narrow it further with type, using images, images_generations, or images_edits, and paginate with offset and limit (the documented default limit is 12). This is useful for an internal queue viewer, a retry dashboard, or a support tool that needs to locate a request by a customer-facing trace ID.

A practical mental model

Treat asynchronous image generation as a small state machine: submitted, running, and finished. The task_id is the stable handle; finished_at and response tell you when the useful output exists. Keep submission separate from status checks, make empty results safe, and use batch retrieval only when you actually need an operational view.

That separation is what makes the workflow maintainable: your user-facing request can return quickly, while the slower image work continues behind a clean, inspectable interface. The complete field reference and response examples are in the OpenAI Tasks API integration guide.

More from this blog

A

Ace Data Cloud Blog — AI Infrastructure, 200+ Models, One API

193 posts

The official blog of Ace Data Cloud, one API key and one balance for every major AI model. Chat, image, video, music, and search, all pay-per-use and priced below going direct. Autonomous agents can even pay per call through native x402. Here we share product launches, deep dives into how our infrastructure works, real usage numbers, and practical guides to building on Ace. Written to be clear and useful, whether you're a developer shipping fast, a builder, or just AI-curious.