Batch the shots of a micro-drama episode in Python, safe to re-run
A 25 line Python script submits an episode's Seedance 2.5 shots with one Idempotency-Key per shot and a job ledger, so a crash and re-run never double-bills.

Give every shot a stable Idempotency-Key built from season, episode, shot number and a prompt version, and a re-run of your script returns the same job instead of creating a second paid one. The script below submits three 30 second Seedance 2.5 shots for one episode and writes each job id to a ledger file.
Episodic series are the shape that Metricool says TikTok's The Next Episode program is aimed at, so a repeatable batch is worth setting up.
The script
It uses plain requests, so no async code. POST /v1/videos returns the job envelope with id, polling_url and status. Set SUME_API_KEY first. Bump the v1 in the key when you intentionally change a prompt.
import os, json, requests
API = "https://api.sume.com/v1/videos"
HEAD = {"Authorization": "Bearer " + os.environ["SUME_API_KEY"]}
SHOTS = [
"Rain on a night bus, a woman reads a message and goes pale",
"She steps off the bus and sees a man waiting under a streetlight",
"He holds out her lost phone, screen lit, a call incoming",
]
def submit(ep, n, prompt):
body = {"model": "seedance-2.5", "prompt": prompt, "duration": 30,
"resolution": "720p", "aspect_ratio": "9:16"}
key = "s1-e%d-shot%d-v1" % (ep, n)
r = requests.post(API, json=body, timeout=60,
headers={**HEAD, "Idempotency-Key": key})
r.raise_for_status()
return r.json()["id"]
ledger = {}
for n, prompt in enumerate(SHOTS, 1):
ledger[n] = submit(1, n, prompt)
print("shot", n, ledger[n])
json.dump(ledger, open("ep1-ledger.json", "w"))What the keys buy you
Writes accept an Idempotency-Key so a retry of the same request is not treated as a new one. This is the habit the jobs page asks for: do not resubmit a paid request just because a local process timed out. Keep the key stable per shot and change it only when you mean to pay again.
| Situation | Key | Result |
|---|---|---|
| Script crashed, re-run | Same key | Retry of the same request |
| Prompt edited on purpose | New version suffix | New job, new bill |
| One shot redone | New version for that shot only | One new job |
Cost of the batch
Three 30 second shots at 720p and 9:16 are 90 seconds at $0.5778 per second, so $52.00. Each shot is its own job, and workspace concurrency limits apply when workers pick them up.
Next steps and limits
Poll the returned polling_url (GET /v1/videos/{id}) or GET /v1/jobs/{id}/status with backoff until a terminal status, then read the result for the video URL. For push instead of polling, pass a callback_url or see the webhooks guide. The script has no retry logic on purpose: a 4xx should stop and be read.
It does not pass first frames; add a frame_images entry per shot if you chain from a still.
Sources
Related posts
More in Developers
- Benchmark TTS latency yourself: submit to finished file in Python
Vendors quote 45 ms or 150 ms; your pipeline waits for a file. A Python harness that times Sume TTS jobs from submit to a finished state, with p50 and p95.
- Bruno collection that polls a Sume job until it is terminal
Use bru.setNextRequest and bru.sleep in a Bruno post-response script to poll a Sume job's status route with a bounded attempt count, then fetch the result.
- Bulk run: a bad item fails the whole create, a child failure does not
In a Sume bulk Format run, a bad item is a 400 with details.index and no queue; a child that fails admission after the 202 becomes one failed item.
- Bulk run worst-case spend: items x per-item cap on Sume
A Sume bulk run has no queue-level cap: each item carries its own generation_spend_cap_usd. 100 items at $120 can authorize up to $12,000. How to size it.
Written by Sume