Callback or polling for a ported video worker: pick by job count

Replacing a Sora polling worker on Sume: when callback_url beats polling, when polling is enough, and one function that does both.

5 min readSume
All posts

Use polling when your worker handles a handful of video jobs and already loops; use callback_url when jobs number in the hundreds or when you do not want a process alive just to wait. Sume lets you do both on the same job: send callback_url on POST /v1/videos, and keep a poll as the backup, because a failed delivery never changes the job's real state (Sume webhooks, read 2026-10-06).

The OpenAI Sora API ended on 2026-09-24 (Magic Hour tracker, read 2026-10-06). A worker that polled it needs a new target either way, so this is the moment to choose how it hears about results.

The two ways to hear back

Polling is GET /v1/videos/{id} until the status is completed, failed or cancelled. The docs suggest about a 30-second interval, since video takes from 30 seconds to several minutes. A webhook is a signed POST that Sume sends once the job reaches a terminal state; it sends terminal events only, never progress (Sume video docs, read 2026-10-06).

Polling versus callback, from the Sume docs, read 2026-10-06
QuestionPollingcallback_url
Who initiatesYour worker, every pollSume, once per terminal state
Progress eventsStatus onlyNone, terminal events only
Needs a public endpointNoYes, HTTPS, not localhost or private network
If your endpoint is downNot applicableUp to 10 attempts, 30 s apart, 10 s timeout each
Recovery pathKeep pollingPoll the status URL; redeliver per job

A rule of thumb

  • Under about twenty jobs at a time and a worker already running: poll. It is simpler and needs no endpoint.
  • Hundreds of jobs, or a serverless host that cannot sleep: callback, with the handler returning 2xx fast and doing the copy in the background.
  • Anything customer-facing: callback plus a slow poll, so a missed delivery is found within minutes.

One function that does both

The function submits with a callback and a stable Idempotency-Key, then polls with a long interval until the job is terminal. If the callback arrives first, your handler can set a flag that this loop checks; here it just polls so the sketch runs alone.

import os
import time
import requests

BASE = "https://api.sume.com/v1/videos"
H = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}


def render(prompt: str, key: str, callback: str, deadline_s: int = 900) -> dict:
    body = {"model": "gemini-omni-flash-1.1", "prompt": prompt,
            "aspect_ratio": "9:16", "resolution": "720p", "duration": 5,
            "callback_url": callback}
    r = requests.post(BASE, headers={**H, "Idempotency-Key": key},
                      json=body, timeout=30)
    r.raise_for_status()
    url = r.json()["polling_url"]
    end = time.time() + deadline_s
    while time.time() < end:
        time.sleep(30)
        s = requests.get(url, headers=H, timeout=30).json()
        if s["status"] in ("completed", "failed", "cancelled"):
            return s
    raise TimeoutError(url)

Two details matter. The Idempotency-Key makes a retried submit return the original job rather than create a second paid one. And the deadline is yours: do not lengthen an HTTP timeout to wait for video, because the sync mode waits at most 30 seconds and the job continues regardless (Jobs and results, read 2026-10-06).

On the receiving end, verify the x-sume-webhook-signature over <timestamp>.<raw_body> and use job_id as your idempotency key; the delivery can repeat. Earlier posts cover the 10-second handler limit and the polling loop port in more detail.

Running both without double work

When a job has a callback and a slow poll, two paths can reach the same result. The rule is simple: make finishing a job idempotent on the job id. The first path to see a terminal state does the work, such as copying the file and updating the row, and the second sees the row already done and returns.

A single status column with a compare-and-set update is enough. Set it from running to done only if it is still running, and do the copy only if the update changed a row. That one statement also handles a repeated webhook delivery, which the docs allow for, since a delivery can be retried up to ten times.

  • Verify the signature before reading the body as trusted, and reject an empty secret at startup.
  • Return a 2xx quickly and do the download in a background task, since the delivery times out after 10 seconds.
  • Keep a slow poll for any job older than a few minutes, to catch a missed delivery.
  • Use the job_id as the key for your own deduplication.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume