Poll a Sume job in Python with a deadline and next_poll_after_seconds
A Python wait loop for a Sume job: stop on terminal, sleep at least next_poll_after_seconds, and give up at a monotonic deadline. A timeout is not a failure.

A polling loop with no end is a bug that waits for the right bad day. Generation jobs on the Sume API can sit in a queue behind your workspace's concurrency limit, so the time a job takes is the sum of waiting and running, and you do not control the first part. Your own code needs a ceiling that is separate from the API's.
The status route makes the loop short. GET /v1/jobs/{id}/status returns terminal, a sume_status, a result_url and a next_poll_after_seconds hint. The hint is a minimum, not a schedule: asking sooner just spends read budget on an answer that cannot have changed.
Three rules for the loop
| Rule | Reason |
|---|---|
| Stop when terminal is true | completed, failed and canceled are all terminal, so do not test for completed alone |
| Sleep at least next_poll_after_seconds | The API names the earliest useful next read |
| Check a monotonic deadline before each sleep | Wall-clock changes cannot stretch or cut your ceiling |
The loop
The function uses time.monotonic() for the deadline and refuses to start a sleep that would end after it. That means it raises before the deadline instead of overshooting by one pause. The sample stops after one success, and a second call with a short deadline would raise. It returns the status data for every terminal state, so the caller reads sume_status and decides what failure means.
import json, os, time, urllib.request
HEAD = {"x-api-key": os.environ["SUME_API_KEY"]}
def status(job_id: str) -> dict:
url = f"https://api.sume.com/v1/jobs/{job_id}/status"
with urllib.request.urlopen(urllib.request.Request(url, headers=HEAD), timeout=30) as r:
return json.load(r)["data"]
def wait(job_id: str, deadline_s: float = 900.0, floor_s: float = 2.0) -> dict:
end = time.monotonic() + deadline_s
while True:
s = status(job_id)
if s["terminal"]:
return s
pause = max(floor_s, s.get("next_poll_after_seconds") or 0)
if time.monotonic() + pause > end:
raise TimeoutError(f"{job_id} still {s['sume_status']} after {deadline_s:g}s")
time.sleep(pause)
s = wait("job_1", deadline_s=30)
print(s["sume_status"], s["result_url"])What a timeout means
TimeoutErrorsays your loop gave up, not that the job failed. The job keeps running and may still bill, so store the job id and check it later instead of resubmitting.- Resubmitting after a timeout creates a second paid job unless you send the same
Idempotency-Key. Waiting longer is usually the cheaper answer. - A non-completed terminal state is a separate branch. Read
GET /v1/jobs/{id}for the error object whensume_statusisfailed. - Count reads. Polling one job every 2 seconds is 30 reads a minute, and reads default to 40 times the write budget, so a handful of jobs is fine and a large pool should share a single list call.
- Choose the deadline from the job type. A short clip and a long render should not share one number, and a webhook is a better fit for the long ones.
The status fields and the polling advice are in the jobs guide.
Sources
Related posts
More in Developers
- Log x-sume-request-id and Idempotency-Key on every call (Python)
A requests response hook that writes one JSON log line per Sume call: x-sume-request-id, idempotency key, error code and rate-limit headers. Tested.
- A Python SumeError class: code, retryable, next_action, request id
Turn a Sume error envelope into one Python exception that carries code, retryable, retry_after_seconds, next_action and the request id. Standard library only.
- Python webhook receiver: read artifacts[].url and error.next_action
A standard-library Python receiver for Sume job webhooks: verify, then branch on status OK or ERROR to get the artifact URL or error.retryable and next_action.
- Does a queue_full 429 charge me? Sume's reservation rules
A Sume 429 queue_full means the workspace has no accepted-job capacity left. The failed admission releases its reservation; retry with the same Idempotency-Key.
Written by Sume