Seedance 2.5 in 16:9, 9:16 and 1:1: one idempotency key per ratio
Fan one prompt out to three aspect ratios on /v1/videos, derive a separate Idempotency-Key for each ratio, and rerun the script without paying twice.

To render one Seedance 2.5 prompt in several aspect ratios, send one POST /v1/videos per ratio and build each Idempotency-Key from a base id plus the ratio. A rerun of the same script then returns the original jobs, because a replay with the same key and body returns the first job instead of starting a second one.
Check the ratios against the model's catalog entry first, since each model reports its own supported_aspect_ratios.
Why one key per ratio?
One key means one operation. If you reuse a single key across three different bodies, the second call fails with 409 idempotency_conflict, and if you mint a random key per attempt, a crash-and-rerun bills the clip again. A derived key such as launch-42-9x16 fixes both.
| Situation | Result |
|---|---|
| Same key, same body | The original job comes back |
| Same key, different body | 409 idempotency_conflict |
| New key, same body | A new paid job |
| No key | Not replay-safe |
What does the script look like?
The call uses the default async path, so the script only prints the polling URLs.
import os, requests
BASE = "https://api.sume.com/v1"
H = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
want = ["16:9", "9:16", "1:1"]
models = requests.get(f"{BASE}/videos/models", headers=H, timeout=30).json()["data"]
m = next(x for x in models if x["id"] == "seedance-2.5")
ratios = [x for x in want if x in m["supported_aspect_ratios"]]
for ratio in ratios:
key = f"{os.environ['CAMPAIGN']}-{ratio.replace(':', 'x')}"
r = requests.post(f"{BASE}/videos", timeout=30,
headers={**H, "Idempotency-Key": key},
json={"model": "seedance-2.5", "duration": 8,
"resolution": "720p", "aspect_ratio": ratio,
"prompt": "A ceramic mug on a desk, slow push-in"})
print(ratio, r.status_code, r.json().get("polling_url"))How do I confirm the three jobs afterwards?
Print the ratio next to the polling URL, as the script does, and keep that output. Rerun the script: the same URLs coming back for the same ratios proves the replay worked, and a new URL means a new paid job. Map ratio to job by that printed label, not by list position.
What changes the key's meaning?
Any edit to the body, even the prompt text, makes the old key a conflict. Bake a version into the key (for example launch-42-v2-9x16) when you change the prompt on purpose, and keep the old key for the old spend.
Will three jobs run at once?
Only up to your plan's concurrency. The rest wait as queued, which is normal, and a full queue returns 429 queue_full. Check the headroom before a larger fan-out.
Sources
Related posts
More in Developers
- Seedance 2.5 is token-priced: 30 s is $8.06, $17.33 or $42.65
The catalog shows per-1000-video-tokens for Seedance 2.5, so seconds times a rate fails. Totals for 480p, 720p and 1080p at 30 s with the ratios between them.
- Send a signed webhook.test with POST /v1/webhooks/test-deliveries
Point POST /v1/webhooks/test-deliveries at a new HTTPS endpoint to get a signed webhook.test before any video job runs, then check the sume-v1 signature.
- Seven pre-flight checks before an Omni, H3 or Recast job
Duration, resolution, ratio, edit conflicts, reference counts, refused fields and URLs: seven per-model checks that catch a refused Sume video request.
- SGLang H3 /v1/videos vs Sume /v1/videos: same path, new fields
A self-hosted MiniMax H3 SGLang server and Sume both expose /v1/videos, but the fields differ: seconds vs duration, seed accepted vs not listed. Small adapter.
Written by Sume