模型定價企業
500+ AI 模型 API,全部整合在一個 API 中。就在 CometAPI
模型 API
開發者
快速入門說明文件API 儀表板
公司
關於我們企業
資源
AI模型部落格更新日誌支援
服務條款隱私政策
© 2026 CometAPI · All rights reserved
Home/Models/Doubao/Doubao-Seedance-2-0
D

Doubao-Seedance-2-0

每秒:$0.08
Seedance 2.0 是 ByteDance 的新一代多模態影片基礎模型,聚焦於電影級、多鏡頭敘事的影片生成。有別於單鏡頭的文本轉影片示範,Seedance 2.0 著重於基於參考的控制(圖像、短片、音訊)、跨鏡頭的角色與風格一致性,以及原生的聲畫同步——旨在使 AI 影片可用於專業創作與預視化工作流程。
新
商業用途
概覽
功能
定價
API
版本

Doubao Seedance 2.0 技術規格

項目詳細資訊
產品系列ByteDance Seedance 影片生成系列
CometAPI slugdoubao-seedance-2-0
提供方ByteDance / BytePlus ModelArk
模型類型影片生成模型
輸入模態文字、圖片、音訊、影片
參考容量在單次多模態請求中最多 9 張圖片、3 段影片與 3 段音訊
輸出類型MP4 影片
輸出解析度480p、720p、1080p
長寬比21:9、16:9、4:3、1:1、3:4、9:16
輸出時長4–15 秒
API 風格非同步任務式影片生成

什麼是 Doubao Seedance 2.0?

Doubao Seedance 2.0 是 ByteDance 的旗艦級多模態影片生成模型(於 2026 年 2 月發布)。它採用統一的聲畫聯合生成架構,可同時參考多張圖片、影片片段與音訊檔案,實現精準的導演級控制。它擅長生成具電影感、運動穩定,且具原生音訊同步的影片—對需要真實物理表現、角色一致性與複雜場景調度的專業創作者而言尤其適合。

CometAPI 上 Doubao Seedance 2.0 的主要功能

  • 統一的多模態生成: 在單一流程中接受文字、圖片、音訊與影片輸入,使使用者能以遠勝僅文字生成器的豐富參考素材來指導場景。
  • 導演式控制: 模型可依提示對表演、光線、陰影、鏡頭運動、動作節奏與聲音特徵做出相應。
  • 影片剪輯與延展: Seedance 2.0 不僅限於首次生成;支援對特定片段、角色、動作與劇情線進行編輯,並可向前/向後延展。
  • 強大的聲畫同步: ByteDance 強調雙聲道立體聲輸出,並能使背景音樂、環境音與旁白行為同步,以獲得更沉浸的效果。
  • 對複雜場景的高可控性: 與 Seedance 1.5 相比,對複雜互動、運動場景與物理合理性的處理有明顯提升。
  • 面向生產的輸出: API 支援可配置的比例、時長與浮水印設定,使其適用於可重複的內容工作流程。

基準表現

ByteDance 表示,Seedance 2.0 採用內部的 SeedVideoBench-2.0 評測,並在文字轉影片、圖片轉影片與多模態任務等維度名列前茅。與 Seedance 1.5 相比,該模型在生成品質、物理準確性、真實感與可控性方面均有提升,尤其在複雜互動與運動場景中表現突出。

CometAPI 上的 Seedance 2.0 API 與 Seedance 2.0 Fast API 模式

模型CometAPI 中的模型名稱最佳用於主要差異
Seedance 2.0doubao-seedance-2-0最高品質的多模態影片創作在 Seedance 2.0 系列中擁有最廣的參考堆疊與最強的可控性。
Seedance 2.0 fastdoubao-seedance-2-0-fast在可略微讓渡品質時追求更快的產出ByteDance 稱其保有與 Seedance 2.0 相同的模型能力,但生成速度更快。
Seedance 1.5 Prodoubao-seedance-1-5-pro較早一代的聲畫生成Seedance 1.5 Pro 定位為原生的聲畫聯合模型,而 Seedance 2.0 擴展了多模態參考與編輯堆疊。

與競品相比的優勢:

  • vs Kling 3.0:更佳的多模態參考控制與原生音訊。
  • vs Sora 2:更優的參考精準度與多鏡頭能力;最高時長略低。
  • vs Veo 3.1:在角色一致性與對提示的遵循上對重參考工作流更強;Veo 在原生電影級調色與更長片段方面領先。

在人像為中心與表演驅動的影片中表現出色。

在 CometAPI 上試用 Seedance 2.0 AI 影片生成器

步驟 1:步驟 1:註冊 API 金鑰

直接在 CometAPI Playground 開始體驗 Doubao Seedance 2.0,無需任何設定或程式碼。只需登入您的 CometAPI 帳戶,前往 doubao-seedance-2-0 的模型頁面,上傳您的參考圖片、短影片或音訊檔案,加入描述性提示,即可即時生成預覽影片。這是最快的方式來了解 Seedance 2.0 如何處理運動一致性、角色外觀、鏡頭運動與原生音訊同步。

步驟 2:在 CometAPI 上取得 Seedance 2.0 的 API 存取權

建立或使用您現有的 CometAPI 金鑰以啟用對 Doubao Seedance 2.0 的完整存取。登入後,前往主控台的 API Token 區段,產生新的金鑰並複製。接著造訪 Seedance 2.0 模型詳細頁與 CometAPI 的 API 文件,了解可用參數以整合至您的專案。

步驟 3:使用 Seedance 2.0 API 發送您的第一個請求

使用 CometAPI 端點提交您的第一個影片生成請求,包含清晰的文字提示與可選的參考檔(圖片、影片或音訊)。系統將以非同步方式處理任務,回傳任務 ID,完成後提供可下載的 MP4 影片。之後您可微調提示、調整設定,並擴展至行銷影片、社群內容、自動化影片管線或由 Seedance 2.0 驅動的創意應用。

此流程讓您能在 Playground 快速試驗,並可透過 CometAPI 順利銜接至生產環境。

選擇 CometAPI 上的 Seedance 2.0 的主要理由

為何在 CometAPI 上使用 Seedance 2.0

  • 透過 API 或 Playground 直接存取
  • 易於控制參數(時長、解析度、格式)
  • 同時支援文字轉影片與圖片轉影片工作流
  • 內建非同步影片生成的任務處理

統一且對開發者友善的 API

CometAPI 提供簡潔、標準化的端點,可與熟悉的 OpenAI 風格格式或專用的影片任務端點無縫配合。您可直接建立任務、輪詢並下載 MP4,而不必面對 Volcengine 複雜的驗證或區域性限制。

具成本效益的定價

相較於直接供應商,CometAPI 通常提供更具競爭力的每秒計價,讓您在不超出預算的情況下,為行銷、社群或自動化管線大規模生成高品質影片。

使用 Playground 快速測試

立即在 CometAPI Playground 開始試驗。上傳參考圖片、影片與音訊檔,微調提示,並在數分鐘內預覽結果——非常適合在進入生產前對風格、運動與音訊同步反覆迭代。

簡而言之,如果您希望獲得 Seedance 2.0 的創作實力——同級最佳的參考控制、自然的運動與原生音訊——又不想面對直接接入 ByteDance 的麻煩,CometAPI 目前是其中一個最佳的平台。

常見問題

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

Seedance 2.0 支援多模態輸入,包括文字提示、最多 9 張圖片、最多 3 段短影片,以及最多 3 個音訊檔,可自由組合以實現豐富且可控的生成。

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

是 — Seedance 2.0 專為連貫的多鏡頭敘事而設計,能在各場景中保持角色、視覺風格與氛圍的一致性,減少常見的 AI 影片漂移問題。

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

Seedance 2.0 可產生電影級影片(最高 2K 解析度),具備原生音訊、同步對白與自然的動作合成,單段影片通常為 5–60 秒。

How does Seedance 2.0 handle audio and lip synchronization?

該模型以音與影聯合生成,提供原生的影音同步,並在 8+ 種語言中實現音素級唇形同步,呈現自然的語音與音效。

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

是 — 憑藉多模態控制、多鏡頭連貫性與高保真輸出,Seedance 2.0 適用於行銷影片、敘事短片、廣告等專業應用。

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

使用者可上傳參考素材,並以自然語言說明各素材應如何影響動作、鏡頭運動或風格元素,從而對生成內容進行細粒度控制。

Does Seedance 2.0 allow editing and extension of existing videos?

是 — 該模型支援影片延長與針對性編輯,例如新增場景、更換角色或修改特定片段,同時保留未編輯的部分。

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

