Generate AI video with text, images, and reference footage. One Rust binary. No MCP server required.
Pay-per-second video generation from ByteDance, wrapped in a tool agents can actually drive. JSON on pipe, human-readable on TTY, semantic exit codes, one static binary.
Install · API Key Setup · Quick Start · Reference Inputs · Companion Tools · Known Quirks · Commands
Seedance 2.0 produces strong 480p and 720p output at a low per-second price. The ModelArk API that serves it wasn't built for scripts — auth is awkward, responses are verbose, and there is no single step from prompt to mp4 on disk. This CLI gives you that step, and keeps the output shape stable whether you're piping into jq or watching a progress bar.
# Cargo (works everywhere Rust does)
cargo install seedance
# Homebrew (macOS + Linux)
brew install 199-biotechnologies/tap/seedance
# Prebuilt binary
# https://github.com/paperfoot/seedance-cli/releases/latestBytePlus is ByteDance's official international cloud. The model platform is ModelArk. Three minutes, start to finish:
- Go to console.byteplus.com/ark and sign in (free account, credit card needed for paid generation).
- Open the API Keys page from the left sidebar.
- Click Create API Key — copy the
ark-…string it shows you (it won't show again). - Hand it to the CLI in one of three ways, pick whatever fits:
# (a) Environment variable — best for CI
export SEEDANCE_API_KEY=ark-xxxxxxxx
# ARK_API_KEY is also accepted
# (b) Config file — stored locally at chmod 600, never echoed back
seedance config set api-key ark-xxxxxxxx
# (c) Per-command flag — overrides everything else
seedance generate --prompt "…" --api-key ark-xxxxxxxxVerify:
seedance doctorIf anything's wrong, doctor tells you which step to revisit.
seedance generate --prompt "A cat yawns at the camera" --wait
# -> ~/Documents/seedance/20260420T023015Z-abc12345.mp4
# -> ~/Documents/seedance/20260420T023015Z-abc12345.seedance.json # sidecar manifestDefault output is ~/Documents/seedance/. Override with -o /path/to/file.mp4 or -o /some/dir/, or use --label / --project to drive the default naming.
seedance generate --project cafe-scene --label alice-walk \
--image ~/Documents/seedance/cafe-scene/alice-sheet.png \
--prompt "..." --wait
# -> ~/Documents/seedance/cafe-scene/20260420T023015Z-alice-walk-abc12345.mp4
# -> ~/Documents/seedance/cafe-scene/20260420T023015Z-alice-walk-abc12345.seedance.jsonSidecars written by generate --wait carry the full request — prompt, references, model, seed, duration, task id, timestamps — with "source": "generate". Sidecars written by the bare seedance download command carry task/download metadata only (the BytePlus GetTask response doesn't echo the original request back) with "source": "download". Agents should key on .source before trusting either:
jq 'select(.source == "generate") | {file: .downloaded_to, label, prompt}' \
~/Documents/seedance/**/*.seedance.json prompt + refs ModelArk queue mp4
| | ^
v v |
seedance gen -> task id returned -> seedance download
|
v
seedance status <id>
One task id from start to finish. Fire and forget, or pass --wait to block until the file lands on disk.
Seedance 2.0 accepts a free mix of references in a single content array:
| Flag | Kind | Limit | Notes |
|---|---|---|---|
--image / -i |
Image | 0–9 | Path (base64'd inline) or URL. Role: reference_image |
--first-frame |
Image | 1 | Role: first_frame |
--last-frame |
Image | 1 | Role: last_frame (requires --first-frame) |
--video / -v |
Video | 0–3 | URL only (API restriction). Total ≤ 15s |
--audio / -a |
Audio | 0–3 | wav/mp3. Path (base64'd) or URL. Total ≤ 15s. Needs an image or video alongside. |
Address references inside the prompt:
[Image 1] the boy waves; [Video 1] camera style; [Audio 1] background music
Use time codes for multi-shot: [0-4s]: wide establishing shot; [4-8s]: push in.
Supported resolutions: 480p, 720p. Supported ratios: 16:9, 4:3, 1:1, 3:4, 9:16, 21:9, adaptive. Duration: 4–15s or -1 for auto.
# Text-to-video, wait and download
seedance generate \
--prompt "A kitten yawns and blinks at the camera, cozy warm light" \
--duration 6 --resolution 720p --ratio 16:9 \
--wait --output kitten.mp4
# Multimodal reference-to-video with the fast tier
seedance generate \
--prompt "[Image 1] the boy smiles; [Image 2] the corgi jumps in; [Video 1] camera motion" \
--image boy.png --image corgi.png \
--video https://my-cdn.example/style.mp4 \
--fast --wait -o out.mp4
# Fire and forget, poll later
TASK=$(seedance gen --prompt "..." | jq -r '.data.id')
seedance status "$TASK"
seedance download "$TASK" -o final.mp4Four helpers ship in the same binary, built to work around specific Seedance limits. Each one chains cleanly into generate.
| Tool | What it does |
|---|---|
seedance character-sheet --character N |
Builds a 9-angle (or 4-angle) sheet from a single photo via Nano Banana Pro. Pass the resulting PNG to --image to keep one person consistent across shots. For multi-character scenes, run this once per person with distinct --character names and pass every sheet as a separate --image. |
seedance audio-to-video |
Wraps an audio file in a silent mp4 (ffmpeg under the hood) so you can pass it as --video instead of --audio. Preserves lyrics exactly. --upload hosts the file on tmpfiles.org and prints the URL. |
seedance prep-face |
Applies an empirically verified grain + desaturation recipe so a real portrait clears ModelArk's face filter. --bw swaps to the pure grayscale variant. |
seedance upload <file> |
Uploads a local file to tmpfiles.org and prints a direct-download URL, ready to paste into --video. |
Cap scenes at two characters. Three or more in the same shot breaks identity lock — Seedance starts morphing faces across frames. For three-person scenes, split into two shots and intercut in post.
# One sheet per character, shared project folder
seedance character-sheet alice.jpg --character alice --project cafe-scene
seedance character-sheet bob.jpg --character bob --project cafe-scene
# Generate with both sheets; each gets an explicit job in the prompt
seedance generate --project cafe-scene --label cafe-opening \
--image ~/Documents/seedance/cafe-scene/alice-sheet.png \
--image ~/Documents/seedance/cafe-scene/bob-sheet.png \
--prompt "[Image 1] is Alice's 9-angle reference. Her face and hair match [Image 1] \
exactly. [Image 2] is Bob's 9-angle reference. His face and beard match \
[Image 2] exactly. [0-4s]: wide, Alice and Bob at a corner table in a \
sunlit Parisian cafe. [4-14s]: medium, Alice picks up her espresso cup." \
--duration 14 --ratio 16:9 --waitSeedance reads prompts literally. Do not rely on logic or genre shorthand — describe everything you need to see.
- Every reference must have an explicit job in the prompt. Unnamed refs are silently dropped.
- One verb per shot. Split multi-action scenes with time codes:
[0-4s]: ...; [4-9s]: .... - Negative prompts do not work. Phrase positively: "eyes natural, soft eye contact, blinks occasionally" beats "no weird eyes".
- Early tokens dominate. Put camera language, subject, and style in the first half of the prompt.
- Prompt length: 30–200 words. Too short underspecifies; too long causes detail dropout.
- Describe wardrobe, lighting, and setting concretely even when it's visible in a reference — these drift more than faces do.
For deeper prompt recipes (UGC vs marketing vs cinematic templates, chaining scenes into longer films, voice cloning workflows), see the seedance-prompting skill.
Hard-won notes from real users, saved so you don't hit the same walls:
- Audio upload mutates lyrics. Reported by @MrDavids1 and @simeonnz: uploading audio directly alters the song. Workaround —
seedance audio-to-video song.mp3 --upload, then pass the printed URL as--video. The API trusts reference videos for audio content but post-processes raw audio. - No real human faces in references. ModelArk blocks direct upload of real human portraits. Either run the photo through
seedance prep-facefirst, or build acharacter-sheetfrom one photo and use that. - Videos must be URLs. Host the file first (S3, Cloudinary, or
seedance upload→ tmpfiles.org) and pass the URL. Local files are rejected by the API. - No 1080p. Seedance 2.0 tops out at 720p. Use
--resolution 720p—1080preturns an error.
| Command | Purpose |
|---|---|
seedance generate / gen |
Create a video generation task |
seedance status <id> / get |
Poll a task |
seedance download <id> |
Download the mp4 for a completed task |
seedance cancel <id> / rm |
Cancel a queued task |
seedance character-sheet <photo> |
Build a consistent-character reference grid |
seedance audio-to-video <file> |
Wrap audio in silent mp4 (lyrics workaround) |
seedance prep-face <photo> |
Prepare a portrait to pass the face filter |
seedance upload <file> |
Host a local file and print a public URL |
seedance models / ls |
List available model ids |
seedance doctor |
Check API key, base URL, and deps |
seedance agent-info / info |
Machine-readable capability manifest |
seedance skill install |
Deploy SKILL.md to Claude, Codex, or Gemini |
seedance config show / path / set / unset |
Manage the TOML config |
seedance update [--check] |
Self-update from GitHub Releases |
Global flags: --json (force JSON), --quiet (suppress info), --help, --version.
| Code | Meaning |
|---|---|
0 |
Success |
1 |
Transient (network, API, IO, update) — retry |
2 |
Config (missing API key, bad base URL) — fix setup |
3 |
Invalid input — fix arguments |
4 |
Rate limited — wait and retry |
Every error also prints a machine-readable error_code and a literal recovery suggestion when you're in --json mode.
- agent-cli-framework — the scaffolding every paperfoot CLI is built on.
PRs and issues welcome. See CONTRIBUTING.md for the short version.
MIT — see LICENSE.