Python asyncio loop for a Sume job: next_poll_after_seconds
A runnable httpx and asyncio loop for GET /v1/jobs/{id}/status that honors next_poll_after_seconds, backs off otherwise, and leaves the job running on timeout.

Polling a Sume job in Python is a loop over GET /v1/jobs/{id}/status that stops on terminal, sleeps for next_poll_after_seconds when the server sends it, and otherwise backs off. The deadline belongs to your client. The job keeps running and billing if you stop waiting, so a timeout must not trigger a new paid submit.
The sample uses httpx and wraps the awaits in asyncio.run(main()). It reads a job id from the command line, so you can resume a wait for a job you stored earlier.
import asyncio, os, sys, httpx
BASE = "https://api.sume.com/v1"
async def wait_job(client, job_id, deadline=1200.0):
loop = asyncio.get_running_loop()
end = loop.time() + deadline
delay = 2.0
while loop.time() < end:
r = await client.get(f"{BASE}/jobs/{job_id}/status")
r.raise_for_status()
s = r.json()
if s.get("terminal"):
return s
hint = s.get("next_poll_after_seconds")
delay = max(2.0, float(hint)) if hint else min(delay * 1.5, 30.0)
await asyncio.sleep(delay)
raise TimeoutError(f"still running; job {job_id} was not canceled")
async def main():
key = os.environ["SUME_API_KEY"]
async with httpx.AsyncClient(headers={"Authorization": f"Bearer {key}"}, timeout=30) as c:
s = await wait_job(c, sys.argv[1])
print(s.get("sume_status"), s.get("result_ready"))
asyncio.run(main())
What the loop relies on
Per the Jobs and results page, you can poll on the booleans terminal and result_ready, or on sume_status. The same payload has a queue-shaped status field (IN_QUEUE, IN_PROGRESS, COMPLETED, FAILED, CANCELED) that maps one to one onto sume_status. Pick one and do not mix them.
After the loop returns, fetch the result only if the job completed. GET /v1/jobs/{id}/result answers 409 job_not_completed for failed and canceled jobs, so read the failure from GET /v1/jobs/{id} instead.
queuedandprocessingare normal non-terminal states.- A
429on the status read is poll backpressure: wait forretry-afterwhen present. - Never resubmit the paid request because this function raised
TimeoutError. - Store the job id before you start waiting, so a restart can resume.
Tuning
The 2-second floor and 30-second cap are choices, not API rules. The documented rules are to obey next_poll_after_seconds when present, use backoff when not, and stop on a terminal state or your own application deadline. For video, 20 minutes is the deadline the TypeScript SDK uses by default, and it is a reasonable starting value here too.
Running it
Run SUME_API_KEY=... python wait_job.py job_123. The script prints the final sume_status and whether result_ready is true. Because the id is a command-line argument, the same file resumes a wait after a restart, which is the reason to store job ids as soon as the submit returns.
To wait for many jobs, wrap wait_job in asyncio.gather, but keep the total below the accepted-capacity numbers of your plan, and give each task its own delay state. Add a semaphore so a Free workspace with 6 accepted jobs does not poll 600 at once.
Treat this as a habit, not a one-time fix. Write the rule down next to the code that calls the API, add a test that exercises it, and review it whenever the docs change. Check the linked documentation pages in the sources list for the current wording before you rely on any number here, because limits and field names can be revised, and a short test run costs far less than debugging a production incident.
When something does not match what you read here, capture the x-sume-request-id response header and the job or run id, and send those to support. Do not paste API keys, signing secrets or full request bodies into a ticket or a chat; the ids are enough for the team to find the request.
Sources
Related posts
More in Developers
- Python cost cap for a mixed media job: round up, then refuse
A 25-line Python estimator with Decimal prices that rounds the total up to the cent, as Sume's reservation does, and exits before submit if it is over your cap.
- Python httpx 429 handler for Sume: retry-after, then ratelimit-reset
A small async Python wrapper for the Sume API that waits on retry-after, falls back to ratelimit-reset, and never retries a POST that lacks an Idempotency-Key.
- Python verifier for Sume webhooks: rotation header and empty secrets
A Python function that checks the sume-v1 HMAC over timestamp.raw_body, accepts either signature during rotation, and refuses an empty secret. Under 30 lines.
- Quantized H3 vs lossless H3 vs a hosted clip: compare fairly
MiniMax's guide says not to mix ComfyUI quantized and SGLang lossless outputs. How to set up a fair comparison with a hosted clip, including the seed catch.
Written by Sume