Seedance 2.5 sync mode: a 30-second wait, then poll

mode sync on the Video Router blocks at most 30 seconds. A Seedance 2.5 job can outlast it, so read sync.timed_out and poll status_url without resubmitting.

5 min readSume
All posts

With mode: "sync" on the Video Router, a Seedance 2.5 submit waits at most 30 seconds (wait_timeout_seconds is clamped to 0 to 30); if the job is not finished, you still get a normal response with sync.timed_out: true, and you must poll status_url rather than send the request again. Sume's docs say video jobs "routinely" outlast the wait, and a 4 to 30 second Seedance clip is a video job.

Everything here is from Sume's Jobs and results page and the Video Router entry in the API reference. The model's range, 4 to 30 seconds per generation, is in Video Router and ByteDance Seed's launch post (read 2026-10-02).

What does a timed-out sync response contain?

A 2xx with the job id and URLs. The wait running out is not an error and does not cancel or refund anything.

What a sync submit returns when the wait ends first, from Sume's Jobs and results docs (read 2026-10-02).
FieldMeaningWhat you do
sync.timed_out: trueWait ended before a terminal statePoll status_url
sync.capacity_exhausted: trueSume skipped the wait; the waiter budget was fullPoll status_url
status_url, result_url, events_url, cancel_urlWhere to follow the jobStore the job id
next_poll_after_secondsSuggested delay before the next pollHonour it when present

Should I use sync for Seedance at all?

The docs steer new integrations to async or webhook: sync and subscribe "are just the wrong tool for anything that can outlast 30 seconds, which is most video work." subscribe is an alias of sync, with the same 30-second bound. If you do use sync for a short 4-second draft, still write the polling branch.

What is the safe polling loop?

Submit with an Idempotency-Key, read the envelope, and poll until terminal is true. A retry of the submit with the same key returns the original job; a new key buys a second render.

import asyncio, os, httpx

BODY = {"model": "seedance-2.5", "prompt": "A paper boat crossing a puddle",
        "duration": 8, "mode": "sync", "wait_timeout_seconds": 30}

async def main():
    key = os.environ["SUME_API_KEY"]
    h = {"Authorization": f"Bearer {key}", "Idempotency-Key": "boat-001"}
    async with httpx.AsyncClient(timeout=60) as c:
        r = await c.post("https://api.sume.com/v1/video-router/generate", headers=h, json=BODY)
        r.raise_for_status()
        d = r.json()["data"]
        while not d["terminal"]:
            await asyncio.sleep(d.get("next_poll_after_seconds") or 5)
            s = await c.get(d["status_url"], headers=h)
            s.raise_for_status()
            body = s.json()
            d = body.get("data", body)
        print(d.get("status"), d.get("result_ready"))

asyncio.run(main())

What if my client gives up first?

A client-side timeout does not cancel the job; it keeps running and still bills. Keep the job id and resume from status_url, or cancel through cancel_url while cancelable is true. For long renders a webhook is terminal-only (job.completed, job.failed, job.canceled), so keep polling as a backup.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume