Poll every 2 s or 30 s? Reads per 20-minute video job on Free and Pro

Polling a Sume video job every 2 s is 600 reads in 20 minutes; every 30 s is 40. Share of the read budget on Free and Pro, with a Python check.

5 min readSume
All posts

Polling a video job every 2 seconds for 20 minutes is 600 reads; every 30 seconds it is 40. Even at 2 seconds, a Pro workspace with all 24 accepted jobs polled by one key uses 6.00% of its read budget, and a Free workspace with its 6 jobs uses 3.75%. So rate limits are not the reason to slow down; cost of your own compute and noise are. The 30-second cadence in Sume's examples is a default, not a limit.

The two budgets

Each API key has a per-minute budget for all of /v1, set by the plan of the workspace that owns it. Reads and writes are counted separately, so a tight poll loop cannot cause a 429 on your own submits. A read is any GET or HEAD, including polls of status, events and result. Free gets 4,800 reads and 120 writes per minute; Pro gets 12,000 reads and 300 writes.

The number of jobs you can have in flight is capped too. Free accepts 6 paid generation jobs at a time (1 processing plus 5 queued) and Pro accepts 24 (4 plus 20), so those are the worst cases for the share table: every accepted job being polled at the same interval.

Reads per job and share of the per-minute read budget if every accepted job is polled (Sume docs, read 2026-10-09)
IntervalPolls per job (20 min)Free (6 jobs)Pro (24 jobs)
2 s6003.75%6.00%
5 s2401.50%2.40%
10 s1200.75%1.20%
30 s400.25%0.40%

How the numbers are computed

For Pro at 2 seconds: 24 jobs x 60 / 2 = 720 reads per minute, and 720 / 12,000 = 6.00%. For Free at 30 seconds: 6 x 60 / 30 = 12 reads per minute, and 12 / 4,800 = 0.25%. Polls per job is 1,200 seconds divided by the interval. The script does the same arithmetic from the two plan tables so you can add Startup and Scale.

READS_PER_MIN = {"free": 4800, "pro": 12000, "startup": 24000, "scale": 48000}
ACCEPTED_JOBS = {"free": 6, "pro": 24, "startup": 48, "scale": 120}
DEADLINE_S = 20 * 60

print("interval  polls_per_job  free(6 jobs)  pro(24 jobs)")
for interval in (2, 5, 10, 30):
    row = []
    for plan in ("free", "pro"):
        per_min = ACCEPTED_JOBS[plan] * 60 / interval
        row.append(f"{per_min / READS_PER_MIN[plan]:.2%}")
    print(f"{interval:>6}s  {DEADLINE_S // interval:>13}  {row[0]:>12}  {row[1]:>12}")

What to do instead of tuning

Fixed intervals are the simplest policy and they are fine. Better ones are cheap to add. On the generic job routes the status response can include next_poll_after_seconds, and the docs say to obey it when present and use backoff when it is not. A webhook with callback_url removes most polls, and the docs still advise keeping a slow poll as a backup for deliveries that never arrive. A webhook receiver with a 30-second backup poll uses 40 reads per 20-minute job at most, the last row of the table, and usually far fewer because the poll stops when the callback lands.

One caveat: the budget is per key. If several services share one key, add their polls together before you read the table.

Startup and Scale follow the same arithmetic. Startup reads 24,000 per minute with 48 accepted jobs, and Scale reads 48,000 per minute with 120 accepted jobs. At a 2-second interval that is 48 x 30 / 24,000 = 6.00% and 120 x 30 / 48,000 = 7.50% of the read budget, so the share stays in single digits at every self-serve plan even for the most aggressive loop.

  • Start fast for short clips, then back off.
  • Poll less when a job is queued; it cannot finish before it starts.
  • Keep a 30-second backup poll when you use webhooks.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume