Designing a Seedance draft pipeline: cheap exploration, gated promotion, final render
Updated 2026-10-02
Seedance is sold as several model ids that share one request shape: bytedance/seedance-2.0-mini, bytedance/seedance-2.0-fast, bytedance/seedance-2.0 and bytedance/seedance-2.5. That makes a two-stage pipeline almost free to build: explore on a lighter variant, promote only what you approve, render finals on a heavier one. The hard part is not the API call. It is deciding what counts as "approved", and making sure the pipeline cannot spend more than you intended. This page is the design and the gating code. The variant trade-offs themselves are in the variant comparison; the image-to-video angle is in this guide.
What a draft is for
A draft answers questions that do not depend on final texture: does the composition work, does the motion do what the prompt says, does the subject stay on-model, is the camera move right. It cannot answer questions about fine detail, because a promoted clip is a new generation on a different model and will not be pixel-identical. Keep the two kinds of question separate. If you review drafts for sharpness, you will reject good concepts; if you ship drafts, you will ship the wrong quality.
The stages
- Explore. N prompts x short duration x lowest sensible resolution tier on the light variant.
- Gate. Automatic checks, then human selection. Only winners continue.
- Finalise. Re-submit each winner with the same prompt and inputs on the final model, at the delivery tier and duration.
- Verify. Probe the finished file (dimensions and length) and copy it to your storage.
Cost logic, symbolically
Let N be candidate prompts, w the fraction you approve, d and D the draft and final durations, and rd and rf the per-second rates of the draft and final variants. Then:
draft_cost = N * d * r_d
final_cost = (N * w) * D * r_f
total = draft_cost + final_cost
direct_cost = N * D * r_f # no draft stage
The pipeline wins when total < direct_cost, which rearranges to: d * r_d / (D * r_f) < 1 - w. In words, the draft stage pays off when a draft is cheap relative to a final and you reject a meaningful share of candidates. If you approve nearly everything (w close to 1), drafting is overhead; if you approve one in five, it is a large saving. Read rd and rf from the live table rather than hard-coding them, because host choice moves them.
| Model | Cheapest host | Priciest host | Cheapest is | Hosts |
|---|---|---|---|---|
| bytedance/seedance-2.5 (480p) | OpenSand $0.0525 / second | Fal-US $0.2646 / second | 80% lower | 9 |
| bytedance/seedance-2.0 (2160p) | MachGen $0.59 / second | Fal $1.5552 / second | 62% lower | 9 |
| bytedance/seedance-2.0-fast (480p) | Atlas Cloud $0.027 / second | Fal $0.2419 / second | 89% lower | 9 |
| bytedance/seedance-2.0-mini (480p) | OpenSand $0.0104 / second | Fal $0.0721 / second | 86% lower | 8 |
| seedance-2-mini-unrestricted (480p) | OpenSand $0.0114 / second | SandBase $0.0721 / second | 84% lower | 3 |
| seedance-2-5-unrestricted (1080p) | OpenSand $0.3482 / second | TOAPIS $0.5881 / second | 41% lower | 2 |
| seedance-2.0-fast-unrestricted (480p) | OpenSand $0.0344 / second | SandBase $0.0448 / second | 23% lower | 2 |
| seedance-2-unrestricted (2160p) | OpenSand $0.661 / second | TOAPIS $0.7966 / second | 17% lower | 2 |
Per second, before VideoRouter's 2% platform fee. For tiered models each row compares the resolution tier with the widest host-to-host gap. Built 2026-10-02 from the live catalog.
The gating code
The pipeline below separates three concerns: a budget guard that refuses to submit past a limit, an approval callback you supply, and a promotion step that reuses the draft's exact inputs. Rates are passed in, never typed in.
from dataclasses import dataclass, field
import requests
BASE = "https://videorouter.sh/api/v1"
H = {"Authorization": "Bearer llmr_sk_live_..."}
DRAFT_MODEL = "bytedance/seedance-2.0-mini"
FINAL_MODEL = "bytedance/seedance-2.5"
class BudgetExceeded(Exception): pass
@dataclass
class Budget:
limit: float # your own ceiling, in account currency
rate: dict # {"model-id": per-second rate} from the live table
spent: float = 0.0
def charge(self, model: str, secs: float) -> None:
cost = self.rate[model] * secs
if self.spent + cost > self.limit:
raise BudgetExceeded(f"{self.spent + cost:.2f} > {self.limit:.2f}")
self.spent += cost # billed once, at creation
@dataclass
class Candidate:
id: str
prompt: str
inputs: dict = field(default_factory=dict) # e.g. {"start_image_url": ...}
draft_job: str | None = None
final_job: str | None = None
def submit(model, c: Candidate, secs, budget, **extra):
budget.charge(model, secs) # raises before any request is made
r = requests.post(f"{BASE}/videos", headers=H, timeout=30, json={
"model": model, "prompt": c.prompt, "duration_secs": secs,
**c.inputs, **extra})
r.raise_for_status()
return r.json()["id"]
def pipeline(cands, approve, budget, d=3, D=8):
for c in cands: # stage 1: explore
c.draft_job = submit(DRAFT_MODEL, c, d, budget, resolution="480p")
# ... poll every draft_job to completion (free), download, auto-check ...
winners = [c for c in cands if approve(c)] # stage 2: gate
for c in winners: # stage 3: finalise
c.final_job = submit(FINAL_MODEL, c, D, budget, resolution="1080p")
return winners
Two details are deliberate. The budget check runs before the request, because creation is the billing event; a guard that runs after the fact is only a report. And inputs travels unchanged from draft to final, so the final render sees the same start_image_url and prompt. The resolution strings above are placeholders: the 2.5 model page lists specific tier names, and an unsupported combination is ignored rather than rejected, so copy values from the page for the host you use and verify the output size.
Automatic checks before a human looks
- Dimensions and length via
ffprobe, to catch an ignored aspect ratio or a snapped duration early. - Frame diff against the source image for image-to-video drafts, to flag a subject that morphed.
- Duplicate detection: two candidates with near-identical prompts and the same inputs are one experiment.
Humans then choose from a contact sheet. Record the decision with the candidate id, because those labels tell you which prompt patterns survive and let you shrink N next round.
Failure handling inside the pipeline
Treat each submission as a ledger entry: write the candidate row before calling the API and the job id after, so a crash cannot lead to a blind re-submit. Never retry a create after a dropped connection; reconcile instead. A promoted job that fails is a fresh decision: failed jobs whose every upstream host failed are not billed, so re-submitting is cheap, but read the error first. The error side is covered in the retry guide.
When not to draft
- Single hero shot with a known-good prompt: just render the final.
- Reference-to-video with several assets: confirm the light variant accepts the same arrays on the host you use before treating it as a faithful draft. Capabilities differ per variant and host, and a draft that cannot take your references is not a draft.
- Workflows where the final's look depends on texture you cannot judge at low resolution.
Start by running ten candidates through the loop with a hard Budget, then measure your real approval rate w and recompute the break-even. Create a key with a monthly cap as a second line of defence behind your own guard.
Frequently asked questions
Is a Seedance draft pixel-identical to its final render?
No. Variants are different models, so a promoted prompt is a new generation. Use drafts to judge composition and motion, and judge detail only on finals.
When does a draft stage save money?
When a draft costs much less than a final and you reject a good share of candidates. If you approve nearly everything, drafting is mostly overhead. Measure your approval rate to decide.
Do the lighter Seedance variants accept the same inputs as 2.5?
Not necessarily. Some variants accept modes that others do not, such as start images or reference arrays, and support differs by host. Confirm on the model pages before relying on a variant as a draft.
Keep reading
- Seedance 2.5 vs 2.0 vs Fast vs Mini — Choosing a Variant for API Use
- Seedance Reference-to-Video API: Images, Video, Audio Refs
- Seedance Image-to-Video API: start_image_url Prompts & Setup
- Seedance vs Kling vs Veo API: How to Choose by Workload
VideoRouter puts it next to dozens of other video and image models behind one API key, so you can compare providers, prices and fail over automatically. Compare providers on VideoRouter →