Retry a Seedance submit safely: Idempotency-Key on /v1/videos

A timed-out POST to /v1/videos can create two paid jobs. Send an Idempotency-Key and a replay returns the original job. Python example included.

5 min readSume
All posts

Send an Idempotency-Key header on every POST /v1/videos. The Sume docs say this makes retries safe: a replay returns the original job instead of creating a second one. Without it, a request that times out on your side may still have reserved money on the server.

This matters most for automation: a workflow tool that retries on a timeout is exactly the case where a duplicate job appears.

Why does a timeout cost money?

Video submits are asynchronous, so the server may accept the job and reserve funds before your client sees the 202. Sume reserves the provider list price times 1.25 on submit, per the docs. If your retry code POSTs again with no key, you now have two jobs and two reservations.

How do I pick the key?

Derive it from your own record, not from a random value generated per attempt. A key built from the row id and the intent ("order-1042-hero-clip") stays the same across retries, which is the point. A fresh uuid4 on every attempt defeats it.

In practice a good key has three parts: what you are making, for whom, and which version. For example a catalog id, a variant number and a date. When the business intent changes, the key changes. When only the transport failed, the key stays.

Also log the key next to the job id. If a person later asks why there are two clips, the log answers the question at once.

What does a safe submit look like?

This Python submits with a stable key and retries on network errors. Pass the same key each time.

A few details in that code are deliberate. The timeout is set so a hung connection ends instead of blocking a worker. Exponential backoff spaces the attempts at one, two and four seconds. And raise_for_status turns a 4xx or 5xx into an exception, so a validation error such as a bad ratio is retried only up to the loop limit; in real code, stop immediately on a 400, because a malformed body will not become valid by waiting.

Do not retry 4xx responses other than a rate limit. Retry network errors, timeouts and 5xx.

import os, time, requests

URL = "https://api.sume.com/v1/videos"
H = {
    "Authorization": f"Bearer {os.environ['SUME_API_KEY']}",
    "Idempotency-Key": "order-1042-hero-clip",
}
body = {"model": "seedance-2.5", "prompt": "Slow push-in on a ceramic mug",
        "duration": 6, "resolution": "720p", "aspect_ratio": "16:9"}

for attempt in range(4):
    try:
        r = requests.post(URL, headers=H, json=body, timeout=30)
        r.raise_for_status()
        print(r.json()["id"])
        break
    except requests.RequestException as e:
        print("retry", attempt, e)
        time.sleep(2 ** attempt)

What are the limits of this?

The docs state the behavior in one line: send the header and a replay returns the original job. They do not document how long a key is remembered, so do not assume it lasts forever; if you change the body, use a new key. A key protects the submit only; polling is a GET and is always safe to repeat. For failure accounting see whether failed jobs cost money.

To test the behavior safely, submit a small 480p clip twice with the same key and the same body, and compare the two job ids. The docs say a replay returns the original job, so they should match. Do that once in a development workspace before you rely on it in a pipeline.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume