ModelsPricingEnterprise
500+ AI Model API, All In One API.Just In CometAPI
Models API
Developer
Quick StartDocumentationAPI Dashboard
Company
About usEnterprise
Resources
AI ModelsBlogChangelogSupport
Terms of ServicePrivacy Policy
© 2026 CometAPI · All rights reserved
Home/Models/Doubao/Doubao-Seedance-2-0
D

Doubao-Seedance-2-0

Per Second:$0.08
Seedance 2.0 is ByteDance’s next-generation multimodal video foundation model focused on cinematic, multi-shot narrative video generation. Unlike single-shot text-to-video demos, Seedance 2.0 emphasizes reference-based control (images, short clips, audio), coherent character/style consistency across shots, and native audio/video synchronization — aiming to make AI video useful for professional creative and previsualization workflows.
New
Commercial Use
Overview
Features
Pricing
API
Versions

Technical specifications of Doubao Seedance 2.0

ItemDetails
Product familyByteDance Seedance video generation family
CometAPI slugdoubao-seedance-2-0
ProviderByteDance / BytePlus ModelArk
Model typeVideo generation model
Input modalitiesText, image, audio, video
Reference capacityUp to 9 images, 3 video clips, and 3 audio clips in a single multimodal request
Output typeMP4 video
Output resolution480p, 720p, 1080p
Aspect ratios21:9, 16:9, 4:3, 1:1, 3:4, 9:16
Output duration4–15 seconds
API styleAsynchronous task-based video generation

What is Doubao Seedance 2.0?

Doubao Seedance 2.0 is ByteDance’s flagship multimodal video generation model (released February 2026). It uses a unified audio-video joint generation architecture that allows precise director-level control by referencing multiple images, video clips, and audio files simultaneously. It excels at producing cinematic, motion-stable videos with native audio synchronization — ideal for professional creators needing realistic physics, consistent characters, and complex scene composition.

Main features of Doubao Seedance 2.0 on CometAPI

  • Unified multimodal generation: It accepts text, image, audio, and video inputs in one workflow, so users can direct a scene with richer reference material than a text-only generator.
  • Director-style control: The model is built to follow prompts for performance, lighting, shadow, camera movement, motion rhythm, and sound characteristics.
  • Video editing and extension: Seedance 2.0 is not limited to first-pass generation; it supports editing specific clips, characters, actions, and storylines, plus forward/backward extension.
  • Strong audiovisual sync: ByteDance highlights dual-channel stereo output and synchronized background music, ambient sound, and voiceover behavior for more immersive results.
  • High controllability for complex scenes: It emphasize improved handling of complex interactions, motion scenes, and physical plausibility compared with Seedance 1.5.
  • Production-oriented output: API supports configurable ratio, duration, and watermark settings, which makes the model practical for repeatable content workflows.

Benchmark performance

ByteDance says Seedance 2.0 uses the internal SeedVideoBench-2.0 evaluation and places in the leading position across text-to-video, image-to-video, and multimodal task dimensions. The model improves generation quality, physical accuracy, realism, and controllability versus Seedance 1.5, especially in complex interaction and motion scenes.

Seedance 2.0 API and Seedance 2.0 Fast API Modes on CometAPI

ModelModel Name in CometAPIBest forMain difference
Seedance 2.0doubao-seedance-2-0Highest-quality multimodal video creationBroadest reference stack and strongest controllability in the Seedance 2.0 family.
Seedance 2.0 fastdoubao-seedance-2-0-fastFaster production when quality can be traded a bitByteDance says it keeps the same model capabilities as Seedance 2.0, but generates faster.
Seedance 1.5 Prodoubao-seedance-1-5-proEarlier-generation audio-video creationSeedance 1.5 Pro is positioned as a native joint audio-video model, while Seedance 2.0 expands the multimodal reference and editing stack.

Strengths vs Competitors:

  • vs Kling 3.0: Better multimodal reference control and native audio.
  • vs Sora 2: Superior reference precision and multi-shot capability; slightly lower max duration.
  • vs Veo 3.1: Stronger in character consistency and prompt adherence for reference-heavy workflows; Veo leads in raw cinematic grading and longer clips.

Excels in human-centric and performance-driven videos.

Try Seedance 2.0 AI Video Generator on CometAPI

Step 1: Step 1: Sign Up for API Key

Start directly in the CometAPI Playground to experience Doubao Seedance 2.0 without any setup or coding. Simply log into your CometAPI account, go to the model page for doubao-seedance-2-0, upload your reference images, short video clips, or audio files, add a descriptive prompt, and generate preview videos instantly. This is the fastest way to understand how Seedance 2.0 handles motion consistency, character appearance, camera movements, and native audio synchronization.

Step 2: Get API Access for Seedance 2.0 on CometAPI

Create or use your existing CometAPI key to enable full access to Doubao Seedance 2.0. After logging in, navigate to the API Token section in your console, generate a new key, and copy it. Then visit the Seedance 2.0 model detail page and API doc on CometAPI to review the supported parameters integrating into your projects.

Step 3: Send Your First Request with Seedance 2.0 API

Use the CometAPI endpoint to submit your first video generation request with a clear text prompt and optional reference files (images, video, or audio). The system will process the task asynchronously, return a task ID, and provide a downloadable MP4 video once completed. You can then refine your prompts, adjust settings, and scale up for marketing videos, social media content, automated video pipelines, or creative applications powered by Seedance 2.0.

This workflow gives you quick experimentation in the playground and smooth transition into production use via the CometAPI.

Top Reasons to Choose Seedance 2.0 on CometAPI

Why Use CometAPI for Seedance 2.0

  • Direct access via API or Playground
  • Easy parameter control (duration, resolution, format)
  • Supports both text-to-video and image-to-video workflows
  • Built-in job handling for asynchronous video generation

Unified & Developer-Friendly API

CometAPI offers a clean, standardized endpoint that works seamlessly with familiar OpenAI-style formats or dedicated video task endpoints. You get straightforward task creation, polling, and MP4 downloads without dealing with Volcengine’s complex authentication or regional limitations.

Cost-Effective Pricing

CometAPI typically delivers more competitive per-second rates compared to direct providers, allowing you to generate high-quality videos at scale for marketing, social media, or automated pipelines without breaking your budget.

Fast Testing with Playground

Experiment immediately in the CometAPI Playground. Upload reference images, videos, and audio files, tweak prompts, and preview results in minutes — perfect for iterating on style, motion, and audio sync before going into production.

In short, if you want the creative power of Seedance 2.0 — best-in-class reference control, natural motion, and native audio — without the hassle of direct ByteDance access, CometAPI is currently one of the best platforms to use it.

FAQ

What kinds of inputs does Seedance 2.0 support for video generation?

Seedance 2.0 supports multimodal inputs including text prompts, up to 9 images, up to 3 short video clips, and up to 3 audio files, which can be freely combined for rich, controllable generation.

Can Seedance 2.0 maintain character and style consistency across multiple video shots?

Yes — Seedance 2.0 is designed for coherent multi-shot storytelling with consistent characters, visual style, and atmosphere across scenes, reducing common AI video drift issues.

What outputs and quality levels can I expect from Seedance 2.0 videos?

Seedance 2.0 can generate cinematic-grade videos (up to 2K resolution) with native audio, synchronized dialogue, and natural motion synthesis, typically in clips of 5–60 seconds.

How does Seedance 2.0 handle audio and lip synchronization?

The model generates audio and video jointly, offering native audio-visual sync with phoneme-level lip sync in 8+ languages for natural speech and sound effects.

Is Seedance 2.0 suitable for professional creative projects like marketing or narrative shorts?

Yes — Seedance 2.0’s multimodal control, multi-shot continuity, and high fidelity output make it suitable for marketing videos, narrative shorts, ads, and other professional applications.

How do referencing assets (images, video clips) work in Seedance 2.0 prompts?

