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.

5 min readSume
All posts

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.

Per-shot request fields used above (Sume docs, read 2026-10-05)
FieldValueWhy
modelseedance-2lists first_frame and last_frame in supported_frame_images
frame_images[].frame_typefirst_framestarts the clip on your still
duration5within 4-15 s for this id
aspect_ratio9:16vertical delivery
Idempotency-Keyshot-NNa 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

All Developers posts

Written by Sume