單支影片的典型長度約為 ~5 至 ~60 秒,結合大量素材或使用高解析度設定可能會增加生成時間。

Doubao-Seedance-2-0 的功能

探索 Doubao-Seedance-2-0 的核心功能,專為提升效能和可用性而設計。了解這些功能如何為您的專案帶來效益並改善使用者體驗。

Doubao-Seedance-2-0 的定價

探索 Doubao-Seedance-2-0 的競爭性定價,專為滿足各種預算和使用需求而設計。我們靈活的方案確保您只需為實際使用量付費,讓您能夠隨著需求增長輕鬆擴展。了解 Doubao-Seedance-2-0 如何在保持成本可控的同時提升您的專案效果。

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.

Doubao-Seedance-2-0 的範例程式碼和 API

存取完整的範例程式碼和 API 資源,以簡化您的 Doubao-Seedance-2-0 整合流程。我們詳盡的文件提供逐步指引,協助您在專案中充分發揮 Doubao-Seedance-2-0 的潛力。
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

Doubao-Seedance-2-0的版本

Doubao-Seedance-2-0擁有多個快照的原因可能包括:更新後輸出結果存在差異需保留舊版快照以確保一致性、為開發者提供適應與遷移的過渡期,以及不同快照對應全球或區域端點以優化使用者體驗等潛在因素。各版本間的具體差異請參閱官方文件說明。
version
doubao-seedance-2-0
doubao-seedance-2-0-fast

更多模型

O

Sora 2 Pro

每秒:$0.24
Sora 2 Pro 是我們最先進且最強大的媒體生成模型,能生成帶有同步音訊的影片。它可以從自然語言或圖像創建細節豐富、動態的影片片段。
O

Sora 2

每秒:$0.08
超強大的影片生成模型,具備音效,支援對話格式。
M

mj_fast_video

每次請求:$0.6
Midjourney video generation
X

Grok Imagine Video

每秒:$0.04
可根據文字提示生成影片,將靜態圖片動態化,或以自然語言編輯現有影片。API 支援為生成的影片自訂時長、寬高比與解析度 — SDK 會自動處理非同步輪詢。
G

Veo 3.1 Pro

每秒:$0.25
Veo 3.1-Pro 指的是 Google 的 Veo 3.1 系列的高階存取/設定 — 這是一代支援音訊的短影片模型,並加入更豐富的原生音訊、改進的敘事/剪輯控制與場景延伸工具。
G

Veo 3.1

每秒:$0.05
Veo 3.1 是 Google 對其 Veo 文本與圖像→影片系列的一次漸進但意義重大的更新,新增更豐富的原生音訊、更長且更可控的影片輸出,以及更精細的編輯與場景層級控制。

相關部落格

如何使用 Seedance 2.0 API
Apr 17, 2026

如何使用 Seedance 2.0 API

Seedance 2.0 API 是 ByteDance 最新的多模態 AI 影片生成模型(於 2026 年 4 月 9 日推出)。它可在單一請求中接受文字、圖片、影片片段與音訊,生成具原生音訊同步、導演級鏡頭控制與卓越動作一致性的電影級 4–15 秒 MP4 影片。使用方式:在 CometAPI.com 註冊,取得 API 金鑰,透過 REST 提交非同步任務,輪詢完成狀態,並下載影片 URL。
什麼是 HappyHorse-1.0?如何比較 Seedance 2.0?
Apr 11, 2026
seedance-2-0

什麼是 HappyHorse-1.0?如何比較 Seedance 2.0?

了解 HappyHorse-1.0 是什麼、為何登上 Artificial Analysis 影片排行榜榜首、它與 Seedance 2.0 的比較,以及最新排名對 AI 影片生成意味著什麼。
什麼是 Seedance 2.0?全面分析
Mar 24, 2026
seedance-2-0

什麼是 Seedance 2.0?全面分析

Seedance 2.0 是 ByteDance 開發的新一代多模態 AI 影片生成模型,可從文字、圖片、音訊和參考影片生成高品質、具電影感的影片。它具備音訊與影片聯合生成、動作穩定性,以及基於參考內容的編輯能力,並已迅速攀升至 Artificial Analysis 排行榜等全球基準的前列,使其在 2026 年躋身頂尖 AI 影片模型之列。