Users can upload reference assets and then describe in natural language how each should influence motion, camera movement, or stylistic elements, giving fine-grained control over the generated content.

Does Seedance 2.0 allow editing and extension of existing videos?

Yes — the model supports video extension and targeted editing like adding scenes, replacing characters, or altering specific segments while preserving unedited portions.

What are known limitations or typical generation lengths with Seedance 2.0?

Typical output lengths range from ~5 to ~60 seconds per video, and combining many assets or high-resolution settings can increase generation time.

Features for Doubao-Seedance-2-0

Explore the key features of Doubao-Seedance-2-0, designed to enhance performance and usability. Discover how these capabilities can benefit your projects and improve user experience.

Pricing for Doubao-Seedance-2-0

Explore competitive pricing for Doubao-Seedance-2-0, designed to fit various budgets and usage needs. Our flexible plans ensure you only pay for what you use, making it easy to scale as your requirements grow. Discover how Doubao-Seedance-2-0 can enhance your projects while keeping costs manageable.

doubao-seedance Video Generation Pricing

Parameters

ParameterDescription
Duration (seconds)4–15 seconds, default 5 seconds
Aspect Ratio (size)21:9 / 16:9 / 4:3 / 1:1 / 3:4 / 9:16, default 16:9
Resolution480p / 720p / 1080p*, default 720p

*1080p only available for doubao-seedance-1-5-pro and doubao-seedance-1-0-pro

Pricing (Per Second)

Model480p720p1080p
doubao-seedance-2-0$0.08$0.24—
doubao-seedance-2-0-fast$0.064$0.192—
doubao-seedance-1-5-pro$0.018$0.04147$0.09331
doubao-seedance-1-0-pro$0.01875$0.0432$0.0972

💡 Billed per second. Total cost = price per second × video duration (seconds). Duration range: 4–15 seconds.

Sample code and API for Doubao-Seedance-2-0

Access comprehensive sample code and API resources for Doubao-Seedance-2-0 to streamline your integration process. Our detailed documentation provides step-by-step guidance, helping you leverage the full potential of Doubao-Seedance-2-0 in your projects.
POST
/v1/videos
Python
JavaScript
Curl
import json
import os
import time

import requests

# Get your CometAPI key from https://www.cometapi.com/console/token, and paste it here
COMETAPI_KEY = os.environ.get("COMETAPI_KEY") or "<YOUR_COMETAPI_KEY>"
BASE_URL = "https://api.cometapi.com"
OUTPUT_DIR = "./output"
POLL_INTERVAL_SECONDS = 10
RETRY_DELAY_SECONDS = 5
MAX_CREATE_ATTEMPTS = 5
MAX_QUERY_ATTEMPTS = 3
TERMINAL_STATUSES = {"success", "completed", "failed", "error"}
SUCCESS_STATUSES = {"success", "completed"}

def is_progress_complete(progress):
    if isinstance(progress, int):
        return progress >= 100
    if isinstance(progress, float):
        return progress >= 100
    if isinstance(progress, str):
        try:
            return float(progress.rstrip("%")) >= 100
        except ValueError:
            return False
    return False

def is_transient_status(status_code):
    return status_code == 429 or 500 <= status_code < 600

def create_task(files):
    for attempt in range(1, MAX_CREATE_ATTEMPTS + 1):
        response = requests.post(
            f"{BASE_URL}/v1/videos",
            headers=headers,
            files=files,
            timeout=30,
        )
        if response.ok:
            return response
        if not is_transient_status(response.status_code) or attempt == MAX_CREATE_ATTEMPTS:
            response.raise_for_status()
        print(f"Create request returned {response.status_code}, retrying...")
        time.sleep(RETRY_DELAY_SECONDS)

    raise SystemExit("Failed to create task.")

def get_task(task_id):
    for attempt in range(1, MAX_QUERY_ATTEMPTS + 1):
        response = requests.get(
            f"{BASE_URL}/v1/videos/{task_id}",
            headers=headers,
            timeout=15,
        )
        if response.ok:
            return response
        if not is_transient_status(response.status_code) or attempt == MAX_QUERY_ATTEMPTS:
            response.raise_for_status()
        print(f"Status request returned {response.status_code}, retrying...")
        time.sleep(RETRY_DELAY_SECONDS)

    raise SystemExit("Failed to query task.")

if COMETAPI_KEY == "<YOUR_COMETAPI_KEY>":
    print("Set COMETAPI_KEY before running this example.")
    raise SystemExit(0)

headers = {"Authorization": f"Bearer {COMETAPI_KEY}"}

create_response = create_task(
    {
        "prompt": (None, "A slow cinematic camera push across a coastal landscape at sunrise."),
        "model": (None, "doubao-seedance-2-0"),
        "seconds": (None, "5"),
        "size": (None, "16:9"),
        "resolution": (None, "720p"),
    }
)
create_response.raise_for_status()
create_result = create_response.json()

task_id = create_result.get("id") or create_result.get("task_id")
if not task_id:
    print(json.dumps(create_result, indent=2))
    raise SystemExit("No task id returned.")

print(f"Task created: {task_id}")
print(f"Initial status: {create_result.get('status')}")

while True:
    task_response = get_task(task_id)
    task_response.raise_for_status()
    task = task_response.json()
    status = str(task.get("status") or "unknown")
    normalized_status = status.lower()
    progress = task.get("progress")
    should_try_download = normalized_status in SUCCESS_STATUSES or (
        normalized_status == "unknown" and is_progress_complete(progress)
    )

    print(f"Status: {status}, progress: {progress}")

    if should_try_download or normalized_status in TERMINAL_STATUSES:
        if should_try_download:
            video_url = task.get("video_url") or ""
            content_url = f"{BASE_URL}/v1/videos/{task_id}/content"
            output_path = os.path.join(OUTPUT_DIR, f"{task_id}.mp4")

            os.makedirs(OUTPUT_DIR, exist_ok=True)
            with requests.get(
                content_url,
                headers=headers,
                timeout=120,
                stream=True,
            ) as video_response:
                video_response.raise_for_status()
                with open(output_path, "wb") as output_file:
                    for chunk in video_response.iter_content(chunk_size=8192):
                        if chunk:
                            output_file.write(chunk)

            print(f"Video URL: {video_url}")
            print(f"Content endpoint: {content_url}")
            print(f"Saved to {output_path}")
            print(f"File size: {os.path.getsize(output_path)} bytes")
        else:
            print(json.dumps(task, indent=2))
            raise SystemExit(1)
        break

    time.sleep(POLL_INTERVAL_SECONDS)

Python Code Example

import json
import os
import time

import requests

# Get your CometAPI key from https://www.cometapi.com/console/token, and paste it here
COMETAPI_KEY = os.environ.get("COMETAPI_KEY") or "<YOUR_COMETAPI_KEY>"
BASE_URL = "https://api.cometapi.com"
OUTPUT_DIR = "./output"
POLL_INTERVAL_SECONDS = 10
RETRY_DELAY_SECONDS = 5
MAX_CREATE_ATTEMPTS = 5
MAX_QUERY_ATTEMPTS = 3
TERMINAL_STATUSES = {"success", "completed", "failed", "error"}
SUCCESS_STATUSES = {"success", "completed"}


def is_progress_complete(progress):
    if isinstance(progress, int):
        return progress >= 100
    if isinstance(progress, float):
        return progress >= 100
    if isinstance(progress, str):
        try:
            return float(progress.rstrip("%")) >= 100
        except ValueError:
            return False
    return False


def is_transient_status(status_code):
    return status_code == 429 or 500 <= status_code < 600


def create_task(files):
    for attempt in range(1, MAX_CREATE_ATTEMPTS + 1):
        response = requests.post(
            f"{BASE_URL}/v1/videos",
            headers=headers,
            files=files,
            timeout=30,
        )
        if response.ok:
            return response
        if not is_transient_status(response.status_code) or attempt == MAX_CREATE_ATTEMPTS:
            response.raise_for_status()
        print(f"Create request returned {response.status_code}, retrying...")
        time.sleep(RETRY_DELAY_SECONDS)

    raise SystemExit("Failed to create task.")


def get_task(task_id):
    for attempt in range(1, MAX_QUERY_ATTEMPTS + 1):
        response = requests.get(
            f"{BASE_URL}/v1/videos/{task_id}",
            headers=headers,
            timeout=15,
        )
        if response.ok:
            return response
        if not is_transient_status(response.status_code) or attempt == MAX_QUERY_ATTEMPTS:
            response.raise_for_status()
        print(f"Status request returned {response.status_code}, retrying...")
        time.sleep(RETRY_DELAY_SECONDS)

    raise SystemExit("Failed to query task.")

if COMETAPI_KEY == "<YOUR_COMETAPI_KEY>":
    print("Set COMETAPI_KEY before running this example.")
    raise SystemExit(0)

headers = {"Authorization": f"Bearer {COMETAPI_KEY}"}

create_response = create_task(
    {
        "prompt": (None, "A slow cinematic camera push across a coastal landscape at sunrise."),
        "model": (None, "doubao-seedance-2-0"),
        "seconds": (None, "5"),
        "size": (None, "16:9"),
        "resolution": (None, "720p"),
    }
)
create_response.raise_for_status()
create_result = create_response.json()

task_id = create_result.get("id") or create_result.get("task_id")
if not task_id:
    print(json.dumps(create_result, indent=2))
    raise SystemExit("No task id returned.")

print(f"Task created: {task_id}")
print(f"Initial status: {create_result.get('status')}")

while True:
    task_response = get_task(task_id)
    task_response.raise_for_status()
    task = task_response.json()
    status = str(task.get("status") or "unknown")
    normalized_status = status.lower()
    progress = task.get("progress")
    should_try_download = normalized_status in SUCCESS_STATUSES or (
        normalized_status == "unknown" and is_progress_complete(progress)
    )

    print(f"Status: {status}, progress: {progress}")

    if should_try_download or normalized_status in TERMINAL_STATUSES:
        if should_try_download:
            video_url = task.get("video_url") or ""
            content_url = f"{BASE_URL}/v1/videos/{task_id}/content"
            output_path = os.path.join(OUTPUT_DIR, f"{task_id}.mp4")

            os.makedirs(OUTPUT_DIR, exist_ok=True)
            with requests.get(
                content_url,
                headers=headers,
                timeout=120,
                stream=True,
            ) as video_response:
                video_response.raise_for_status()
                with open(output_path, "wb") as output_file:
                    for chunk in video_response.iter_content(chunk_size=8192):
                        if chunk:
                            output_file.write(chunk)

            print(f"Video URL: {video_url}")
            print(f"Content endpoint: {content_url}")
            print(f"Saved to {output_path}")
            print(f"File size: {os.path.getsize(output_path)} bytes")
        else:
            print(json.dumps(task, indent=2))
            raise SystemExit(1)
        break

    time.sleep(POLL_INTERVAL_SECONDS)

JavaScript Code Example

import fs from "fs";
import path from "path";

// Get your CometAPI key from https://www.cometapi.com/console/token, and paste it here
const api_key = process.env.COMETAPI_KEY || "<YOUR_COMETAPI_KEY>";
const base_url = "https://api.cometapi.com";
const output_dir = "./output";
const poll_interval_ms = 10_000;
const retry_delay_ms = 5_000;
const max_create_attempts = 5;
const max_query_attempts = 3;
const terminal_statuses = new Set(["success", "completed", "failed", "error"]);
const success_statuses = new Set(["success", "completed"]);

function is_progress_complete(progress) {
  if (typeof progress === "number") {
    return progress >= 100;
  }

  if (typeof progress === "string") {
    const numeric = Number(progress.replace(/%$/, ""));
    return Number.isFinite(numeric) && numeric >= 100;
  }

  return false;
}

function is_transient_status(status) {
  return status === 429 || status >= 500;
}

async function fetch_with_retry(url, options, attempts, label) {
  for (let attempt = 1; attempt <= attempts; attempt += 1) {
    const response = await fetch(url, options);
    if (response.ok) {
      return response;
    }

    if (!is_transient_status(response.status) || attempt === attempts) {
      return response;
    }

    console.log(`${label} returned ${response.status}, retrying...`);
    await new Promise((resolve) => setTimeout(resolve, retry_delay_ms));
  }

  throw new Error(`${label} failed`);
}

if (api_key === "<YOUR_COMETAPI_KEY>") {
  console.log("Set COMETAPI_KEY before running this example.");
  process.exit(0);
}

const headers = {
  Authorization: `Bearer ${api_key}`,
};

const form = new FormData();
form.set("prompt", "A slow cinematic camera push across a coastal landscape at sunrise.");
form.set("model", "doubao-seedance-2-0");
form.set("seconds", "5");
form.set("size", "16:9");
form.set("resolution", "720p");

const create_response = await fetch_with_retry(`${base_url}/v1/videos`, {
  method: "POST",
  headers,
  body: form,
}, max_create_attempts, "Create request");

if (!create_response.ok) {
  console.log(await create_response.text());
  process.exit(1);
}

const create_result = await create_response.json();
const task_id = create_result.id || create_result.task_id;

if (!task_id) {
  console.log(JSON.stringify(create_result, null, 2));
  process.exit(1);
}

console.log(`Task created: ${task_id}`);
console.log(`Initial status: ${create_result.status}`);

while (true) {
  const task_response = await fetch_with_retry(`${base_url}/v1/videos/${task_id}`, {
    headers,
  }, max_query_attempts, "Status request");

  if (!task_response.ok) {
    console.log(await task_response.text());
    process.exit(1);
  }

  const task = await task_response.json();
  const status = String(task.status || "unknown");
  const normalized_status = status.toLowerCase();
  const progress = task.progress;
  const should_try_download = success_statuses.has(normalized_status) || (
    normalized_status === "unknown" && is_progress_complete(progress)
  );

  console.log(`Status: ${status}, progress: ${progress}`);

  if (should_try_download || terminal_statuses.has(normalized_status)) {
    if (should_try_download) {
      const video_url = task.video_url || "";
      const content_url = `${base_url}/v1/videos/${task_id}/content`;
      const output_path = path.join(output_dir, `${task_id}.mp4`);

      if (!fs.existsSync(output_dir)) {
        fs.mkdirSync(output_dir, { recursive: true });
      }

      const video_response = await fetch(content_url, { headers });
      if (!video_response.ok) {
        console.log(await video_response.text());
        process.exit(1);
      }

      const video_buffer = Buffer.from(await video_response.arrayBuffer());
      fs.writeFileSync(output_path, video_buffer);

      console.log(`Video URL: ${video_url}`);
      console.log(`Content endpoint: ${content_url}`);
      console.log(`Saved to ${output_path}`);
      console.log(`File size: ${fs.statSync(output_path).size} bytes`);
    } else {
      console.log(JSON.stringify(task, null, 2));
      process.exit(1);
    }
    break;
  }

  await new Promise((resolve) => setTimeout(resolve, poll_interval_ms));
}

Curl Code Example

#!/bin/bash

set -euo pipefail

# Get your CometAPI key from https://www.cometapi.com/console/token
# Export it as: export COMETAPI_KEY="your-key-here"

if [[ -z "${COMETAPI_KEY:-}" ]]; then
  echo "Set COMETAPI_KEY before running this example."
  exit 0
fi

BASE_URL="https://api.cometapi.com"
OUTPUT_DIR="./output"
POLL_INTERVAL_SECONDS=10
RETRY_DELAY_SECONDS=5
MAX_CREATE_ATTEMPTS=5
MAX_QUERY_ATTEMPTS=3

is_progress_complete() {
  local progress="$1"
  local normalized="${progress%%%}"

  if [[ -z "$normalized" ]]; then
    return 1
  fi

  [[ "$normalized" =~ ^[0-9]+([.][0-9]+)?$ ]] || return 1
  awk -v value="$normalized" 'BEGIN { exit !(value >= 100) }'
}

create_task() {
  local attempt=1
  while (( attempt <= MAX_CREATE_ATTEMPTS )); do
    local response
    local status_code
    response=$(curl -sS -w $'\n%{http_code}' "${BASE_URL}/v1/videos" \
      -H "Authorization: Bearer $COMETAPI_KEY" \
      -F 'prompt="A slow cinematic camera push across a coastal landscape at sunrise."' \
      -F 'model="doubao-seedance-2-0"' \
      -F 'seconds="5"' \
      -F 'size="16:9"' \
      -F 'resolution="720p"')

    status_code=$(echo "$response" | tail -n 1)
    CREATE_RESPONSE=$(echo "$response" | sed '$d')

    if [[ "$status_code" =~ ^2 ]]; then
      return 0
    fi

    if [[ "$status_code" == "429" || "$status_code" =~ ^5 ]] && (( attempt < MAX_CREATE_ATTEMPTS )); then
      echo "Create request returned ${status_code}, retrying..."
      sleep "$RETRY_DELAY_SECONDS"
      (( attempt += 1 ))
      continue
    fi

    echo "$CREATE_RESPONSE"
    return 1
  done
}

get_task() {
  local task_id="$1"
  local attempt=1
  while (( attempt <= MAX_QUERY_ATTEMPTS )); do
    local response
    local status_code
    response=$(curl -sS -w $'\n%{http_code}' "${BASE_URL}/v1/videos/${task_id}" \
      -H "Authorization: Bearer $COMETAPI_KEY")

    status_code=$(echo "$response" | tail -n 1)
    TASK_RESPONSE=$(echo "$response" | sed '$d')

    if [[ "$status_code" =~ ^2 ]]; then
      return 0
    fi

    if [[ "$status_code" == "429" || "$status_code" =~ ^5 ]] && (( attempt < MAX_QUERY_ATTEMPTS )); then
      echo "Status request returned ${status_code}, retrying..."
      sleep "$RETRY_DELAY_SECONDS"
      (( attempt += 1 ))
      continue
    fi

    echo "$TASK_RESPONSE"
    return 1
  done
}

create_task

TASK_ID=$(echo "$CREATE_RESPONSE" | jq -r '.id // .task_id // empty')

if [[ -z "$TASK_ID" ]]; then
  echo "$CREATE_RESPONSE" | jq .
  echo "No task id returned."
  exit 1
fi

echo "Task created: $TASK_ID"
echo "Initial status: $(echo "$CREATE_RESPONSE" | jq -r '.status // empty')"

while true; do
  get_task "$TASK_ID"

  STATUS=$(echo "$TASK_RESPONSE" | jq -r '.status // empty')
  NORMALIZED_STATUS=$(echo "$STATUS" | tr '[:upper:]' '[:lower:]')
  PROGRESS=$(echo "$TASK_RESPONSE" | jq -r '.progress // empty')
  SHOULD_TRY_DOWNLOAD=0

  if [[ "$NORMALIZED_STATUS" == "success" || "$NORMALIZED_STATUS" == "completed" ]]; then
    SHOULD_TRY_DOWNLOAD=1
  elif [[ "$NORMALIZED_STATUS" == "unknown" ]] && is_progress_complete "$PROGRESS"; then
    SHOULD_TRY_DOWNLOAD=1
  fi

  echo "Status: ${STATUS}, progress: ${PROGRESS}"

  if [[ "$SHOULD_TRY_DOWNLOAD" == "1" || "$NORMALIZED_STATUS" == "failed" || "$NORMALIZED_STATUS" == "error" ]]; then
    if [[ "$SHOULD_TRY_DOWNLOAD" == "1" ]]; then
      VIDEO_URL=$(echo "$TASK_RESPONSE" | jq -r '.video_url // empty')
      CONTENT_URL="${BASE_URL}/v1/videos/${TASK_ID}/content"
      OUTPUT_PATH="${OUTPUT_DIR}/${TASK_ID}.mp4"

      mkdir -p "$OUTPUT_DIR"
      curl -fsS "$CONTENT_URL" \
        -H "Authorization: Bearer $COMETAPI_KEY" \
        -o "$OUTPUT_PATH"

      if [[ ! -s "$OUTPUT_PATH" ]]; then
        echo "Failed to download video"
        exit 1
      fi

      echo "Video URL: ${VIDEO_URL}"
      echo "Content endpoint: ${CONTENT_URL}"
      echo "Saved to ${OUTPUT_PATH}"
      echo "File size: $(wc -c < "$OUTPUT_PATH" | tr -d ' ') bytes"
    else
      echo "$TASK_RESPONSE" | jq .
      exit 1
    fi
    break
  fi

  sleep "$POLL_INTERVAL_SECONDS"
done

Versions of Doubao-Seedance-2-0

The reason Doubao-Seedance-2-0 has multiple snapshots may include potential factors such as variations in output after updates requiring older snapshots for consistency, providing developers a transition period for adaptation and migration, and different snapshots corresponding to global or regional endpoints to optimize user experience. For detailed differences between versions, please refer to the official documentation.
version
doubao-seedance-2-0
doubao-seedance-2-0-fast

More Models

O

Sora 2 Pro

Per Second:$0.24
Sora 2 Pro is our most advanced and powerful media generation model, capable of generating videos with synchronized Audio. It can create detailed, dynamic video clips from natural language or images.
O

Sora 2

Per Second:$0.08
Super powerful video generation model, with sound effects, supports chat format.
M

mj_fast_video

Per Request:$0.6
Midjourney video generation
X

Grok Imagine Video

Per Second:$0.04
Generate videos from text prompts, animate still images, or edit existing videos with natural language. The API supports configurable duration, aspect ratio, and resolution for generated videos — with the SDK handling the asynchronous polling automatically.
G

Veo 3.1 Pro

Per Second:$0.25
Veo 3.1-Pro refers to the high-capability access/configuration of Google’s Veo 3.1 family — a generation of short-form, audio-enabled video models that add richer native audio, improved narrative/editing controls and scene-extension tools.
G

Veo 3.1

Per Second:$0.05
Veo 3.1 is Google’s incremental-but-significant update to its Veo text-and-image→video family, adding richer native audio, longer and more controllable video outputs, and finer editing and scene-level controls.

Related Blog

How to Use Seedance 2.0 API
Apr 17, 2026

How to Use Seedance 2.0 API

Seedance 2.0 API is ByteDance’s latest multimodal AI video generation model (launched April 9, 2026). It accepts text, images, video clips, and audio in a single request to produce cinematic 4–15 second MP4 videos with native audio sync, director-level camera control, and exceptional motion consistency. To use it: sign up on a CometAPI.com, obtain an API key, submit an async task via REST, poll for completion, and download the video URL.
What is HappyHorse-1.0? How to Compare Seedance 2.0?
Apr 11, 2026
seedance-2-0

What is HappyHorse-1.0? How to Compare Seedance 2.0?

Learn what HappyHorse-1.0 is, why it hit the top of the Artificial Analysis video leaderboard, how it compares with Seedance 2.0, and what the latest rankings mean for AI video generation.
What is Seedance 2.0? A Comprehensive Analysis
Mar 24, 2026
seedance-2-0

What is Seedance 2.0? A Comprehensive Analysis

Seedance 2.0 is a next-generation multimodal AI video generation model developed by ByteDance that can generate high-quality, cinematic videos from text, images, audio, and reference videos. It features audio-video joint generation, motion stability, and reference-based editing, and has rapidly climbed global benchmarks like the Artificial Analysis leaderboard, positioning itself among the top AI video models in 2026.