arq worker that polls an AI video job with defer_by in Python
An arq task reads Sume's job status once and enqueues itself again with _defer_by from next_poll_after_seconds, giving asyncio polling without a sleep loop.

arq is an asyncio job queue on Redis, and it fits video polling because a task can enqueue its own successor with _defer_by. The task reads Sume's GET /v1/jobs/{id}/status, returns when terminal is true, and otherwise enqueues the same task again after next_poll_after_seconds (Sume jobs guide, read 2026-10-06). One worker process can track hundreds of renders because each task is a short HTTP read.
The submit step below uses seedance-2-mini at 720p for 8 seconds. At Sume's rate that is $1.512 (the 10 second price of $1.89 scaled to 8 seconds).
What are the submit and poll tasks?
ctx['redis'] is the arq pool passed to every task. The Idempotency-Key means an arq retry of submit returns the same job. Start the worker with arq worker.WorkerSettings.
import os
import httpx
from arq.connections import RedisSettings
API = "https://api.sume.com"
H = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
async def submit(ctx, prompt: str, key: str):
async with httpx.AsyncClient(timeout=30) as c:
r = await c.post(f"{API}/v1/videos", headers={**H, "Idempotency-Key": key},
json={"model": "seedance-2-mini", "prompt": prompt,
"duration": 8, "resolution": "720p"})
r.raise_for_status()
await ctx["redis"].enqueue_job("poll", r.json()["id"], 0, _defer_by=15)
async def poll(ctx, job_id: str, n: int):
async with httpx.AsyncClient(timeout=30) as c:
r = await c.get(f"{API}/v1/jobs/{job_id}/status", headers=H)
r.raise_for_status()
s = r.json()
if s["terminal"]:
return s["sume_status"]
if n < 120:
wait = s.get("next_poll_after_seconds") or 15
await ctx["redis"].enqueue_job("poll", job_id, n + 1, _defer_by=wait)
class WorkerSettings:
functions = [submit, poll]
redis_settings = RedisSettings()Why does a queued job need a longer delay?
A job that is accepted but not yet running is queued, and the status response suggests a delay of up to 30 seconds for it. Sume runs a limited number of jobs per workspace at once (Pro: 4 processing and 20 queued, per the generation admission guide), so submitting 30 clips at once leaves most waiting. Honoring the suggested delay keeps the read count low during that wait.
Does a retry of the poller bill a second clip?
No, as long as the poller only reads. Keep the paid POST /v1/videos in a separate step with an Idempotency-Key you own, and let the poller use GET /v1/jobs/{id}/status. A client-side give-up never cancels the render; Sume's jobs guide says the job keeps running, so store the job id and read it again later instead of submitting a second time.
Sources
Related posts
More in Developers
- asyncio Semaphore size for Sume image batches: accepted capacity
Size the semaphore to what Sume accepts, concurrency plus queue: Free 6, Pro 24, Startup 48, Scale 120. A fake-submit test proves the peak never exceeds it.
- asyncio TaskGroup cancels siblings: poll many Sume jobs safely
A TaskGroup cancels every other poller when one raises. For a batch of Sume video jobs that abandons waits, not jobs. Catch inside the task and return results.
- Check balance before bulk transcription: 402 and admission preview
A 402 insufficient_credits arrives before any provider work. Read GET /v1/balance and POST /v1/generation/admission-preview first and size the run.
- Port a Bedrock image call to Sume /v1/images in Python
Moving from boto3 invoke_model for Nova Canvas or Titan to Sume's REST call: the request mapping, the response shape, the status codes, and the swap code.
Written by Sume