OpenRouter: poll video every 30 s. Sume: obey next_poll_after_seconds

OpenRouter recommends a 30 second poll on video. Sume's jobs API returns next_poll_after_seconds, else backoff. A Python poller that does both.

5 min readSume
All posts

OpenRouter recommends polling a video job every 30 seconds, and Sume's own /v1/videos example also sleeps 30 seconds, but Sume's jobs API can do better: its status response may include next_poll_after_seconds, which you should obey, with exponential backoff as the fallback. A poller that reads that field uses fewer requests than a fixed 30 second sleep and reacts sooner at the start.

OpenRouter's recommendation is from its video generation guide, read 2026-10-10. Sume's are from the Jobs and results and Videos API docs.

Two polling contracts

On OpenRouter you submit to POST /api/v1/videos, poll the polling_url, and download from unsigned_urls[0] when the job completes. On Sume's OpenRouter-compatible route the shapes match, and the example loop waits 30 seconds between polls.

Sume's job-level endpoint, GET /v1/jobs/{id}/status, is the better target for a new poller. Its envelope has the booleans terminal and result_ready, and the docs say to continue polling status_url, obey next_poll_after_seconds when present, and fall back to backoff.

OpenRouter from its video guide (read 2026-10-10); Sume from the Jobs and results and Videos API docs.
QuestionOpenRouterSume
Where to pollpolling_urlpolling_url on /v1/videos, or status_url on the jobs API
Suggested wait30 seconds30 seconds in the /v1/videos example; next_poll_after_seconds when the job envelope gives it
Stop conditionA completed or failed statusterminal is true
Result readyunsigned_urls[0]result_ready true, then GET result_url
Read budgetNot read from the pageReads get 40 times the write budget per plan
Do notNot read from the pageSubmit a new paid job because a local timeout fired

A poller that obeys both

The loop below starts with a short delay, uses the server's hint when present, and otherwise doubles its wait up to 30 seconds, which keeps it no more aggressive than the OpenRouter recommendation at steady state.

import time, requests

def wait(status_url, headers, deadline_s=1200):
    delay, end = 3.0, time.time() + deadline_s
    while time.time() < end:
        r = requests.get(status_url, headers=headers, timeout=30)
        r.raise_for_status()
        s = r.json()
        s = s.get("data", s)
        if s.get("terminal"):
            return s
        hint = s.get("next_poll_after_seconds")
        delay = float(hint) if hint else min(delay * 2, 30.0)
        time.sleep(delay)
    raise TimeoutError("deadline passed; keep the job id")

Why the hint matters

Reads have their own bucket on Sume, forty times the write limit, so a polling loop will not block your submits, and a 429 names which bucket you hit in error.details.scope. Even so, a long fixed sleep wastes the first seconds of a short job, while a tight loop on a long job just burns requests. Honouring the hint lets the server tell you when to come back.

The deadline is client-side. For video, the Sume docs suggest 20 minutes is reasonable. When it passes, stop polling but keep the job id; the job may still finish and bill, so look at it again later instead of submitting a new one.

Mixing a webhook with the poll

The cheapest design is a webhook plus a slow sweep. Submit with a webhook URL, record the job id, and let a background task read the status of any job that is still open after a few minutes. The webhook handles the common case at no polling cost, and the sweep catches deliveries that never arrived. Sume's docs ask for exactly this: continue to poll as a backup.

If you move from OpenRouter, remember that the callback body changes shape on Sume, so the handler is not a drop-in. Sume signs the raw body with a timestamp, and your verifier must refuse an empty secret.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume