Polling a 5-minute video: 30 s is 10 reads, 2 s is 150 on Sume
How often to poll a Sume video job: read counts for a 300 s job at 30 s and 2 s, and an asyncio loop that obeys next_poll_after_seconds.

Poll as often as the response tells you to. When next_poll_after_seconds is present, obey it; when it is not, back off. For a video that happens to take 300 seconds, a fixed 30-second interval costs 10 status reads and the SDK's 2-second floor costs 150. Both are far below the read budget, so the interval is a latency choice, not a limit problem.
Two documented defaults
Sume's video guide suggests a moderate interval such as 30 seconds, and notes that generation usually takes from 30 seconds to several minutes. The TypeScript SDK waitForJob uses a 2-second poll interval as a floor, a 20-minute default timeout, and lets a longer next_poll_after_seconds win.
| Interval | Reads for 300 s | Reads per minute for one job | Share of a Free key's 4,800 reads per minute |
|---|---|---|---|
| 30 s | 10 | 2 | 0.04% |
| 2 s | 150 | 30 | 0.63% |
| 2 s, all 6 accepted Free jobs at once | 900 | 180 | 3.75% |
What the loop should do
Stop on terminal: true, not on a guess. Honor next_poll_after_seconds. Keep your own deadline, because the server-side sync wait is capped at 30 seconds and says nothing about job duration. A client timeout does not cancel the job, and it still bills. Store the job id so you can read it again.
import asyncio
import json
import os
import time
import urllib.request
HEADERS = {"Authorization": f"Bearer {os.environ.get('SUME_API_KEY', '')}"}
def get(path: str) -> dict:
req = urllib.request.Request("https://api.sume.com/v1" + path, headers=HEADERS)
with urllib.request.urlopen(req, timeout=20) as resp:
body = json.load(resp)
return body.get("data", body)
async def wait(job_id: str, deadline_s: float = 1200) -> dict:
end = time.monotonic() + deadline_s
reads, delay = 0, 2.0
while time.monotonic() < end:
status = await asyncio.to_thread(get, f"/jobs/{job_id}/status")
reads += 1
if status.get("terminal"):
print("status reads:", reads)
return status
delay = float(status.get("next_poll_after_seconds") or min(delay * 2, 30))
await asyncio.sleep(delay)
raise TimeoutError(job_id) # the job keeps running and billing
async def main() -> None:
print(await wait(os.environ["JOB_ID"]))
asyncio.run(main())Reads have their own budget
Reads and writes are separate buckets, and reads get 40 times the plan's write number. On Free that is 4,800 reads per minute. A poll loop cannot cause a 429 on your submits. Reading ratelimit-remaining and retry-after is still better than counting calls yourself.
If many clients start at once, add jitter. The SDK helpers do, because without it clients that start together stay in phase and hit the ceiling as a group.
Choosing an interval
Use the server's hint. The status response carries next_poll_after_seconds, and following it keeps reads low while still catching completion promptly. The read bucket is large, 40 times the write limit, so polling rarely causes a 429 by itself, but a fleet of pollers on one key can add up.
For long jobs, prefer a webhook plus one backup poll. If a job takes 300 seconds, a 30-second interval costs about 10 reads, while a 2-second interval costs 150 for the same information.
Sources
Related posts
More in Developers
- Porting chat history to Agent Completions: assistant turns fail
Sume Agent Completions take OpenAI-style messages but return 400 on any assistant turn and start each call in a new thread. What to send instead.
- OpenRouter video payload on Sume: drop seed, size, provider.options
Sume /v1/videos follows the OpenRouter shape but rejects seed, size and provider.options with a 400. A Node function that strips them and checks the model id.
- POST /v1/videos status codes: which of 10 are safe to retry
OpenAPI lists 202 plus ten error codes for POST /v1/videos. Which to retry with the same key, which to fix or stop on, a Python classifier and a backoff plan.
- Preflight a mixed 30 s batch: 2 Seedance and 4 Wan needs $49.67
Sum the reserve for two Seedance 2.5 720p clips and four Wan 3.0 720p clips (49,668,000 micros), compare with GET /v1/balance in integers, then submit.
Written by Sume