跳到正文
北京时间
原文
OpenRouter:Announcements(RSS)· OpenRouter·· 2026-08-25精选AI 评分66

OpenRouter 视频生成 API:一份代码优先的接入指南

OpenRouter Video Generation API: A Code-First Guide

AI 导读

OpenRouter 推出统一的异步视频生成 API,通过 POST /api/v1/videos 提交任务、轮询状态并下载 MP4,支持 Seedance、Veo、Wan 等模型,切换模型只需更改 model 标识符。

推荐理由

将视频生成抽象为提交、轮询、下载的异步作业,模型切换只改标识符,同时覆盖幂等、webhook 与并发控制,可迁移到其他异步生成 API 的集成。

正文 · AI 翻译

OpenRouter Video Generation API: A Code-First Guide

在测试单个模型时,为应用添加视频生成功能是件很简单的事。但当你想要尝试另一个模型时,复杂性就显现出来了。每家提供商可能都有自己的端点、请求参数、任务状态、轮询逻辑和输出格式。这就把一个简单的模型更换变成了一次需要构建和维护的新集成。

我们把这一整套流程封装到了一个异步视频 API中。你向 POST /api/v1/videos 提交一个提示词,收到一个任务 ID,轮询直到生成完成,然后下载生成好的视频。

在本指南中,我们将从头到尾构建这一完整流程。我们会用 Seedance 提交一个任务,安全地轮询它,保存 MP4 文件,然后用 Veo 和 Wan 运行同样的集成。

摘要速览

  • 一个端点,多个视频模型。通过 POST /api/v1/videos 使用 Seedance、Veo、Wan 及其他受支持的模型进行生成。
  • 该流程是异步的。提交任务,轮询其状态,然后下载完成的视频。
  • 通过更改模型标识符即可切换模型。某些模型专属设置(如时长和宽高比)仍需要针对每个模型进行调整,更多内容见第 4 步,但端点、认证、轮询循环和下载逻辑始终不变。

为什么异步 API 比其他方案更好

视频生成所需的时间比典型的 API 响应更长。模型必须生成并协调大量帧,保持它们之间的视觉一致性,有时还要生成匹配的音频。根据模型和所请求的设置,这一过程可能需要几秒到几分钟。

在整个期间保持原始 HTTP 请求处于打开状态是很脆弱的。浏览器会话可能关闭,无服务器函数可能达到其执行限制,或者代理可能在视频准备好之前超时。

异步 API 将提交与完成分离开来:

  1. 提交生成请求。
  2. 立即收到一个任务 ID。
  3. 单独检查任务的状态。
  4. 生成完成后下载视频。

当模型在后台工作时,你的应用程序可以继续运行。它还可以在重启后恢复任务,因为生成过程绑定在一个持久的任务 ID 上,而不是一个长期保持的连接上。

直接与单一提供商集成

当你已经确定想要哪个模型,并且预计不会改变时,直接与提供商集成可以很好地工作。你使用该提供商的认证、请求格式、任务状态、轮询端点和输出响应。

当你想要对比另一个模型时,这些额外的工作就会显现出来。新的提供商可能在时长和分辨率上使用不同的字段名,或者返回带有不同终态状态的作业对象。它可能还需要另一种方法来下载生成完成的资产。这样一来,你的应用就需要第二个客户端、另一组环境变量,以及更多针对特定提供商的错误处理逻辑。

这种做法本身并没有什么问题。它只是意味着切换模型变成了一次集成改动,而不是一次配置改动,这会让实验速度变慢,并且随着你的模型列表不断增长,维护成本也会随之上升。

在本地运行视频模型

本地生成能给你最大的掌控力。你可以选择模型权重、自定义工作流、将资产保留在自己的环境中,并且不必为每一次生成都向托管的提供商付费。

这种掌控力也伴随着基础设施方面的责任。你需要合适的 GPU 算力,以及正确的 Python 和 CUDA 依赖。你还需要足够的存储空间,并为每个模型家族搭建可用的运行环境。更高的分辨率和更长的视频会增加内存和处理需求,而新增一个模型可能意味着要下载更多权重或维护另一套工作流。

对于已经运营 GPU 基础设施或需要本地处理的团队来说,这样做是值得的。但如果你目标是快速加入视频生成能力并测试多个模型,这算是一个比较重的起点。托管的 OpenRouter 路径省去了大部分这类搭建工作,这也是本指南其余部分要介绍的内容。

通过 OpenRouter 使用一个统一托管的 API

我们在所有受支持的视频模型之间保持一致的生成生命周期。无论所选模型是 Seedance、Veo、Wan 还是目录中的其他模型,应用程序都使用相同的 API 密钥、POST /api/v1/videos 端点、任务状态流程和结果获取过程。

这些模型仍然具备不同的能力。有的可能支持更长的时长,有的则提供额外的宽高比、更高的分辨率、音频生成或特定于提供商的控制选项。我们通过视频模型端点来呈现这些差异,而不是强行让每个模型都具备完全相同的功能集。

这样既能为你提供稳定的集成体验,又不会掩盖每个模型的独特之处。你的应用程序可以查询当前的能力、构建有效的请求,并在不替换周边任务基础设施的情况下切换模型。

前提条件与设置

你只需要一个 OpenRouter API 密钥和一个能够发送 HTTP 请求的工具。这里的示例使用 Python 搭配 requests,以及 TypeScript 搭配内置的 fetch API,但该工作流程适用于任何能够发起 HTTP 请求的编程语言。

首先从你的 OpenRouter 账户创建一个 API 密钥,然后将其存储在环境变量中,而不是直接写进源代码:

export OPENROUTER_API_KEY="sk-or-..."

对于 Python 示例,如果你尚未安装 requests,请先安装:

pip install requests

OpenRouter 使用 bearer token 对 API 请求进行身份验证。在 Python 中,我们会先定义一次共享值,然后在整个指南中重复使用:

import os
import requests

API_KEY = os.environ["OPENROUTER_API_KEY"]
BASE_URL = "https://openrouter.ai/api/v1"

HEADERS = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
}

在提交任务之前,你还可以查询视频模型端点,查看当前有哪些模型可用,以及每个模型支持哪些功能:

curl "https://openrouter.ai/api/v1/videos/models" \
  -H "Authorization: Bearer $OPENROUTER_API_KEY"

响应中包含每个模型支持的时长、分辨率、宽高比、帧图像支持、音频能力、定价 SKU 以及各提供商专属参数。这比假设某个视频模型接受的设置也适用于另一个模型要可靠得多。

第 1 步:提交视频生成任务

向 /api/v1/videos 发送一个 POST 请求,并指定视频模型。请求中包含一个描述你想要生成内容的提示词。

每个请求都必须包含 model,而文本生成视频则必须包含 prompt。支持仅凭图像输入生成视频的模型可以省略后者。当所选模型支持时,你还可以提供可选设置,例如时长、分辨率、宽高比、音频生成、参考图像和随机种子。

我们将在整个指南中使用相同的提示词:

PROMPT = (
    "A paper boat drifting down a rain-slicked gutter at night, "
    "neon reflections, slow tracking shot, cinematic lighting"
)

以下函数使用 Seedance 2.0 提交任务:

def submit_video(model: str, prompt: str) -> dict:
    response = requests.post(
        f"{BASE_URL}/videos",
        headers=HEADERS,
        json={
            "model": model,
            "prompt": prompt,
            "duration": 4,
            "resolution": "720p",
            "aspect_ratio": "16:9",
            "generate_audio": False,
        },
        timeout=60,
    )

    response.raise_for_status()
    return response.json()


job = submit_video(
    model="bytedance/seedance-2.0",
    prompt=PROMPT,
)

print("Job ID:", job["id"])
print("Status:", job["status"])
print("Polling URL:", job["polling_url"])

等效的 cURL 请求如下:

curl "https://openrouter.ai/api/v1/videos" \
  -H "Authorization: Bearer $OPENROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "bytedance/seedance-2.0",
    "prompt": "A paper boat drifting down a rain-slicked gutter at night, neon reflections, slow tracking shot, cinematic lighting",
    "duration": 4,
    "resolution": "720p",
    "aspect_ratio": "16:9",
    "generate_audio": false
  }'

请求成功后会返回 HTTP 202 Accepted。该响应代表的是一个后台任务,而非已完成的视频:

{
  "id": "job-abc123",
  "status": "pending",
  "polling_url": "https://openrouter.ai/api/v1/videos/job-abc123"
}

在继续之前,请先保存返回的任务 ID。如果你的进程重启,你应该能够恢复跟踪现有任务,而不是重新提交并再为一次生成付费。

第 2 步:轮询任务直至其完成

第 1 步中返回的 polling_url 指向的任务资源,与你在 GET /api/v1/videos/{id} 处访问的是同一个任务资源,它们是同一个端点。视频任务可能经历以下状态:

状态含义
pending任务已被接受,正在等待运行
in_progress服务提供商正在生成视频
completed视频已准备好可供下载
failed生成失败
cancelled该任务已被取消
expired该任务超出了其允许的存活时间

你的轮询循环应在遇到 completed 时返回,并在遇到 failed、cancelled 或 expired 时停止并报错。否则,应用程序可能会持续检查一个永远不会生成视频的任务。

文档中记录的响应会将 polling_url 作为完整 URL 返回。下面的 urljoin 调用属于防御性编码,用于同时处理相对路径的情况,这样循环在两种情况下都能正常工作:

import time
from urllib.parse import urljoin

TERMINAL_ERROR_STATES = {
    "failed",
    "cancelled",
    "expired",
}


def poll_video(
    initial_job: dict,
    interval: float = 30.0,
    timeout: float = 3600.0,
) -> dict:
    """Poll until the video completes or reaches an error state."""

    polling_url = urljoin(
        "https://openrouter.ai",
        initial_job["polling_url"],
    )

    deadline = time.monotonic() + timeout
    job = initial_job

    while True:
        status = job["status"]
        print("Status:", status)

        if status == "completed":
            return job

        if status in TERMINAL_ERROR_STATES:
            error = job.get("error") or "No error details were returned."
            raise RuntimeError(
                f"Video generation ended with status '{status}': {error}"
            )

        if status not in {"pending", "in_progress"}:
            raise RuntimeError(
                f"Received unexpected job status: {status}"
            )

        if time.monotonic() >= deadline:
            raise TimeoutError(
                f"Job {job['id']} did not complete within "
                f"{timeout} seconds."
            )

        time.sleep(interval)

        response = requests.get(
            polling_url,
            headers={
                "Authorization": f"Bearer {API_KEY}",
            },
            timeout=30,
        )

        response.raise_for_status()
        job = response.json()


completed_job = poll_video(job)

这个循环包含了两道快速示例中常常省略的防护措施。首先,它处理了所有文档中记录的终态,而不仅仅是等待 completed。其次,它设置了一小时的超时时间,确保任务不会让进程无限期地运行下去。有一个值得了解的边界情况:由于截止时间是在每次休眠之前而非之后检查的,在最坏的情况下,任务在循环捕获到超时之前,可能会比名义超时时间多运行一个轮询间隔。对于后台任务来说,这是一个可以接受的权衡。如果你需要硬性上限,也可以在从休眠中醒来后立即再次检查截止时间。

我们目前的建议是使用 30 秒的轮询间隔。视频任务通常需要大约 30 秒到几分钟的时间,每秒检查一次并不会让服务提供商更快完成。上述的间隔和超时上限都属于操作层面的建议,并非该接口本身文档化的契约,因此你可以根据自己的工作负载进行调整。

同样的轮询流程用 TypeScript 实现如下:

type VideoJobStatus =
  | "pending"
  | "in_progress"
  | "completed"
  | "failed"
  | "cancelled"
  | "expired";

type VideoJob = {
  id: string;
  polling_url: string;
  status: VideoJobStatus;
  error?: string;
  unsigned_urls?: string[];
};

const apiKey = process.env.OPENROUTER_API_KEY;

if (!apiKey) {
  throw new Error("OPENROUTER_API_KEY is not set");
}

const terminalErrorStates = new Set<VideoJobStatus>([
  "failed",
  "cancelled",
  "expired",
]);

async function pollVideo(
  initialJob: VideoJob,
  intervalMs = 30_000,
  timeoutMs = 3_600_000,
): Promise<VideoJob> {
  const pollingUrl = new URL(
    initialJob.polling_url,
    "https://openrouter.ai",
  );

  const deadline = Date.now() + timeoutMs;
  let job = initialJob;

  while (true) {
    console.log(`Status: ${job.status}`);

    if (job.status === "completed") {
      return job;
    }

    if (terminalErrorStates.has(job.status)) {
      throw new Error(
        job.error ?? `Video generation ${job.status}`,
      );
    }

    if (Date.now() >= deadline) {
      throw new Error(
        `Video job ${job.id} did not complete before the timeout`,
      );
    }

    await new Promise((resolve) =>
      setTimeout(resolve, intervalMs),
    );

    const response = await fetch(pollingUrl, {
      headers: {
        Authorization: `Bearer ${apiKey}`,
      },
    });

    if (!response.ok) {
      throw new Error(
        `Polling failed: ${response.status} ${await response.text()}`,
      );
    }

    job = (await response.json()) as VideoJob;
  }
}

要把状态请求失败与视频任务失败区别对待。轮询时出现临时超时并不能证明生成本身失败了。应该针对同一个任务 ID 重试状态请求,而不是提交一个新任务。

第 3 步:获取并保存视频

当状态变为 completed 时,任务响应中会包含一个已填充的 unsigned_urls 数组。每个条目都指向该任务经过认证的内容端点:

GET /api/v1/videos/{jobId}/content?index=0

索引默认为 0。只有当模型返回多个视频输出时才需要更改。尽管字段名如此,这些 URL 并不是预签名的,所以发送请求时需要在 Authorization 头中带上你的 API 密钥,就像轮询时一样。

下面的辅助函数在存在未签名 URL 时使用第一个,在极少数情况下如果没有,则根据任务 ID 重新构造内容 URL。

def download_video(
    job: dict,
    output_path: str = "out.mp4",
    index: int = 0,
) -> None:
    unsigned_urls = job.get("unsigned_urls") or []

    download_url = (
        unsigned_urls[index]
        if len(unsigned_urls) > index
        else (
            f"{BASE_URL}/videos/"
            f"{job['id']}/content?index={index}"
        )
    )

    with requests.get(
        download_url,
        headers={
            "Authorization": f"Bearer {API_KEY}",
        },
        stream=True,
        timeout=180,
    ) as response:
        response.raise_for_status()

        with open(output_path, "wb") as output_file:
            for chunk in response.iter_content(
                chunk_size=1024 * 1024
            ):
                if chunk:
                    output_file.write(chunk)

    print(f"Saved {output_path}")


download_video(completed_job)

分块流式接收响应可以避免在将整个 MP4 写入磁盘之前将其全部加载到内存中。

下面是 TypeScript 等价实现。请注意,这个版本是将下载内容缓冲到内存中,而不是流式写入磁盘,对于短视频片段来说没问题,但如果你经常下载长视频或高分辨率视频,建议换成管道流式写入:

import { writeFile } from "node:fs/promises";

async function downloadVideo(
  job: VideoJob,
  outputPath = "out.mp4",
  index = 0,
): Promise<void> {
  const downloadUrl =
    job.unsigned_urls?.[index] ??
    `https://openrouter.ai/api/v1/videos/` +
      `${job.id}/content?index=${index}`;

  const response = await fetch(downloadUrl, {
    headers: {
      Authorization: `Bearer ${apiKey}`,
    },
  });

  if (!response.ok) {
    throw new Error(
      `Download failed: ${response.status} ` +
      `${await response.text()}`,
    );
  }

  const videoBuffer = Buffer.from(
    await response.arrayBuffer(),
  );

  await writeFile(outputPath, videoBuffer);
  console.log(`Saved ${outputPath}`);
}

此时,你已在磁盘上生成了一个 MP4 文件。请将完成的视频移动到你自己控制的存储中,而不要把生成端点当作永久文件托管来使用。已完成的任务还可能包含一个 usage 对象,其中记录了最终费用,无论你使用哪种语言,该对象都是响应体的一部分:

usage = completed_job.get("usage") or {}

print("Generation cost:", usage.get("cost"))
print("Used BYOK:", usage.get("is_byok"))

将该值与你的内部任务记录一起保存,以便跟踪每次生成的实际费用。

第 4 步:一行代码切换模型

提交、轮询和下载函数并不绑定 Seedance。要使用其他受支持的视频模型,只需更改模型标识符:

# Seedance
MODEL = "bytedance/seedance-2.0"

# Veo
# MODEL = "google/veo-3.1"

# Wan
# MODEL = "alibaba/wan-2.7"

job = submit_video(
    model=MODEL,
    prompt=PROMPT,
)

completed_job = poll_video(job)
download_video(completed_job)

端点、身份验证、响应结构、状态处理和下载逻辑在这三个模型上保持一致。不会自动迁移的是各种可选设置。切换模型在代码上只是一行改动,但这并不保证任意时长、分辨率或宽高比组合都能在新模型上通过校验。本指南中的这个配置恰好在这三个模型上均可移植:

{
  "duration": 4,
  "resolution": "720p",
  "aspect_ratio": "16:9",
  "generate_audio": false
}

