Public Model / seedance

Seedance 2 API pricing

Estimate your video's cost, generate it through the API, and download the result. Explore current rates, a video demo, and a complete Python example.

Availability
Available
Tasks
Create Video
Published rates from
$0.0575
Press play and turn on sound.

Seedance 2 · video demo

From orbit to a field-side selfie

A rapid dive through clouds and patchwork fields lands on a handheld selfie in golden wheat. The prompt gives each scene its own timing, camera direction, and dialogue.

Video length
5 seconds
Frame size
720 × 1280
Format
9:16 · audio

The original prompt

"'SCENE 1: Earth from space, camera rapidly zooming in continuously — punching through white clouds, green landscape appears, patchwork fields getting closer, golden wheat field rushing up, camera keeps diving toward a single person standing in the middle of the field — a voiceover says "did you know that most people have no idea about this" — 2 seconds. SCENE 2: Seamless transition landing on a young guy in a white t-shirt standing in the wheat field, waist-high golden stalks around him, wind blowing his hair, he's already mid-sentence looking directly at a front-facing phone camera held at arm's length saying "let me explain why this changes everything", wide eyes, leaning into the camera, gesturing with his free hand. Bright afternoon sunlight, blue sky, raw selfie camera quality — 3 seconds. "

Try this prompt through the API →

Request calculator

Configure Seedance 2

Controls and rates come directly from the checked public catalog.

Interactive calculator

Price a direct generation

Choose a task, configure its inputs, and see the current catalog price.

Step 1

Choose a generation type

Step 2

Configure generation

Model
Seedance 2
Output duration8s
4s15s
Resolution720p
Reference videoNone
Generate audioOnAudio generation does not change the current price.
Outputs1

Step 3

Reference total

$1.641 output at $1.64 each
Generation
$1.648 seconds x $0.205
Minimum request
$0.345
Get started

The API estimate for your exact request is authoritative before paid work starts.

Worked costs

What would your request cost?

Estimate a new text-to-video request with audio and no reference media. These catalog examples are rounded to the nearest cent.

Short draft

$0.38

4 seconds · 480p · one output

Five-second vertical video

$1.03

5 seconds · 720p · one output

Higher-resolution video

$2.55

5 seconds · 1080p · one output

Duration and resolution change the price. Reference-video requests use a different rate and include total reference-video length in billable duration. The API estimate for your exact inputs is authoritative.

Python quickstart

From prompt to a downloaded video.

Use the original prompt in a new five-second, 720p Seedance 2 request with audio. The script estimates the cost, creates a generation, waits for it to finish, and saves the video. Each generation can produce a different result.

  1. 1. Get API accessCreate a Console workspace, add prepaid balance, and create an organization API key. Generation requires enough available balance for the current estimate.
  2. 2. Check the estimateDownload seedance-2.py and run it with uv. The default command only estimates the request and needs no API key.
  3. 3. Generate when readySet ENTIREFEED_API_KEY, pass a unique idempotency key, and confirm the displayed estimate. Keep that key to recover the same request after a network error.
# Requires uv: https://docs.astral.sh/uv/getting-started/installation/
# Free estimate; no API key required
uv run seedance-2.py

# Paid generation after confirmation
export ENTIREFEED_API_KEY="your-organization-api-key"
uv run seedance-2.py --generate --idempotency-key earth-selfie-001

# Resume an existing generation without creating another
uv run seedance-2.py --resume gen_YOUR_GENERATION_ID
View the complete Python script
# /// script
# requires-python = ">=3.11"
# dependencies = ["requests>=2.32,<3"]
# ///
"""Default: free estimate only. Paid creation requires --generate and confirmation."""
import argparse
import json
import os
import re
import time
from pathlib import Path
from urllib.parse import urljoin, urlparse

import requests

API_BASE = "https://api.entirefeed.com/v1"

