Retry a timed-out POST /v1/videos with one Idempotency-Key, pay once
A Python submit wrapper that retries network errors, 429 and 5xx with a stable Idempotency-Key, so a replay returns the original job instead of a second charge.

Send the same Idempotency-Key on every retry of the same POST /v1/videos, and a replay returns the original job rather than starting and billing a second one. Build the key from something stable in your own system, such as an order id and clip number, not a fresh UUID per attempt.
This matters most on timeouts. Your client cannot tell whether the request died before or after Sume created the job, and the docs are explicit that you must not submit duplicate paid work because a local call timed out.
The wrapper
It retries connection errors, timeouts, 429 and 5xx, honors retry-after, and raises immediately on other 4xx such as 402 or 400.
import os, time, requests
H = {"Authorization": "Bearer " + os.environ["SUME_API_KEY"]}
def submit(body, key, tries=5):
for n in range(tries):
r = None
try:
r = requests.post("https://api.sume.com/v1/videos", json=body,
headers={**H, "Idempotency-Key": key}, timeout=30)
except (requests.ConnectionError, requests.Timeout):
pass
if r is not None and r.status_code != 429 and r.status_code < 500:
r.raise_for_status() # 400/402/409 are not retried
return r.json()
wait = float(r.headers.get("retry-after", 0)) if r is not None else 0
time.sleep(max(wait, 2 ** n))
raise RuntimeError("gave up; resend with the same key: " + key)
job = submit({"model": "wan-3.0", "prompt": "Rain on a window, macro",
"duration": 6, "resolution": "480p"}, "order-8823-clip-1")
print(job["id"])The key rules
- Reuse a key only for the exact same operation and payload.
- The same key with a different body returns
409 idempotency_conflict. - Do not retry
402 insufficient_credits; no key can fix a short balance. - After a
queue_fullorprovider_capacity_exceeded, retry later with the same key.
What a duplicate would have cost
One six-second Wan 3.0 clip at 480p is $0.375 on Sume. A wrapper that mints a new key per attempt and retries five times after an ambiguous timeout could, in the worst case, create five jobs and reserve $1.875 for one intended clip. A stable key keeps it at one.
Choosing the key
A good key names the intent, not the attempt. order-8823-clip-1 stays the same across retries, restarts and even a different worker picking up the same item. A random UUID generated inside the retry loop does the opposite and defeats the whole mechanism. If you do generate a UUID, create it once, persist it with the work item, and read it back on every attempt.
| Key source | Survives a crash? | Safe to retry? |
|---|---|---|
| Order id plus clip number | Yes | Yes |
| UUID stored with the work item | Yes | Yes |
| UUID created inside the retry loop | No | No, each attempt is a new job |
| Timestamp | No | No |
Remember that a key is bound to its payload. If you edit the prompt and resend under the same key, you get 409 idempotency_conflict; that is the signal to mint a new key because the operation is now a different one.
Sources
Related posts
More in Developers
- Review a 30-second Wan 3.0 clip: video inspect caps at 24 stills
Video inspect returns at most 24 stills per call, so a 30-second clip needs a sample rate of 0.8 fps or an explicit list of 24 times. Python builds the request.
- Rotate the Sume webhook secret without dropping events
After POST /v1/webhooks/signing-secret/rotate, Sume signs with both secrets for 24 hours. How verifyWebhook handles the two-entry header, and the deploy order.
- Same prompt on five Sume image models in one script: about 18 cents
One loop sends a text-in-image prompt to Flux 2 Pro, Seedream 5.0 Lite, Qwen Image, Imagen 4 Fast and Recraft V4. Expected total $0.18125. Code you can run.
- Score your own audio: a word error rate script for Sume STT
Microsoft quotes 5.2% WER on FLEURS for MAI-Transcribe-2. Measure your audio: a short Python WER function, a reference file and a cent-a-minute Sume STT run.
Written by Sume