Avatar video mode: sync waits 30 seconds, so use async or webhook
Sume's sync and subscribe modes wait at most 30 seconds. Avatar video usually takes longer. How to read the timed-out response and what to do next.
For an avatar video, send mode: "async" (the default) or mode: "webhook", not sync. A sync call on Sume blocks for at most 30 seconds, and the docs say avatar-video and face-swap jobs "routinely" do not finish inside that budget. When the budget runs out you still get a 2xx and a job id; the response then has sync.timed_out: true, and you must poll status_url. subscribe is an alias of sync: the same 30-second wait, not a stream.
The mode only decides how you find out. It never changes cost, whether a job is created or how long the render takes.
The four modes
What each returns for a talking-video submit.
| Mode | HTTP result | Blocks the server | Next step |
|---|---|---|---|
async (default) | 202 with job envelope and polling URLs | No | Poll status_url until terminal, then read result_url |
sync | Envelope after up to 30 s (wait_timeout_seconds 0 to 30) | Yes, at most 30 s | If not terminal, poll; do not resubmit |
subscribe | Identical to sync | Same | Same |
webhook | 202; callback stored | No | Verify the signature on the callback; keep polling as backup |
A polling loop that behaves
Honor next_poll_after_seconds when the response has it, and back off otherwise. Stop on completed, failed or canceled. Do not treat a local timeout as a failed job; the render may still be running and billing.
import asyncio, os
import httpx
BASE = "https://api.sume.com"
HEAD = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
async def wait(job_id: str, limit: int = 600) -> dict:
delay, waited = 5, 0
async with httpx.AsyncClient(headers=HEAD, timeout=20) as c:
while waited < limit:
r = (await c.get(f"{BASE}/v1/jobs/{job_id}/status")).json()["data"]
if r.get("terminal"):
return r
delay = r.get("next_poll_after_seconds") or min(delay * 2, 30)
await asyncio.sleep(delay)
waited += delay
raise TimeoutError(job_id)
asyncio.run(wait(os.environ["JOB_ID"]))When sync is fine
For avatar video, the rule of thumb in the docs is to submit async with an Idempotency-Key, store the four URLs, and either poll or take a webhook.
- Short image jobs that usually finish inside 30 seconds.
- Quick tests where you will poll anyway if it times out.
- A UI where you can show a spinner and then switch to polling.
Limits
There is no SSE or WebSocket transport on the Developer API today; GET /v1/jobs/:id/events is a pull snapshot. If the API process has no spare waiter capacity it skips the blocking wait entirely and sets sync.capacity_exhausted, so your code has to handle an immediate non-terminal response even when you asked for a wait. In practice that means writing the polling path first and treating a finished sync response as a shortcut. A client built that way works the same whether the render took 20 seconds, four minutes, or hit a busy moment on the API. Log the job id on the first line of your handler, before you look at anything else in the response, so a crash after submit never leaves you with a paid job you cannot find.
Sources
Related posts
More in Developers
- Bannerbear sync API 408 after 10 seconds vs Sume mode sync
Bannerbear's sync endpoint answers 408 if the render takes over 10 seconds. Sume's mode sync waits up to 30 seconds, then returns 202 to poll.
- The c2pa.ai-disclosure assertion: what fields it asks you to declare
C2PA's implementation guide describes an ai-disclosure assertion: model provenance, scientific domain and human oversight. What to prepare before you sign.
- C2PA label for a partly AI-edited video: compositedWith...
When only part of a clip is AI-generated or AI-edited, the C2PA 2.3 guidance uses a different digital source type. What it says and how to log Sume edit jobs.
- C2PA Conformance Explorer: vet your signer before promising credential
Before you promise clients C2PA Content Credentials, look your signing tool up in the C2PA Conformance Explorer. What it lists and how IPTC used it in 2026.
Written by Sume