Stable idempotency keys for pause-cut trims: hash source, start, end
Derive each trim's Idempotency-Key from a hash of source, start, end and precision. A retried batch then returns the same jobs and bills 8 trims once.

A pause-cut batch can fire dozens of $0.02 trims, and a network retry should not create a second set. Use a deterministic key: a hash of the source URL, the rounded start, the rounded end and the precision. The same range then reuses the same key and returns the same job, while a changed range gets a new key.
What the API does with the key
Every video-trim write needs an Idempotency-Key. Reusing a key with the same body returns the original job; reusing it with a different body returns 409 idempotency_conflict. That is why a threshold change must change the key, and why a random UUID created inside the retry loop defeats the purpose.
| Key strategy | Jobs created | Rate | Total |
|---|---|---|---|
| Hash of source, start, end | 8 | $0.02 | $0.16 |
| New random key per attempt | 16 | $0.02 | $0.32 |
Key function
Round times to three decimals before hashing so 1.84 and 1.8400000001 map to one key. Keep the key under typical header limits by truncating the digest.
import hashlib, json
def trim_key(source_url, start, end, precision="exact"):
raw = json.dumps([source_url, round(start, 3), round(end, 3), precision])
return "trim-" + hashlib.sha256(raw.encode()).hexdigest()[:32]
k1 = trim_key("https://media.sume.com/artifacts/artf_demo/talk.mp4", 1.84, 9.1)
k2 = trim_key("https://media.sume.com/artifacts/artf_demo/talk.mp4", 1.84, 9.1)
k3 = trim_key("https://media.sume.com/artifacts/artf_demo/talk.mp4", 1.84, 9.2)
assert k1 == k2 and k1 != k3Gotchas
- Include everything that changes the body, such as
precision,audioandoutput; leave one out and a changed request getsidempotency_conflict. - Hash the same rounded values you put in the body, or the body and key disagree.
- A key is tied to the request, not to the result, so store the job id next to the key in your own table.
Sources
Related posts
More in Developers
- Still on /v1/image-router/generate? It works but gets no new params
Sume's legacy image-router generate and models routes still work but are deprecated and get no new parameters. What to change to move to POST /v1/images.
- Stop a Seedance 2.5 batch on SumeInsufficientCreditsError
Submit a batch with createVideoGeneration and one idempotency key per item. On a 402, stop the loop, top up, and rerun the same script without paying twice.
- Store the Sume job webhook, then answer 204: SQLite insert-or-ignore
Commit the event keyed on job_id before you return 2xx, so a retry or a redeliver is harmless and a crash never loses it. Runnable Python and sqlite3 example.
- Stream a Sume video download in Python: .part file, then rename
Download /v1/videos/{id}/content with requests stream=True, write a .part file and rename on success. A lost 30 s clip costs $1.875 to $17.334 to re-buy.
Written by Sume