Sync mode returned queued on a 30-second video: the 30 s cap
mode sync waits at most 30 seconds on the HTTP call, not on the job. A 30-second clip returns a job id with sync.timed_out true; keep polling, never resubmit.

The 30 seconds in mode: "sync" is how long the submit request may block, not how long the job may run. A 30-second Seedance 2.5 or Wan 3.0 clip will not finish in that window, so you get a normal 2xx envelope with the job id and sync.timed_out: true, and you keep polling.
The word clash is easy to miss: 30 s of output length and 30 s of wait budget are unrelated limits that happen to share a number.
What the response tells you
wait_timeout_seconds is clamped to 0-30. When the budget ends, or when the API process has no waiter capacity left, the response is still a success and still carries status_url, result_url, events_url, and cancel_url. sync.timed_out means the wait returned before a terminal state. sync.capacity_exhausted means Sume skipped the wait because its waiter budget was full.
subscribe is an alias of sync with the same cap. It is not a stream, and there is no SSE or WebSocket transport on the Developer API.
| Mode | Blocks | Right for a 30 s clip? |
|---|---|---|
| async (default) | No | Yes, poll status_url |
| webhook | No | Yes, with a poll as backup |
| sync | At most 30 seconds | No |
| subscribe | At most 30 seconds | No, same as sync |
Branch on the envelope
Read terminal first. If it is false, hand the job id to your poller and return. If you retry the submit itself after a network failure, send the same Idempotency-Key.
import os, requests
H = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}",
"Idempotency-Key": "sync-demo-001"}
r = requests.post("https://api.sume.com/v1/video-router/generate", headers=H,
timeout=45, json={"model": "wan-3.0", "prompt": "Steam over a bowl of ramen",
"duration": 30, "resolution": "480p",
"mode": "sync", "wait_timeout_seconds": 30})
r.raise_for_status()
d = r.json()["data"]
if d["job"]["status"] == "completed":
print("done in the wait", d["result_url"])
else:
print("keep polling", d["status_url"], (d.get("sync") or {}).get("timed_out"))Use async for video
Sume recommends async or webhook for new integrations. Keep sync for short image work that usually fits the wait. A gateway or function with its own shorter timeout makes this worse, so check the limit of every hop between you and the API.
Sources
Related posts
More in Developers
- 10 hooks by 10 endings: a 100-variant grid in one Sume bulk queue
A 10 by 10 hook and ending grid is exactly 100 items, the bulk queue maximum. How to build the items array, pick concurrency up to 16, and read the result.
- A ten-photo edit regression suite to re-run when a model launches
Ten photos, five edits each, one scoring sheet: a cheap test to re-run whenever a new image edit model launches. 50 edits cost $1.875 at the low tier on Sume.
- How many video scenes fit in one Sume script_run? Ten
script_run allows at most 32 paid calls per run. At three paid calls per scene (voice, image, clip) that is ten scenes, with two calls to spare.
- Test a Sume webhook receiver with signed fixtures, no paid job needed
Generate sume-v1 signatures yourself and test six cases: good, rotated, reserialized, stale, empty-secret and unknown event. Python code that runs as is.
Written by Sume