OpenRouter 入门与概览

OpenRouter 入门与概览

视频生成

6 分钟阅读

视频生成

如何使用 OpenRouter 模型生成视频

OpenRouter 通过专用的异步 API,支持根据文本提示词(以及可选的参考图)生成视频。你可以通过按视频输出筛选我们的模型列表来查找支持的模型、其能力与定价。

雨夜中,镜头缓慢以电影感推向一家温馨咖啡馆橱窗里发光的霓虹灯牌,灯牌上写着 "OpenRouter";雨水顺着玻璃流下,倒影在湿漉漉的路面上荡漾

  • 模型minimax/hailuo-3
  • 输出:一段 5 秒、2K16:9、带音频的视频

完整选项见请求参数

要在应用中加入视频生成? 视频生成 Cookbook 将这一流程拆成逐步实操,涵盖如何选择模型、提交文生视频任务、使用图像、传入模型服务提供商选项,以及处理 Webhook。

若要在多个项目中复用智能体知识,可安装 openrouter-video 技能

查找模型

你可以通过多种方式查找视频生成模型:

通过视频模型 API

使用专用的视频模型端点,列出所有可用的视频生成模型及其支持的参数:

curl "https://openrouter.ai/api/v1/videos/models"

响应返回 data 数组,其中每个模型包含:

{
  "data": [
    {
      "id": "google/veo-3.1",
      "canonical_slug": "google/veo-3.1",
      "name": "Google: Veo 3.1",
      "description": "...",
      "created": 1719792000,
      "supported_resolutions": ["720p", "1080p"],
      "supported_aspect_ratios": ["16:9", "9:16", "1:1"],
      "supported_sizes": ["1280x720", "1920x1080"],
      "pricing_skus": {
        "per-video-second": "0.50",
        "per-video-second-1080p": "0.75"
      },
      "allowed_passthrough_parameters": ["output_config"]
    }
  ]
}
字段说明
id生成请求中使用的模型 slug
canonical_slug永久模型标识符
supported_resolutions支持的输出分辨率列表(例如 720p1080p
supported_aspect_ratios支持的宽高比列表(例如 16:99:16
supported_sizes支持的像素尺寸列表(例如 1280x720
pricing_skus各 SKU 的定价信息
allowed_passthrough_parameters可通过 provider 选项透传的模型服务提供商特定参数

在提交生成请求之前,使用此端点查看各模型支持哪些分辨率、宽高比和透传参数。

通过 Models API

你也可以在 Models API 上使用 output_modalities 查询参数来发现视频生成模型:

# 仅列出视频生成模型
curl "https://openrouter.ai/api/v1/models?output_modalities=video"

在模型页面上

访问模型页面,按输出模态筛选,查找具备视频生成能力的模型。请查找在输出模态中列出 "video" 的模型。

工作原理

与文本或图像生成不同,视频生成是异步的,因为生成视频耗时明显更长。流程为:

  1. 提交生成请求到 POST /api/v1/videos
  2. 立即收到任务 ID 和轮询 URL
  3. 轮询该轮询 URL(GET /api/v1/videos/{jobId}),直到状态为 completed
  4. 从内容 URL 下载视频(GET /api/v1/videos/{jobId}/content

API 用法

提交视频生成请求

import requests
import json
import time

url = "https://openrouter.ai/api/v1/videos"
headers = {
    "Authorization": f"Bearer {API_KEY_REF}",
    "Content-Type": "application/json"
}

payload = {
    "model": "google/veo-3.1",
    "prompt": "一只金毛寻回犬在阳光明媚的海滩上玩抛接,背景是拍岸的海浪"
}

# 第 1 步:提交生成请求
response = requests.post(url, headers=headers, json=payload)
result = response.json()

job_id = result["id"]
polling_url = result["polling_url"]
print(f"任务已提交:{job_id}")
print(f"状态:{result['status']}")

# 第 2 步:轮询直到完成
while True:
    time.sleep(30)  # 两次轮询之间等待 30 秒
    poll_response = requests.get(polling_url, headers=headers)
    status = poll_response.json()

    print(f"状态:{status['status']}")

    if status["status"] == "completed":
        # 第 3 步:下载视频
        content_url = status["unsigned_urls"][0]
        video_response = requests.get(content_url)
        with open("output.mp4", "wb") as f:
            f.write(video_response.content)
        print("视频已保存到 output.mp4")
        break
    elif status["status"] == "failed":
        print(f"生成失败:{status.get('error', '未知错误')}")
        break

请求参数

参数类型是否必需说明
modelstring用于视频生成的模型(例如 google/veo-3.1
promptstring要生成的视频的文本描述
durationinteger生成视频的时长(秒)
resolutionstring输出视频的分辨率(例如 720p1080p
aspect_ratiostring输出视频的宽高比(例如 16:99:163:2
sizestringWIDTHxHEIGHT 格式的精确像素尺寸(例如 1280x720)。可与 resolution + aspect_ratio 互换使用
frame_imagesarray用于首帧/末帧的图像(图生视频)
input_referencesarray用于风格引导的参考图(参考图生视频)
generate_audioboolean是否同时生成音频。对于支持音频输出的模型,默认为 true
seedinteger用于确定性生成的种子(并非所有模型服务提供商都保证)
callback_urlstring任务完成时接收 Webhook 通知的 URL。若设置,将覆盖工作空间级默认回调 URL。必须为 HTTPS
providerobject模型服务提供商特定的透传配置

支持的分辨率

  • 480p
  • 720p
  • 768p
  • 1080p
  • 1K
  • 2K
  • 4K

支持的宽高比

  • 16:9:横向宽屏
  • 9:16:竖屏/纵向
  • 1:1:正方形
  • 4:3:标准横向
  • 3:4:标准纵向
  • 3:2:摄影横向
  • 2:3:摄影纵向
  • 21:9:超宽
  • 9:21:超高

使用图像

提供图像有两种方式,分别触发不同的生成模式:

  • frame_images:指定首帧或末帧图像,用于图生视频。每条记录必须包含 first_framelast_frameframe_type
  • input_references:提供风格或内容参考图,用于参考图生视频。模型将这些图像作为视觉引导,而不是精确帧。

如果两个字段同时提供,frame_images 优先,请求将按图生视频处理。

图生视频(frame_images)

{
  "model": "alibaba/wan-2.7",
  "prompt": "一个角色走在森林中",
  "frame_images": [
    {
      "type": "image_url",
      "image_url": {
        "url": "https://example.com/first-frame.png"
      },
      "frame_type": "first_frame"
    }
  ],
  "resolution": "1080p"
}

参考图生视频(input_references)

{
  "model": "alibaba/wan-2.7",
  "prompt": "行星旁的巨大太阳耀斑",
  "input_references": [
    {
      "type": "image_url",
      "image_url": {
        "url": "https://example.com/style-ref.png"
      }
    }
  ],
  "resolution": "1080p"
}

模型服务提供商特定选项

你可以使用 provider 参数传入模型服务提供商特定选项。选项以模型服务提供商 slug 为键,只有匹配到的模型服务提供商的选项会被转发:

{
  "model": "google/veo-3.1",
  "prompt": "一朵花绽放的延时摄影",
  "provider": {
    "options": {
      "google-vertex": {
        "parameters": {
          "personGeneration": "allow",
          "negativePrompt": "模糊,低质量"
        }
      }
    }
  }
}

使用视频模型 API,通过 allowed_passthrough_parameters 字段查看各模型支持哪些透传参数。

响应格式

提交响应(202 Accepted)

提交视频生成请求后,你会立即收到包含任务详情的响应:

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

轮询响应

轮询任务状态时,响应会随着任务进展包含更多字段:

{
  "id": "abc123",
  "generation_id": "gen-1234567890-abcdef",
  "polling_url": "https://openrouter.ai/api/v1/videos/abc123",
  "status": "completed",
  "unsigned_urls": [
    "https://openrouter.ai/api/v1/videos/abc123/content?index=0"
  ],
  "usage": {
    "cost": 0.25,
    "is_byok": false
  }
}

任务状态

状态说明
pending任务已提交并排队
in_progress正在生成视频
completed视频已可下载
failed生成失败(请查看 error 字段)

下载视频

一旦任务状态为 completedunsigned_urls 数组会包含用于下载生成视频内容的 URL。你也可以直接使用内容端点:

curl "https://openrouter.ai/api/v1/videos/{jobId}/content?index=0" \
  -H "Authorization: Bearer $OPENROUTER_API_KEY" \
  --output video.mp4

index 查询参数默认为 0,在模型生成多个视频输出时使用。

Webhook

除了轮询任务状态,你也可以在视频生成任务完成时接收 Webhook 通知。配置回调 URL 有两种方式:

  1. 按请求:在请求体中传入 callback_url。其优先级高于工作空间默认值。
  2. 工作空间默认值:在工作空间设置中设置默认回调 URL。该设置适用于所有未自行指定 callback_url 的视频生成请求。

Webhook 载荷

当任务到达终态时,OpenRouter 会向回调 URL 发送带有事件封装的 POST 请求。每次投递还会携带 X-OpenRouter-Idempotency-Key 请求头,格式为 <job_id>-<status>,以便安全地对重试进行去重。

video.generation.completed

{
  "type": "video.generation.completed",
  "created_at": "2026-04-24T12:00:00.000Z",
  "data": {
    "id": "abc123",
    "status": "completed",
    "generation_id": "gen-xyz789",
    "model": "google/veo-3.1",
    "unsigned_urls": [
      "https://openrouter.ai/api/v1/videos/abc123/content?index=0"
    ],
    "usage": {
      "cost": 0.5,
      "is_byok": false
    }
  }
}

video.generation.failed

{
  "type": "video.generation.failed",
  "created_at": "2026-04-24T12:00:00.000Z",
  "data": {
    "id": "abc123",
    "status": "failed",
    "generation_id": "gen-xyz789",
    "model": "google/veo-3.1",
    "error": "Content policy violation"
  }
}

video.generation.cancelled

{
  "type": "video.generation.cancelled",
  "created_at": "2026-04-24T12:00:00.000Z",
  "data": {
    "id": "abc123",
    "status": "cancelled",
    "generation_id": "gen-xyz789",
    "model": "google/veo-3.1",
    "error": "Job was cancelled"
  }
}

video.generation.expired

{
  "type": "video.generation.expired",
  "created_at": "2026-04-24T12:00:00.000Z",
  "data": {
    "id": "abc123",
    "status": "expired",
    "generation_id": "gen-xyz789",
    "model": "google/veo-3.1",
    "error": "Job exceeded maximum time to live"
  }
}

当任务在这些值被赋值之前就失败时(例如早期校验失败),data 中的 generation_idmodel 可能为 null

签名密钥

你可以在工作空间设置中配置签名密钥,以验证 Webhook 载荷确实来自 OpenRouter。配置签名密钥后,每次 Webhook 投递都会包含 X-OpenRouter-Signature 请求头。

签名包含时间戳和 HMAC 哈希:

X-OpenRouter-Signature: t=1234567890,v1=a1b2c3d4...

验证签名

要在 Webhook 接收端验证签名:

  1. 从请求头中提取时间戳(t)和签名哈希(v1
  2. 构造待签名载荷:{timestamp},{raw_request_body}(用逗号连接)
  3. 使用你的签名密钥作为密钥,计算待签名载荷的 HMAC-SHA256
  4. 将十六进制编码结果与 v1 值比较
import crypto from 'crypto';

const FIVE_MINUTES_IN_SECONDS = 300;

function verifyWebhookSignature(
  rawBody: string,
  signatureHeader: string,
  secret: string,
): boolean {
  const parts = signatureHeader.split(',');
  const timestamp = parts.find((p) => p.startsWith('t='))?.slice(2);
  const hash = parts.find((p) => p.startsWith('v1='))?.slice(3);

  if (!timestamp || !hash) {
    return false;
  }

  // 拒绝超过 5 分钟的时间戳,以防止重放攻击
  const age = Math.floor(Date.now() / 1000) - Number(timestamp);
  if (Number.isNaN(age) || age > FIVE_MINUTES_IN_SECONDS) {
    return false;
  }

  const signedPayload = `${timestamp},${rawBody}`;
  const expected = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');

  if (expected.length !== hash.length) {
    return false;
  }

  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(hash),
  );
}

验证时请使用原始请求体(收到的原始字节)。解析并重新序列化 JSON 可能会改变键的顺序或数字格式,从而导致验证失败。

最佳实践

  • 详细的提示词:提供具体、描述性的提示词以获得更好的视频质量。包含关于运动、机位、灯光和场景构图的细节
  • 合适的分辨率:更高分辨率生成更久、费用更高。选择适合你使用场景的分辨率
  • 轮询间隔:使用合理的轮询间隔(例如 30 秒),避免过多 API 调用。视频生成通常需要 30 秒到数分钟,具体取决于模型和参数
  • 错误处理:始终检查任务状态是否为 failed,并妥善处理 error 字段
  • 参考图:使用参考图时,确保它们质量高,并且与所需视频输出相关

零数据保留

视频生成不适用零数据保留(Zero Data Retention,ZDR)。由于视频生成是异步的,模型服务提供商必须短暂保留生成的视频输出,以便你在生成完成后检索。这种临时保留是异步轮询流程所固有的,无法绕过。

如果已启用 ZDR 强制执行(通过账户设置或按请求的 zdr 参数),OpenRouter 将不会路由视频生成请求。

故障排除

任务长时间停留在 pending 状态?

  • 视频生成可能需要数分钟,具体取决于模型、分辨率和服务器负载
  • 请按固定间隔继续轮询

生成失败?

  • 查看轮询响应中的 error 字段以了解详情
  • 确认模型支持视频生成(output_modalities 包含 "video"
  • 确保提示词合适,并符合模型指南
  • 检查所有参考图是否可访问,且为支持的格式

找不到模型?