在撰写本文时,实时模型端点显示 Seedance 2.0、Veo 3.1 和 Wan 2.7 均支持这一特定组合:四秒、720p、16:9。这是这三个示例之间的共享配置,并非声称每个设置在每个模型上都能以相同方式工作。一旦超出该配置,差异便会迅速显现:

  • Veo 3.1 目前显示支持四秒、六秒和八秒的时长。
  • Seedance 2.0 目前支持 4 至 15 秒的时长以及更多宽高比选项。
  • Wan 2.7 目前支持 2 至 10 秒的时长以及 720p 或 1080p 分辨率。

一个 5 秒的请求可以通过 Seedance 和 Wan 的校验,但会在 Veo 上失败。因此,你的应用应该在提交请求之前查询 /api/v1/videos/models,而不是假设某个模型接受的设置也能在另一个模型上生效。上述数字在依赖之前值得对照该实时端点重新核实,因为模型能力确实会变化。

同一个端点还通过 allowed_passthrough_parameters 暴露了各模型特有的功能。这些是你被允许在请求的 provider.options 对象中发送的键,该对象以提供商 slug 为键,例如 provider.options["google-vertex"].parameters。只有处理你请求的提供商对应的选项才会被转发,无法识别的键会被丢弃。例如,Veo 目前列出了诸如 negativePrompt 和 enhancePrompt 之类的控制项,而 Wan 则暴露了包括 negative_prompt 和 prompt_extend 在内的选项。

在投入生产之前,有几件事值得了解

上面的代码足以生成并下载一个视频。一旦它在生产中运行,问题就变了:你需要控制成本、区分任务失败与网络失败、避免重复处理,并在提交进程退出后持续跟踪任务。

在扩展之前先检查成本

视频生成的价格因模型和配置而异。时长、分辨率、音频生成以及提供商的计费方式都会影响最终成本。本地生成则完全改变了这种成本结构,没有按片段计费,但取而代之的是实实在在的前期硬件和维护成本。托管 API 则让成本保持可变并与使用量挂钩,具体是更便宜还是更贵,取决于你的用量以及你是否已经拥有相关硬件。

不要在你的应用中内置一个通用的成本计算公式。在显示预估费用或提交大批量任务之前,请查询 /api/v1/videos/models 并阅读所选模型的 pricing_skus。当任务完成时,响应中可以包含一个 usage 对象,其中带有该次生成的实际成本:

{
  "usage": {
    "cost": 0.5,
    "is_byok": false
  }
}

在运行大批量任务之前,请使用当前的模型数据估算成本,然后将估算值与已完成任务返回的实际 usage.cost 值进行比较。这也有助于你发现因更高分辨率、更长时长、生成音频或不同模型而导致的意外成本变化。

处理失败时避免产生重复任务

轮询 请求失败并不等同于视频生成 任务失败。你的应用可能在检查状态时失去连接,但提供商仍在生成视频。如果你立即重新提交相同的提示词,两个任务可能都会完成,导致一个用户请求产生两段视频和两次扣费。

一旦提交成功,请立即持久化保存 OpenRouter 任务 ID。一份有用的任务记录可能包含如下字段:

{
  "internal_request_id": "req_9f21",
  "openrouter_job_id": "job-abc123",
  "model": "bytedance/seedance-2.0",
  "status": "pending",
  "attempt_number": 1,
  "submitted_at": "2026-07-27T12:00:00Z",
  "output_location": null,
  "cost": null,
  "error": null
}

当状态请求因超时、连接错误或临时服务器响应而失败时,请使用现有任务 ID 重试状态请求。只有在任务本身达到 failed、cancelled 或 expired 之后,且你的应用重试策略允许再次尝试时,才创建新的生成任务。

将任务重试与轮询重试分开处理。轮询重试是再次检查同一个任务,而生成重试则会创建新的付费任务。限制生成重试次数,并保留为同一内部请求创建的所有任务 ID,这样在需要排查重复输出、供应商故障或意外费用时,你就能拥有完整的记录。

当轮询无法扩展时,请使用 Webhook

对于脚本、原型和少量任务,轮询是一个不错的默认方案。但当你的应用可能同时运行数百个生成任务时,轮询的效率就会降低。

要自动接收结果,请在提交任务时包含一个 HTTPS callback_url:

{
  "model": "bytedance/seedance-2.0",
  "prompt": "A paper boat drifting through neon reflections",
  "duration": 4,
  "resolution": "720p",
  "aspect_ratio": "16:9",
  "callback_url": "https://example.com/webhooks/openrouter-video"
}

