Developers

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.

5 min readSume
All posts

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.

Job routes from the API reference, read 2026-09-26.
RouteUse it for
GET /v1/jobs/{id}/statusPolling. A lightweight status read.
GET /v1/jobs/{id}/resultThe 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}/eventsPublic timeline events for debugging and recovery.
POST /v1/jobs/{id}/cancelCancel, 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.

Status fields from Jobs and results and the OpenAPI reference, read 2026-09-26.
FieldMeaning
terminalTrue when the job is completed, failed, or canceled and normal polling can stop.
result_readyTrue only when /result can return 200 with a result body.
next_actionpoll_status while queued or processing, fetch_result once completed, inspect_events for failed or canceled jobs.
next_poll_after_secondsSuggested minimum delay before the next poll. Null for terminal jobs.
recommended_poll_interval_secondsThe default SDK and CLI polling cadence. Null for terminal jobs.
cancelable, cancel_urlcancelable is true only before external generation work starts. cancel_url is null after that and on terminal jobs.
logs_availableFalse 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_seconds when present; otherwise back off exponentially. Avoid tight loops across many jobs.
  • Keep polling until terminal is true or your own deadline passes. For paid generation, queued is a normal accepted state.
  • Status reads come from the read budget, which is separate from the write budget, so a poll loop cannot 429 the submits that spawned it. See Sume API errors and rate limits.
  • In TypeScript, waitForJob from @sume-com/sdk is this loop. Its default timeout is 20 minutes, and its 2-second pollInterval is a floor: a longer next_poll_after_seconds wins.
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.error

What 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