PAYLOAD = json.loads("{\"task\":\"video.create\",\"model\":\"seedance-2\",\"input\":{\"prompt\":\"\\\"'SCENE 1: Earth from space, camera rapidly zooming in continuously — punching through white clouds, green landscape appears, patchwork fields getting closer, golden wheat field rushing up, camera keeps diving toward a single person standing in the middle of the field — a voiceover says \\\"did you know that most people have no idea about this\\\" — 2 seconds. SCENE 2: Seamless transition landing on a young guy in a white t-shirt standing in the wheat field, waist-high golden stalks around him, wind blowing his hair, he's already mid-sentence looking directly at a front-facing phone camera held at arm's length saying \\\"let me explain why this changes everything\\\", wide eyes, leaning into the camera, gesturing with his free hand. Bright afternoon sunlight, blue sky, raw selfie camera quality — 3 seconds. \\\"\",\"duration_seconds\":5,\"resolution\":\"720p\",\"aspect_ratio\":\"9:16\",\"generate_audio\":true}}")

def api(method, path, *, key=None, **kwargs):
    # Never forward the API key to a media download or another origin.
    url = urljoin(API_BASE + "/", path)
    if urlparse(url).netloc != urlparse(API_BASE).netloc or urlparse(url).scheme != "https":
        raise ValueError("Unexpected API URL")
    headers = kwargs.pop("headers", {})
    if key:
        headers["Authorization"] = f"Bearer {key}"
    response = requests.request(method, url, headers=headers, timeout=60, **kwargs)
    response.raise_for_status()
    return response.json()


def main():
    parser = argparse.ArgumentParser(description="Estimate, generate, and download a Seedance 2 video.")
    action = parser.add_mutually_exclusive_group()
    action.add_argument("--generate", action="store_true", help="Enable a paid request after confirmation.")
    parser.add_argument("--idempotency-key", help="Reuse this key to recover the same intended request.")
    action.add_argument("--resume", metavar="GENERATION_ID", help="Read an existing generation; creates no new work.")
    parser.add_argument("--output", type=Path, default=Path("seedance-demo.mp4"))
    args = parser.parse_args()
    key = os.environ.get("ENTIREFEED_API_KEY")

    if (args.generate or args.resume) and args.output.exists():
        raise SystemExit(f"{args.output} already exists. Choose another --output path.")

    if args.resume:
        if not key:
            raise SystemExit("Set ENTIREFEED_API_KEY to resume.")
        if not re.fullmatch(r"gen_[a-zA-Z0-9]+", args.resume):
            raise SystemExit("Expected a gen_... generation ID.")
        current = api("GET", f"generations/{args.resume}", key=key)
        status_url = f"{API_BASE}/generations/{args.resume}"
    else:
        estimate = api("POST", "estimate", json=PAYLOAD)
        print("Current estimate:", json.dumps(estimate["estimated_cost"], indent=2))
        if not args.generate:
            print("Estimate only: no media generated and no balance spent.")
            print("To generate, add --generate --idempotency-key YOUR_UNIQUE_REQUEST_KEY.")
            return
        if not key or not args.idempotency_key:
            raise SystemExit("Set ENTIREFEED_API_KEY and pass --idempotency-key before generating.")
        print("Creating reserves balance using the current price, which can change after this estimate.")
        if input("Type GENERATE to create this paid request: ").strip() != "GENERATE":
            print("No generation created.")
            return
        print(f"Request key: {args.idempotency_key}. Keep it to recover this request after a network error.")
        current = api(
            "POST", "generations", key=key, json=PAYLOAD,
            headers={"Idempotency-Key": args.idempotency_key},
        )
        status_url = current["links"]["self"]

    generation_id = current["id"]
    print(f"Generation: {generation_id}")
    print(f"Resume with: uv run seedance-2.py --resume {generation_id}")
    deadline = time.monotonic() + 900
    # Always fetch the complete record, even if create returned an immediate success.
    while True:
        current = api("GET", status_url, key=key)
        status = current["status"]
        print("Status:", status)
        if status not in {"queued", "running"}:
            break
        if time.monotonic() >= deadline:
            raise SystemExit(f"Still active. Resume {generation_id}; do not create another request.")
        time.sleep(5)

    if current["status"] != "succeeded":
        print(json.dumps(current.get("error"), indent=2))
        print("Usage:", json.dumps(current.get("usage"), indent=2))
        raise SystemExit(f"Stopped at {current['status']}. See Console for details.")

    assets = (current.get("output") or {}).get("assets", [])
    video = next((asset for asset in assets if asset.get("type") == "video"), None)
    if not video:
        raise SystemExit("Succeeded, but no video asset was returned.")
    if urlparse(video["url"]).scheme != "https":
        raise SystemExit("Unexpected download URL.")
    # A separate request keeps the API credential off the asset host.
    partial = args.output.with_name(args.output.name + ".part")
    # Only remove a partial file created by this attempt if the download fails.
    output = partial.open("xb")
    try:
        with output:
            with requests.get(video["url"], stream=True, timeout=120) as response:
                response.raise_for_status()
                for chunk in response.iter_content(chunk_size=1024 * 1024):
                    output.write(chunk)
    except (requests.RequestException, OSError):
        partial.unlink(missing_ok=True)
        raise
    partial.rename(args.output)
    print("Saved:", args.output)
    print("Usage:", json.dumps(current.get("usage"), indent=2))


if __name__ == "__main__":
    try:
        main()
    except requests.RequestException as error:
        raise SystemExit(
            f"Request failed: {error}. Resume the generation ID if available; "
            "otherwise reuse the same idempotency key and unchanged payload."
        ) from error

The script stops on failed, cancelled, or manual-review status. A deliberate retry is new paid work. Read the retry guide before creating another generation.

Before your first request

Can I check the price before creating an account?

Yes. POST /v1/estimate is public and requires no API key. It creates no media and spends no balance. Creating media requires an API key and sufficient prepaid balance.

How long does generation take?

Read the generation status while the request is queued or running. The script polls until it finishes and prints a resume command if it takes longer than its 15-minute waiting window. That window is a script setting, not a completion-time promise.

What happens if a generation fails or I retry it?

Failure or cancellation does not necessarily mean no billable work occurred. Check the returned usage and Console details. Reusing an idempotency key recovers the same intended request; a deliberate retry creates a new generation and balance reservation.

Can I add image or video references?

Seedance 2 accepts reference images, videos, audio, and first/last frames through its request controls. Estimate again after changing the inputs; reference-video duration affects the cost.

Where do I get the finished video?

A successful generation returns output.assets. The script downloads the video asset to seedance-demo.mp4. Asset URLs can expire, so save the finished file to your own storage.

Get API access →

Best fit

Use Seedance 2 when a production needs the widest Seedance resolution range or a reference-led clip with explicit duration pricing.

Use cases

  • 4K product sequences
  • Reference-video recreation

Know before building

  • Reference-video requests bill against output duration plus total reference-video length.

Generated contract

Tasks, controls, and rates

Published rates

video.create
Varies by output duration, resolution, total reference video length
$0.0575 / second$0.345 minimum

Request controls

video.createAspect ratio16:9 · 9:16 · 4:3 · 3:4 · 1:1 · 21:9 · adaptive
video.createDuration seconds4 · 5 · 6 · 7 · 8 · 9 · 10 · 11 · 12 · 13 · 14 · 15
video.createFirst frameMedia or structured input
video.createGenerate audioDefault: true
video.createLast frameMedia or structured input
video.createPromptText input
video.createReference audioUp to 3 items
video.createReference imagesUp to 9 items
video.createReference videosUp to 3 items
video.createResolution480p · 720p · 1080p · 4K

Lifecycle: queued -> running -> succeeded -> failed -> cancelled -> manual_review

Output fields: assets