模型定价企业
500+ AI 模型 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
提供方字节跳动 / 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 是字节跳动于 2026 年 2 月发布的旗舰多模态视频生成模型。它采用统一的音视频联合生成架构,可同时引用多张图像、视频片段和音频文件,实现精确的导演级控制。该模型擅长生成电影质感、运动稳定的视频,并具备原生音频同步能力——非常适合需要真实物理、一致角色与复杂场景调度的专业创作者。

CometAPI 上的 Doubao Seedance 2.0 主要特性

  • 统一多模态生成: 在一次工作流中同时接收文本、图像、音频与视频输入,比纯文本生成器拥有更丰富的参考素材来“导演”场景。
  • 导演式控制: 模型可根据提示控制表演、光线、阴影、镜头运动、运动节奏与声音特性。
  • 视频编辑与扩展: 不仅限于首轮生成;支持编辑特定片段、角色、动作与剧情,并可向前/向后延展。
  • 强音视频同步: 字节跳动强调双通道立体声输出,以及背景音乐、环境音与画外音行为的同步,以获得更沉浸的效果。
  • 复杂场景的高可控性: 相比 Seedance 1.5,改进了对复杂交互、运动场景与物理合理性的处理。
  • 面向生产的输出: API 支持可配置的比例、时长和水印设置,使模型适用于可复用的内容工作流。

基准性能

字节跳动表示,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当可略微牺牲质量以换取更快产出时更合适字节跳动称其保持与 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 下载,而无需处理火山引擎的复杂认证或区域限制。

高性价比的定价

与直接服务商相比,CometAPI 通常提供更具竞争力的按秒计费价格,使您能够以规模化方式生成用于营销、社媒或自动化流水线的高质量视频,而不会超出您的预算。

通过 Playground 快速测试

立即在 CometAPI Playground 中试验。上传参考图像、视频与音频,微调提示词,并在数分钟内预览结果——在进入生产前快速迭代风格、运动与音频同步的理想之选。

总之,如果您希望获得 Seedance 2.0 的创作能力——一流的参考控制、自然的运动与原生音频——同时避免直接接入字节跳动带来的麻烦,那么 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 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 的集成流程,我们提供逐步指导,助你发挥模型潜能。
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 视频模型之列。