Re-render 3 of 17 rejected clips: what the retries add on Seedance 2

Seventeen 8-second Seedance 2 clips cost $51.408; re-rolling 3 rejected clips and then 1 more adds $12.096. Failed jobs are released, rejected takes are billed.

4 min readSume
All posts

A batch of 17 Seedance 2 clips at 720p, 9:16, 8 seconds costs $51.408 ($3.024 each). Re-rendering 3 that the reviewer rejects adds $9.072, and one more round on a single clip adds $3.024, for $63.504 across 21 renders.

The key distinction is failed versus rejected. A job that fails or is canceled before capture is released back to your balance; a job that completes and gets thrown away in review has been captured and is spent.

The arithmetic

Each render is $3.024 from the price tool (Seedance 2, 720p, 8 s, 9:16). Total = renders x $3.024.

17 clips, 3 rejected, 1 rejected again (read 2026-10-09)
StepRendersPer renderSubtotal
First pass: 17 clips17$3.024$51.408
Retry 3 rejected clips3$3.024$9.072
Retry 1 that is still off1$3.024$3.024
Total (21 renders)21$3.024$63.504

Plan the retry reserve

Budget a retry fraction instead of the exact count. Three rejected out of 17 is about 18 percent; a 20 percent retry reserve on the first pass is $10.2816, which covers the 3 retries ($9.072) with room for the second round. If your reviewer rejects more than that, the fix is the prompt, not the budget.

Keep failures and re-rolls apart in code

Re-rolling a rejected clip is a new take, so use a new Idempotency-Key with a take number. Retrying a request that timed out on your side is the same request, so reuse its key and the original job comes back instead of a second charge.

import os, requests

H = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}

def submit(clip, take, prompt):
    body = {"model": "seedance-2", "prompt": prompt, "resolution": "720p",
            "duration": 8, "aspect_ratio": "9:16"}
    h = {**H, "Idempotency-Key": f"batch17-clip{clip:02d}-take{take}"}
    r = requests.post("https://api.sume.com/v1/videos", headers=h, json=body, timeout=60)
    r.raise_for_status()
    return r.json()["id"]

rejected = [4, 9, 15]
jobs = [submit(c, 2, "Product on a desk, handheld, warm light") for c in rejected]
print(jobs)

Read what you actually spent

Do not add up your own estimates after the fact. The usage endpoint folds the ledger for you: add job_id, run_id or thread_id to GET /v1/usage and quote the debited_usd in the summary, which counts captured rows only. Reserved holds and refunds show up in separate fields so a retry that was released does not inflate the number.

For a 17-clip batch where you tag each job in metadata, pull the ledger once at the end of the day and compare it with the table above. The difference between the two is your real rejection rate in dollars.

curl "https://api.sume.com/v1/usage?job_id=job_01HXYZ&limit=50" \
  -H "Authorization: Bearer $SUME_API_KEY"

Gotchas

  • Cancel works only before generation starts; after that the job completes or fails normally.
  • Seedance 2 does not accept a seed, so a re-roll is a different take, not a repeat.
  • A 402 insufficient_credits on a retry means the reserve for that clip no longer fits the balance; add funds in the dashboard and resubmit with the same key.

Sources

Related posts

More in Use cases

All Use cases posts

Written by Sume