Async Jobs API

Manage long-running media generation jobs across image, video, and audio modalities. The Jobs API lets you poll job status, list all jobs for your tenant, and cancel in-flight jobs before they consume credits.

Async jobs are opt-in on image and audio endpoints (?async=true). Video generation is always async. Sync endpoints (chat, embeddings) are unaffected and always return HTTP 200 inline.

Job lifecycle

All async jobs move through the same state machine:

queued β†’ in_progress β†’ succeeded
                         β†˜ failed
                         β†˜ cancelled (via DELETE)
                         β†˜ expired (artifact TTL reached)

The job object

NameTypeRequiredDescription
id
stringRequiredUnique job identifier. Prefix encodes modality: img_* (image), vid_* (video), aud_* (audio).
object
stringRequiredModality discriminator: "image_job" | "video_job" | "audio_job".
status
stringRequiredqueued | in_progress | succeeded | failed | cancelled | expired
model
stringRequiredStandard model alias used to create the job (e.g. openai/gpt-image-2).
provider
stringUpstream provider that processed the job. May be null while queued.
modality
stringRequiredimage | video | audio
created_at
numberRequiredUnix timestamp when the job was submitted.
completed_at
number | nullUnix timestamp when the job reached a terminal state.
expires_at
number | nullUnix timestamp after which artifact URLs are no longer accessible (typically 1 hour post-completion).
result
object | nullModality-specific result payload. Present only when status=succeeded.
error
object | nullError details when status=failed. Contains code and message.
usage
object | nullToken or unit consumption used for billing.

Endpoints

Retrieve a job

GET /v1/jobs/:id

Returns the current state of an async job. Works for all modalities β€” the id prefix (img_, vid_, aud_) routes to the correct table automatically.

bash
curl https://api.therouter.ai/v1/jobs/img_01J9Z7QP8BT4EWHF6V3KDMSR99 \
  -H "Authorization: Bearer $THEROUTER_API_KEY"
json
{
  "id": "img_01J9Z7QP8BT4EWHF6V3KDMSR99",
  "object": "job",
  "status": "succeeded",
  "modality": "image",
  "model": "openai/gpt-image-2",
  "provider": "openai-api",
  "created_at": 1746700800,
  "completed_at": 1746700890,
  "expires_at": 1746704490,
  "result": {
    "data": [{ "url": "https://..." }]
  },
  "usage": { "image_count": 1 }
}

List jobs

GET /v1/customer/jobs

Returns a paginated list of all async jobs for the authenticated tenant, across all three modalities. Supports filtering by modality, status, model, and date range.

NameTypeRequiredDescription
modality
stringFilter by modality: image | video | audio. Omit for all.
status
stringFilter by status: queued | in_progress | succeeded | failed | cancelled | expired.
model
stringFilter by model alias (exact match).
start_date
stringISO 8601 date lower bound for created_at.
end_date
stringISO 8601 date upper bound for created_at.
limit
numberPage size. Range: 1–200. Default: 50.
offset
numberPagination offset. Default: 0.
bash
# List most recent succeeded image jobs
curl "https://api.therouter.ai/v1/customer/jobs?modality=image&status=succeeded&limit=20" \
  -H "Authorization: Bearer $THEROUTER_API_KEY"
json
{
  "jobs": [
    {
      "id": "img_01J9Z7QP8BT4EWHF6V3KDMSR99",
      "modality": "image",
      "model": "openai/gpt-image-2",
      "status": "succeeded",
      "created_at": "2026-05-08T10:00:00Z",
      "actual_cost": 0.04,
      "reserved_credits": 0.05,
      "error_message": null,
      "expires_at": "2026-05-08T11:01:30Z"
    }
  ],
  "pagination": {
    "offset": 0,
    "limit": 20,
    "has_more": false
  }
}

Modality-specific aliases

OpenAI-compatible alias routes delegate to the same underlying job resolver:

  • GET /v1/images/:id β€” alias for image jobs
  • GET /v1/videos/:id β€” alias for video jobs

Polling pattern

Poll GET /v1/jobs/:id until status is a terminal state (succeeded, failed, cancelled, or expired).

Use exponential backoff β€” start at 2 s, cap at 30 s. Artifact URLs expire 1 hour after the job completes.
javascript
async function pollJob(id, apiKey) {
  let delay = 2000;
  while (true) {
    const res = await fetch(`https://api.therouter.ai/v1/jobs/${id}`, {
      headers: { Authorization: `Bearer ${apiKey}` },
    });
    const job = await res.json();
    if (['succeeded', 'failed', 'cancelled', 'expired'].includes(job.status)) {
      return job;
    }
    await new Promise((r) => setTimeout(r, delay));
    delay = Math.min(delay * 1.5, 30_000);
  }
}

Billing

Credits are reserved at submission time (shown as reserved_credits in the job list). The final actual_cost is settled on completion. Failed, cancelled, or expired jobs are fully refunded β€” no credits are consumed.

Help & contact