视频生成 API 实战:一个端点接入 Veo 3.1 / Hailuo 2.3 / Seedance 2.0
作者:CodeGateway 团队 发布日期:2026-06-06 阅读时长:约 12 分钟
TL;DR:视频生成的麻烦从来不在 prompt,而在接入——三个好模型分属三家厂商,就要管三套 Key、三种请求体、三份账单。CodeGateway 把它收成一个端点:POST /v1/video/generate,一套 Bearer Key 就能调用 Google Veo 3.1、MiniMax Hailuo 2.3、ByteDance Seedance 2.0,靠请求体里的 model 字段切换模型。一个要先知道的关键点:端点是同步阻塞的,请求会保持约两分钟直到视频生成完毕再返回结果,所以把读超时设长、别写轮询。
目录
视频生成模型这两年迭代得很快,但凡真正接过的人都知道:模型好不好用是一回事,接入成本是另一回事。当你想同时用上 Google Veo 3.1、MiniMax Hailuo 2.3、ByteDance Seedance 2.0,麻烦才刚开始——三家厂商意味着三套 API Key、三套鉴权约定、三种请求体格式、三份账单。光是把鉴权和密钥管理跑通,就能耗掉一两天,更别说后面还要在代码里维护三条互不兼容的调用路径。
CodeGateway 的视频生成 API 把这件事压缩成了一个端点:POST /v1/video/generate。同一套 Key、同一个接口、同一套计费口径,只靠请求体里的 model 字段切换底层模型。本文用一篇可复制粘贴的教程,带你跑通文生视频和图生视频,并把三个模型各自的参数差异讲清楚——这些差异是真实存在的契约,照着写就能少踩坑。
端点与鉴权
整个视频生成接口只有一个入口:
POST https://api.codegateway.dev/v1/video/generate鉴权走标准的 HTTP Bearer:在请求头里带上你的 API Key 即可。这和 CodeGateway 其他端点用的是同一把 Key,鉴权风格沿用 OpenAI 那套 Authorization: Bearer 习惯,能直接套进你现有的工具链——无论你用的是什么 HTTP 客户端,加一行 header 就行。
Authorization: Bearer <CODEGATEWAY_API_KEY>这里有一个必须先讲清楚的关键行为:这个端点是同步阻塞的。它不是"提交任务拿一个 job id 再去轮询"的异步模式,也没有 webhook 回调。你发起请求后,HTTP 连接会一直保持打开,直到视频真正生成完毕,再把结果直接返回给你。实测下来,veo-3.1-fast 这一档大约需要 118–136 秒。
这意味着两件事:
客户端必须设置足够长的读超时,建议 180 秒起步。默认 30 秒或 60 秒的超时一定会把请求掐断。
不要写轮询逻辑、不要找 job id、不要等 webhook——这些在这个端点上都不存在。一个请求,等两分钟,拿到成品。
成功时返回 200,响应体结构如下:
{
"created": 1733500000,
"model": "google/veo-3.1-fast",
"serving": "presigned",
"expires_at": 1733503600,
"data": [
{ "url": "https://<video-storage-url>" }
]
}视频地址在 data[0].url。这个 URL 指向 CodeGateway 的对象存储:在配置了存储凭证的情况下,返回的是一个带过期时间的预签名 GET 链接,过期时间写在 expires_at(Unix 秒)里;否则返回一个原始 URL。
注意:预签名链接会过期,请拿到后尽快把文件下载到自己这边,不要长期热链(hotlink)。一旦 expires_at 到点,链接就失效了。
输入有问题时会返回 400,错误信息在 error 字段里,例如:
{ "error": "prompt is required for google/veo-3.1-fast" }常见的 400 原因包括缺少必填的 prompt、prompt 超过 2000 字、或者传入的媒体 URL 不是 https / 指向了内部地址。
快速上手:最小文生视频调用
先跑通一个最简单的文生视频请求。我们用 google/veo-3.1-fast 做演示,请求体里只放 model 和 prompt 两个字段,其余参数全部走默认值。
下面三段代码做的是同一件事:发起请求、等待生成完成、读取 data[0].url 并把视频保存到本地。请注意每一段都把读超时设到了 180 秒。
curl
curl -X POST https://api.codegateway.dev/v1/video/generate \
-H "Authorization: Bearer $CODEGATEWAY_API_KEY" \
-H "Content-Type: application/json" \
--max-time 180 \
-d '{
"model": "google/veo-3.1-fast",
"prompt": "A calico cat stretching slowly on a sunlit wooden windowsill, gentle morning light, cinematic"
}'返回的 JSON 里 data[0].url 就是视频地址,再下载即可:
# 把上一步返回的 url 存到变量后下载
curl -o output.mp4 "$VIDEO_URL"Python(requests)
import os
import requests
API_KEY = os.environ["CODEGATEWAY_API_KEY"]
resp = requests.post(
"https://api.codegateway.dev/v1/video/generate",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"model": "google/veo-3.1-fast",
"prompt": "A calico cat stretching slowly on a sunlit wooden windowsill, gentle morning light, cinematic",
},
timeout=180, # 同步阻塞,留足读超时
)
resp.raise_for_status()
result = resp.json()
video_url = result["data"][0]["url"]
print("video url:", video_url)
print("expires_at:", result.get("expires_at"))
# 预签名链接会过期,立即下载
video = requests.get(video_url, timeout=180)
with open("output.mp4", "wb") as f:
f.write(video.content)Node / TypeScript(fetch)
import { writeFile } from "node:fs/promises";
const API_KEY = process.env.CODEGATEWAY_API_KEY!;
// 内置 fetch 默认无读超时,这里用 AbortSignal.timeout 显式设 180s
const resp = await fetch("https://api.codegateway.dev/v1/video/generate", {
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "google/veo-3.1-fast",
prompt:
"A calico cat stretching slowly on a sunlit wooden windowsill, gentle morning light, cinematic",
}),
signal: AbortSignal.timeout(180_000),
});
if (!resp.ok) {
throw new Error(`request failed: ${resp.status} ${await resp.text()}`);
}
const result = await resp.json();
const videoUrl = result.data[0].url as string;
console.log("video url:", videoUrl, "expires_at:", result.expires_at);
// 预签名链接会过期,立即下载落盘
const video = await fetch(videoUrl, { signal: AbortSignal.timeout(180_000) });
await writeFile("output.mp4", Buffer.from(await video.arrayBuffer()));三段代码的共同点:请求体最小化、读超时拉长到 180 秒、拿到 data[0].url 后立刻下载。这就是接入视频生成 API 的基本骨架,剩下的差异都在 model 和模型专属参数上。
三个模型怎么选,参数有何不同
三个模型都支持文生视频,但定位和可调参数差别不小。请求体的通用结构是:
{ "model": "<三选一>", "prompt": "...", "<模型专属字段>": ... }model 字段必须是下面三个精确字符串之一,写错一个字符都会被拒。下表先给一个选型概览,再逐个模型列参数。
模型 |
| 提供方 | 一句话定位 |
|---|---|---|---|
Veo 3.1(fast) |
| 快速档,参数精简,上手快 | |
Seedance 2.0 |
| ByteDance | 控制项最丰富,可调镜头/水印/参考素材 |
Hailuo 2.3(fast) |
| MiniMax | 快速档,可选 prompt 优化 |
选型上的简单判断:想要最少配置、快速出片,用 Veo 3.1 fast;需要精细控制(固定镜头、参考图、参考视频、起止帧)的复杂创作,用 Seedance 2.0;想要轻量且带 prompt 自动优化的,用 Hailuo 2.3 fast。
下面逐个模型把参数列清楚——字段类型严格按契约,尤其注意 duration 在三个模型里类型完全不同,resolution 的写法也不一样。
google/veo-3.1-fast
字段 | 类型 / 取值 | 默认值 | 必填 |
|---|---|---|---|
| 字符串,最长 2000 字 | — | 是 |
|
|
| 否 |
| 字符串 |
| 否 |
|
|
| 否 |
| 布尔 |
| 否 |
| base64 字符串(图生视频) | — | 否 |
要点:duration 是带 `s` 后缀的字符串,写 "6s" 而不是 6。音频默认关闭,需要才显式开。
{
"model": "google/veo-3.1-fast",
"prompt": "Aerial shot flying over autumn forest at golden hour",
"aspect_ratio": "16:9",
"duration": "8s",
"resolution": "1080p"
}bytedance/seedance-2.0
字段 | 类型 / 取值 | 默认值 | 必填 |
|---|---|---|---|
| 字符串,最长 2000 字 | — | 是 |
|
|
| 否 |
| 整数 4–12(秒) |
| 否 |
|
|
| 否 |
| 布尔 |
| 否 |
| 布尔 |
| 否 |
| 固定 24 |
| — |
| 布尔 | — | 否 |
| 字符串(url 或 base64,图生视频) | — | 否 |
| 字符串 | — | 否 |
| 字符串数组 | — | 否 |
| 字符串 | — | 否 |
| 整数 | — | 否 |
要点:duration 是整数(如 8),不是字符串;这是和 Veo 最容易混淆的地方。aspect_ratio 支持的比例最多,还能固定镜头、关水印、喂参考图/参考视频和起止帧,适合需要精细控制的场景。
{
"model": "bytedance/seedance-2.0",
"prompt": "A paper boat drifting down a rain-soaked city gutter, reflections of neon signs",
"aspect_ratio": "21:9",
"duration": 8,
"resolution": "1080p",
"camera_fixed": true
}minimax/hailuo-2.3-fast
字段 | 类型 / 取值 | 默认值 | 必填 |
|---|---|---|---|
| 字符串,最长 2000 字 | — | 否 |
| 枚举(示例用 | — | 否 |
| `"768P"` / `"1080P"`(大写 P) |
| 否 |
| 字符串(图生视频) | — | 否 |
| 布尔 |
| 否 |
| 布尔 |
| 否 |
要点:resolution 写的是大写 P("768P" / "1080P"),和另外两个模型的小写 p 不一样。Hailuo 没有 `aspect_ratio` 字段,不要往请求体里塞这个字段。prompt_optimizer 默认开启,会自动优化你的提示词。
{
"model": "minimax/hailuo-2.3-fast",
"prompt": "Slow dolly toward a steaming bowl of ramen on a wooden table",
"duration": 6,
"resolution": "1080P"
}图生视频:用一张图驱动一段视频
三个模型都支持图生视频,但承载图片的字段名各不相同:
Veo 3.1:
image_input(base64 字符串)Seedance 2.0:
image(url 或 base64)Hailuo 2.3:
first_frame_image
下面用 Seedance 2.0 演示一个完整的图生视频请求——传入一张图片 URL 作为首帧,再用 prompt 描述想要的运动。
import os
import requests
API_KEY = os.environ["CODEGATEWAY_API_KEY"]
resp = requests.post(
"https://api.codegateway.dev/v1/video/generate",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"model": "bytedance/seedance-2.0",
"prompt": "The camera slowly pushes in as the leaves begin to sway in the wind",
"image": "https://example.com/source-frame.jpg", # 必须是 https
"duration": 6,
"resolution": "1080p",
},
timeout=180,
)
resp.raise_for_status()
print(resp.json()["data"][0]["url"])换成 Veo 时,把字段改成 image_input 并传 base64 字符串;换成 Hailuo 时,把字段改成 first_frame_image。其余流程——同步等待、读 data[0].url、尽快下载——完全一致。
注意:所有以 URL 形式传入的媒体输入都必须是 https,且不能指向内部地址,否则会被 400 拒掉。
实战注意点
把这几条记住,能避开大多数线上才会暴露的问题。
prompt 上限 2000 字:三个模型的
prompt都有 2000 字上限,超了会直接400。把场景描述写精炼,别堆冗长背景。Veo 的音频默认关闭:
generate_audio默认false,是 opt-in 设计。需要带声音的视频时记得显式设为true,否则拿到的是静音片段。同步超时一定要扛住两分钟:这是最常见的踩坑点。读超时设短了,请求会在视频还没生成完时就被客户端掐断,白白浪费一次生成。统一把读超时设到 180 秒,并在上层加好重试与错误处理。
预签名 URL 会过期:
data[0].url很可能是带expires_at的预签名链接,过期即失效。拿到后第一时间下载落盘,不要把这个 URL 直接存进数据库长期当作可访问地址用。媒体输入必须 https 且非内部地址:图生视频传 URL 时,http、localhost、内网地址都会被拒。先把素材放到一个公开可访问的 https 地址上。
价格
视频生成按用量计费,三个模型共用同一套计费口径,无需分别和三家厂商对账。当前各模型的具体费率以官方价格页为准:https://www.codegateway.dev/pricing。
小结
一个端点、一套 Bearer 鉴权、一份请求体格式,就能在 Google Veo 3.1、MiniMax Hailuo 2.3、ByteDance Seedance 2.0 三个视频生成模型之间自由切换——切模型只改 model 字段,不用再维护三套 Key 和三套 SDK,账单也归到一处。
接入时记住三件事:端点是同步阻塞的,读超时设 180 秒;三个模型的 duration / resolution 类型不一样,照参数表写;返回的视频链接会过期,拿到尽快下载。
把本文的最小调用骨架复制下来,填上你自己的 Key,就能跑出第一段视频。要开始,先到 CodeGateway 创建 API Key,然后用上面的 curl 命令发出第一个 POST /v1/video/generate 请求。
