Turn a 16-shot list into 16 Sume video jobs in Python
Submit one /v1/videos job per shot with a first frame, run them concurrently with asyncio, and give each shot an Idempotency-Key so a rerun never bills twice.

To turn a 16-shot list into video on Sume, submit one POST /v1/videos per shot with the shot's still as first_frame, run the submissions together with asyncio, and give each shot a stable Idempotency-Key. A rerun then returns the original job for every shot you already sent.
This is the Sume version of a 16-keyframe plan: sixteen independent clips, joined later. It is not one 16-keyframe generation like the one Luma lists for Ray 3.2, which Sume does not run.
The code
It uses httpx. The shot URLs are placeholders; use public HTTPS images. seedance-2 takes first_frame per its catalog entry, and the durations list starts at 4 seconds.
import asyncio, os
import httpx
H = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
SHOTS = [(f"shot-{i:02d}", f"https://example.com/s{i:02d}.png", "slow push-in, soft light") for i in range(1, 17)]
async def submit(c, key, img, prompt):
body = {"model": "seedance-2", "prompt": prompt, "duration": 5, "resolution": "720p",
"aspect_ratio": "9:16",
"frame_images": [{"type": "image_url", "image_url": {"url": img},
"frame_type": "first_frame"}]}
r = await c.post("https://api.sume.com/v1/videos", json=body,
headers={**H, "Idempotency-Key": key})
r.raise_for_status()
return key, r.json()["id"]
async def main():
async with httpx.AsyncClient(timeout=30) as c:
done = await asyncio.gather(*(submit(c, *s) for s in SHOTS))
for key, job in done:
print(key, job)
asyncio.run(main())Why the keys matter
The Video generation docs list Idempotency-Key as a Sume addition: send it to make retries safe, and a replay returns the original job. Keys like shot-07 tie a key to a shot, so a crash and rerun resubmits nothing twice. If you change the prompt of a shot, change its key too, otherwise you get the old job back.
Sume reserves the provider list times 1.25 at submit for each job, so 16 jobs reserve 16 amounts at once. Check your balance before you fire a large list, and keep an eye on workspace concurrency.
Poll or use a callback
Each submit returns a job id and a polling_url. Poll GET /v1/videos/{jobId} until the status is completed, then download from unsigned_urls[0]. The docs suggest a moderate interval of about 30 seconds. Or send an HTTPS callback_url; Sume signs the raw JSON body with x-sume-webhook-timestamp and x-sume-webhook-signature headers.
| Field | Value | Why |
|---|---|---|
| model | seedance-2 | lists first_frame and last_frame in supported_frame_images |
| frame_images[].frame_type | first_frame | starts the clip on your still |
| duration | 5 | within 4-15 s for this id |
| aspect_ratio | 9:16 | vertical delivery |
| Idempotency-Key | shot-NN | a rerun returns the original job |
Then assemble
Download each clip, import it to media.sume.com, and join the 16 in Timeline. With more than 12 slots the render chunks automatically, and no more than 8 fades may sit side by side. The next post shows the join in Python.
Practical guardrails
Sixteen submits at once is fine for a demo and may exceed your workspace's concurrency in production. Add a semaphore (asyncio.Semaphore(4)) around submit if you see queueing. A job that is pending is only waiting in the queue, so poll it rather than resubmitting.
Keep the shot manifest in a file with the key, the still URL, the prompt, and later the job id, so the next script can poll and download without guessing.
Spend arithmetic
Each shot reserves its own list x 1.25 amount, so the batch reserve is 16 times one job. If a single 5-second 720p job reserves R dollars, the batch reserves 16R. Look up R in pricing_skus for your model, multiply, and compare to your balance before you submit.
A failed job is a failed job, not a partial success: check the failed status and the error field per shot and resubmit only that shot with a new key.
Downloading the results
After each job reaches completed, fetch GET /v1/videos/{jobId}/content?index=0 with your key, or use the first entry of unsigned_urls. Save each file under its shot key, then import it to your workspace so Timeline can use it. Keep the polling interval moderate; the docs suggest 30 seconds because video jobs usually take from 30 seconds to several minutes.
Sources
Related posts
More in Developers
- Brand name mispronounced by the AI voice? Four fixes and the cost
When a TTS voice says your brand name wrong, change the voice, the language, the spelling or the dictionary. Each retake on Sume costs 1 to 7 cents.
- Ack a Sume webhook in 10 s, then copy the 30-second MP4 later
Sume gives each delivery 10 seconds. Verify, store the job id, answer 204, and let a worker download the MP4 instead of doing it inside the handler.
- Ad run returns 400 invalid_attachment: count your 30 files first
A Format run carries at most 30 files, split 30 images, 10 videos, 10 audio. A Python counter for attachments plus media URLs inside input, before you call.
- Agency Black Friday: a team-owned Format needs a workspace key
A personal API key cannot run a Format that a team workspace owns. Sume answers 403 workspace_key_required, so issue the key inside the team before launch.
Written by Sume