How to poll a video generation job status API: Sume's /v1/jobs
Poll GET /v1/jobs/{id}/status until terminal is true, wait next_poll_after_seconds between reads, then fetch /result once result_ready is true.

To poll a video generation job on Sume, read GET /v1/jobs/{id}/status with the job id your submit returned, wait at least next_poll_after_seconds between reads, stop when terminal is true, and fetch GET /v1/jobs/{id}/result once result_ready is true. A failed or canceled job has no result: read its error from the job record at GET /v1/jobs/{id}.
The routes and rules below come from Sume's Jobs and results and API reference docs, with field definitions from the live OpenAPI reference, read on 2026-09-26. Format runs have their own receipt; see Sume Format run lifecycle.
Which job endpoints do I poll?
Sume generation endpoints create durable jobs. A submit that answers with Sume's job envelope carries the job id as request_id, with status_url and result_url, in its first response. queued and processing are non-terminal; completed, failed, and canceled are terminal.
POST /v1/videos answers with its own bare object instead: the job id and a polling_url, GET /v1/videos/{id}, whose statuses are spelled pending, in_progress, and cancelled. The same job is visible at GET /v1/jobs/{id}/status and GET /v1/jobs/{id}/result.
| Route | Use it for |
|---|---|
GET /v1/jobs/{id}/status | Polling. A lightweight status read. |
GET /v1/jobs/{id}/result | The completed payload, with public artifact URLs when available. Any other status answers 409 job_not_completed. |
GET /v1/jobs/{id} | The public job record, where a failed job's error lives. |
GET /v1/jobs/{id}/events | Public timeline events for debugging and recovery. |
POST /v1/jobs/{id}/cancel | Cancel, only before generation starts. |
What does the status payload tell me?
Poll on the booleans (terminal, result_ready) or on sume_status. The payload also has a queue-shaped status (IN_QUEUE, IN_PROGRESS, COMPLETED, FAILED, CANCELED) for clients ported from other queue APIs. It maps one-to-one onto sume_status, and the docs say not to mix the two.
| Field | Meaning |
|---|---|
terminal | True when the job is completed, failed, or canceled and normal polling can stop. |
result_ready | True only when /result can return 200 with a result body. |
next_action | poll_status while queued or processing, fetch_result once completed, inspect_events for failed or canceled jobs. |
next_poll_after_seconds | Suggested minimum delay before the next poll. Null for terminal jobs. |
recommended_poll_interval_seconds | The default SDK and CLI polling cadence. Null for terminal jobs. |
cancelable, cancel_url | cancelable is true only before external generation work starts. cancel_url is null after that and on terminal jobs. |
logs_available | False while lifecycle diagnostics come through events_url instead of inline logs. |
How should my poll loop behave?
The docs give this loop as the client-side way to wait for a job. The wait lives in your client, so it can run for minutes without holding an HTTP request open.
- Honor
next_poll_after_secondswhen present; otherwise back off exponentially. Avoid tight loops across many jobs. - Keep polling until
terminalistrueor your own deadline passes. For paid generation,queuedis a normal accepted state. - Status reads come from the read budget, which is separate from the write budget, so a poll loop cannot
429the submits that spawned it. See Sume API errors and rate limits. - In TypeScript,
waitForJobfrom@sume-com/sdkis this loop. Its default timeout is 20 minutes, and its 2-secondpollIntervalis a floor: a longernext_poll_after_secondswins.
job = POST /v1/{product}/generate { mode: "async", ... } with Idempotency-Key
loop:
s = GET /v1/jobs/{job.id}/status
onStatus(s) # optional progress callback
if s.terminal: break
sleep(s.next_poll_after_seconds or exponential backoff)
if s.sume_status == "completed":
return GET /v1/jobs/{job.id}/result
else: # failed or canceled
raise from (GET /v1/jobs/{job.id}).job.errorWhat is in a completed result?
GET /v1/jobs/{id}/result answers only for completed jobs. Queued, processing, failed, and canceled jobs get 409 job_not_completed with the current status in details.
A completed result can include artifacts. Each has an opaque id, a url on media.sume.com, a type (image, video, audio, model, or file), and a content_type, and can carry size_bytes, width, height, duration_ms, and checksum_sha256. Do not parse artifact URLs; the path is opaque. Raw provider URLs are not public API outputs.
Why is my job still queued?
Workspace concurrency applies when workers move jobs into processing, not when the API accepts them, so an accepted job can wait. The status payload's queue object explains why: state (waiting, deferred, runtime_unavailable, …), a provider-neutral reason, and available_at, the earliest time the job is eligible for worker pickup or retry. queue.position stays null until Sume computes a real rank; the API does not return fake queue positions. See Video job concurrency and queueing.
For a step-by-step trail, GET /v1/jobs/{id}/events lists job.created, job.queued, job.started, generation.submitted, job.completed, job.failed, job.canceled, and webhook.delivery. It is a pull snapshot, not a stream, and hides raw provider task ids and URLs.
What if my poller times out or crashes?
The job carries on: a client-side timeout does not cancel it, and it keeps running and still bills. Store the job id at submit and resume from status_url instead of resubmitting; idempotency keys for AI video APIs covers safe retries, and list and recover video jobs shows how to find lost ids.
To skip the loop, ask for a terminal webhook: mode: "webhook" with a public HTTPS webhook_url on routes that take mode, or callback_url on POST /v1/videos. Job webhooks are terminal-only (job.completed, job.failed, job.canceled); keep status_url polling available for missed deliveries. On hosted MCP, jobs_wait does the waiting; see MCP jobs_wait for long video jobs.
Sources
Related posts
Written by Sume