你可以为单个请求设置回调,也可以为工作区配置默认回调。请求级别的值优先于工作区默认值。

当任务达到终态时,我们会发送 webhook。每次投递都包含一个 X-OpenRouter-Idempotency-Key,例如:

job-abc123-completed

在处理事件之前先保存该值。如果 webhook 被再次投递,你的处理器就能识别出该任务已被处理过,从而避免重复下载视频或重复启动下一个工作流。

当配置了 webhook 签名密钥时,请求中还会包含一个 X-OpenRouter-Signature。在解析或重新序列化请求之前,请对照原始请求体验证签名。生产环境的处理器随后应保存新的任务状态、返回快速的成功响应,并将下载、转码或存储工作移交给后台 worker。

在持久化存储中跟踪并发任务

提交和等待是相互独立的操作,因此你的应用可以同时运行多个视频任务。不要为每个任务启动无限制的轮询循环。请使用有界 worker 池或任务队列,并控制状态请求和下载的并发数量。

在 Python 中,你可以使用线程池或异步 worker 队列来处理有限数量的任务。在 TypeScript 中,带并发控制的队列比直接将数千个轮询 promise 传给 Promise.all() 更安全。

具体实现方式不如以下规则重要:

  1. 在开始轮询之前,先保存每个任务 ID。
  2. 限制同时进行的轮询与下载操作数量。
  3. 工作节点重启后,恢复未完成的任务。
  4. 不要仅仅因为应用重启就重新提交任务。
  5. 及时将已生成的视频转移到你自己的存储中。

任务 ID 是你的应用与正在进行的生成之间的持久连接。请将其视为应用状态的一部分,而不是只存在于某个运行进程中的值。

综合运用

我们介绍了四个步骤,无论你对接哪个模型,这些步骤都不会改变。你只需使用 POST /api/v1/videos 提交任务,用 GET /api/v1/videos/{id} 轮询并留意全部四种终态,下载结果,想换模型时改一个字符串即可。

一旦异步生命周期处理正确,模型就变成了一项设置,而不是架构决策——无论你用的是本文介绍的三个模型,还是之后新增的任何模型,都是如此。

如果你想找个起点,可以先浏览视频模型目录,在选定之前并排比较各家定价与能力。

常见问题

OpenRouter 支持视频生成吗?

可以,通过专用的异步 API 即可实现。您向 POST /api/v1/videos 提交提示词,轮询 GET /api/v1/videos/{id} 直到状态变为 completed,然后下载结果。支持的模型包括 Seedance、Veo、Wan 等,均通过同一端点提供。

如何通过 API 从文本生成视频?

向 /api/v1/videos 发送 POST 请求,包含模型和提示词。您会收到一个任务 ID 和 polling_url,而不是视频本身。轮询直到状态达到 completed,然后从 unsigned_urls 或 /content 端点下载。

如何轮询异步视频生成任务?

按一定间隔调用 GET /api/v1/videos/{id},大约 30 秒一次比较合理,直到状态达到终态:completed、failed、cancelled 或 expired。设置超时上限,以免卡住的任务让您的进程无限挂起。

OpenRouter 支持哪些视频模型?

模型目录包括 Seedance、Veo、Wan 等,并且还在持续扩充。查询 GET /api/v1/videos/models 可获取当前列表,以及每个模型支持的分辨率、时长、宽高比和透传参数。

我能否在不重写代码的情况下切换视频模型?

是的。请求结构、身份验证和轮询循环在所有模型中都是相同的,只有 model 字段会变化。模型专属参数仍然通过 provider.options 透传对象传递给提供商。

在本地生成 AI 视频和通过 API 生成,哪个更便宜?

本地生成在支付硬件费用后没有单条视频的费用,但需要一块性能足够的 GPU、依赖管理,并且每个模型家族都需要单独配置。托管 API 按生成次数收费,但完全不需要 GPU 和配置。哪个对你更便宜,取决于你的使用量以及你是否已经拥有硬件。

AI 视频生成需要多长时间?

通常在三十秒到几分钟之间,具体取决于模型、分辨率和视频长度。这就是 API 采用异步而非普通阻塞调用的原因。

视频生成是否符合零数据保留(Zero Data Retention)的条件?

不符合。异步检索步骤要求生成的输出被短暂保留以便下载,因此启用 ZDR 的请求不会被路由到视频生成服务。

来源:OpenRouter:Announcements(RSS) · openrouter.ai