教程
如何通过 HiAPI API 调用 veo-3.1-lite/text-to-video:curl 与 Python
可直接复制的请求示例、提示词生成视频的流程,以及异步轮询模式
veo-3.1-lite/text-to-video 能为你提供什么
veo-3.1-lite/text-to-video 是 HiAPI 上的一个文生视频端点:你发送一段文本提示词,模型返回一段生成的视频片段。名字中的 "lite" 是你选择模型时最关键的信号。轻量版层级是为吞吐量和快速迭代而打造的,而不是追求最高保真度,因此当你需要大量短片段、在决定采用更重的模型之前先做功能原型,或者该片段只是更大自动化流水线中的一环、单片段延迟和批量规模比电影级质量更重要时,它是一个很好的默认选择。
当片段本身就是产品时——比如主视觉镜头、营销素材,或任何会被人逐帧审视的内容——就应该改用更重的文生视频模型。而当片段只是一个组成部分时——比如草稿预览、背景循环、故事板粗略稿,或之后还要精修的首轮渲染——就选 veo-3.1-lite/text-to-video。
这个端点的两个特性决定了后文的所有内容:
- 它是异步的。 一次生成请求不会在同一个响应里直接给你一个可播放的文件。你提交一个任务,收到对该任务的引用,然后轮询直到任务完成并且输出 URL 可用。
- 它由提示词驱动。 这个流程中没有输入图像或源视频——你发送的文本就是完整的创意简报,因此提示词质量是你掌握的主要杠杆。
本指南的其余部分是一套可操作的演练:构建请求、用 curl 提交、移植到 Python、正确轮询,并处理你实际会遇到的失败情况。
前提条件与身份验证
在首次调用之前,请确保你具备以下条件:
- 来自你 HiAPI 账户的 API 密钥。 请像对待数据库密码一样谨慎地将其视为机密。
- 一种发送 HTTP 请求的方式: 用
curl做快速检查,用带requests库的 Python 3 处理任何脚本化任务。 - 一个存放最终片段的位置, 因为输出会以一个 URL 的形式到达,你需要下载它。
密钥会在每个请求中通过 HTTP 头传递。请严格照抄所示的头名称和 Bearer 前缀;头缺失或格式错误是首次调用失败最常见的原因。
让密钥远离源代码管理
绝不要把真实密钥粘贴到你会提交的文件中。改用环境变量:
export HIAPI_API_KEY="your-key-here"
然后在运行时读取它。如果你在团队或 CI 流水线中工作,请将密钥存放在机密管理器中,并在部署时作为环境变量注入。如果密钥曾经暴露——比如意外提交、粘贴到工单中、打印到日志里——请立即轮换,而不是指望没人注意到。
在开始调试其他任何东西之前,先快速检查环境变量是否已设置:
test -n "$HIAPI_API_KEY" && echo "key is set" || echo "key is missing"
一个可用的 curl 请求
下面是文生视频提交请求的形态。请将占位符端点基地址替换为你的 HiAPI 参考文档中记录的地址,并严格保持模型标识符不变:veo-3.1-lite/text-to-video。
curl -X POST "$HIAPI_BASE_URL/v1/videos/generations" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "veo-3.1-lite/text-to-video",
"prompt": "A slow dolly-in on a rain-soaked neon sign at night, shallow depth of field, gentle camera drift, cinematic color"
}'
成功的提交不会返回视频字节。它会返回一个任务对象:包含该任务的标识符、一个初始状态为 queued 或 processing 之类的 status 字段,以及关于创建时间的元数据。该标识符是你后续一切操作的句柄——你需要轮询它,直到状态变为终态,只有那时响应才会携带生成片段的 URL。
如果你是手动测试,请捕获完整响应,而不是只用眼睛看:
curl -s -X POST "$HIAPI_BASE_URL/v1/videos/generations" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"veo-3.1-lite/text-to-video","prompt":"A paper boat drifting down a sunlit gutter, macro lens"}' \
| tee submission.json
有两个习惯值得现在就养成。第一,始终发送 Content-Type: application/json;一个看起来像 JSON 但未声明为 JSON 的请求体会被拒绝。第二,如果调用失败,请先阅读错误响应体再改动任何东西——HiAPI 会返回结构化消息,通常比仅看状态码能更精确地指出问题(密钥错误、模型未知、字段缺失)。
如果这里返回非 2xx 状态码,说明任务从未被接受。不要在失败的提交上轮询;先修复请求。
用 Python 实现同样的请求
curl 调用可以直接移植。下面的脚本提交提示词、检查提交是否成功,并保存任务引用供轮询步骤使用。
import os
import requests
BASE_URL = os.environ["HIAPI_BASE_URL"]
API_KEY = os.environ["HIAPI_API_KEY"]
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
payload = {
"model": "veo-3.1-lite/text-to-video",
"prompt": (
"A single lantern floating on still black water, "
"soft ripples, warm light against cool darkness, slow push-in"
),
}
response = requests.post(
f"{BASE_URL}/v1/videos/generations",
headers=headers,
json=payload,
timeout=30,
)
if response.status_code >= 400:
raise RuntimeError(f"Submission failed: {response.status_code} {response.text}")
job = response.json()
job_id = job["id"]
print(f"submitted job {job_id} with status {job.get('status')}")
该代码片段中有几个有意的选择:
- 使用
json=payload而不是手工拼接字符串。 让库负责序列化并设置内容类型;这样可避免一整类引号相关的 bug。 - 显式设置
timeout。 不设置的话,连接停滞会让脚本无限挂起。提交调用很快——即使在这里设置宽松的超时,也仍然很短。 - 非 2xx 时硬失败。 立即抛出异常能让错误紧贴其成因,而不是让一个损坏的任务 ID 传播到轮询循环中。
- 密钥来自
os.environ。 文件本身不写入任何机密内容。
把它封装成一个返回 job_id 的函数,你就拥有了任何流水线的提交部分。
轮询异步任务并下载片段
视频生成需要时间。提交调用在一两秒内返回;实际渲染则不然。如果你编写的代码假设第一个响应包含视频 URL,它就会失败——不是因为 API 坏了,而是因为你读取的是一个尚未完成的任务。
正确的模式是有界轮询:在检查之间休眠,随着等待时间增长而退避,并在达到最大尝试次数后放弃,而不是无限循环。
import time
import requests
def wait_for_video(job_id, headers, base_url, max_attempts=60, initial_delay=5):
delay = initial_delay
for attempt in range(1, max_attempts + 1):
r = requests.get(
f"{base_url}/v1/videos/generations/{job_id}",
headers=headers,
timeout=30,
)
if r.status_code >= 400:
raise RuntimeError(f"Poll failed: {r.status_code} {r.text}")
data = r.json()
status = data.get("status")
print(f"attempt {attempt}: {status}")
if status == "completed":
return data
if status in {"failed", "cancelled"}:
raise RuntimeError(f"Job {job_id} ended as {status}: {data}")
time.sleep(delay)
delay = min(delay * 1.5, 30)
raise TimeoutError(f"Job {job_id} did not finish within polling budget")
当任务达到 completed 时,响应会携带输出位置——即已完成片段的 URL。如果片段很大,请下载它,而不是将其流式读入内存:
result = wait_for_video(job_id, headers, BASE_URL)
video_url = result["output"]["url"]
with requests.get(video_url, stream=True, timeout=120) as r:
r.raise_for_status()
with open(f"{job_id}.mp4", "wb") as f:
for chunk in r.iter_content(chunk_size=1 << 16):
f.write(chunk)
print("saved", f"{job_id}.mp4")
生产环境中有两个细节很重要。第一,输出 URL 可能有时间限制,因此请及时下载,而不是保存链接几天后再去获取——请持久化文件,或复制到你自己的存储中。第二,在每个产物旁边记录任务 ID。当下游出现问题时,任务 ID 能让你将自己的记录与 API 实际返回的内容进行核对。
常见错误及修复方法
大多数首次运行的失败都属于少数几类。下面是每一类的表现以及需要改动的地方。
身份验证失败
提交时返回 401 或 403 表示密钥未被接受。请按顺序检查:该 shell 或进程中环境变量是否确实有值;头名称是否为 Authorization;Bearer 前缀是否存在且后面有一个空格;密钥是否已被轮换或撤销。一个密钥在你的终端中可用但在 CI 中失败,几乎总是意味着机密未被注入该环境。
模型标识符无效
返回 400 或 404 并指出模型名称,说明字符串不匹配。标识符是 veo-3.1-lite/text-to-video——斜杠和版本号都是它的一部分。请复制粘贴,不要重新输入;一个多余的空格或改动过的版本号都足以导致失败。
提示词格式错误或为空
空的 prompt、缺少 prompt 字段,或发送请求体时未带 Content-Type: application/json,都会在生成开始前被拒绝。如果你动态构建提示词,请在调用失败时记录你发送的确切字符串——产生空白或截断提示词的模板 bug 很常见,而且在打印出来之前是不可见的。
轮询超时
如果 wait_for_video 用尽了预算,不要立即重新提交。任务可能只是仍在运行。再检查一次状态,确认你的轮询间隔没有长到把预算都花在休眠上,并确认预算对请求的片段长度来说是现实的。每次超时都重新提交,正是你意外为同一片段多次付费的原因。
速率限制
429 表示需要退避。轮询循环中的指数延迟已经在此有所帮助;对提交也应用同样的克制,并将工作排队,而不是突发式发送。如有疑问,请先遵守响应头中的任何重试提示,再按自己的退避计划执行。
生产前的成本与迭代建议
视频生成按输出计费——即你产出内容的长度——因此代价高昂的错误是用长片段来测试小想法。最省钱的工作流是那种从不生成你不需要的片段的工作流。
测试提示词结构,而不是最终输出。 针对该端点支持的最短时长来编写和优化提示词。一旦构图、运动和镜头语言在短片段中读取正确,再为最终渲染增加时长。大多数提示词迭代关乎措辞,而非长度,短渲染同样能回答措辞问题。
保持提示词日志。 记录每次测试的提示词、时长和你自己的评判。十次有记录的迭代比五十次无记录的迭代更能让你了解这个模型对什么有反应,而且你不会再重复测试已经否决的提示词。
有意识地批处理。 如果你需要许多片段,请将它们排队,并在速率限制内并发运行——但先用少量提示词试跑整个批次。在提交两百个任务之后才发现系统性的提示词 bug,是最可避免的成本。
克制地轮询。 频繁轮询不会让渲染更快完成,还可能把你推入速率限制。适度的初始间隔加上温和的退避是正确的默认值。
尽早且响亮地失败。 上面代码中的每个防护——提交时非 2xx 抛出异常、轮询中检测终态失败、有界尝试次数——都是为了防止你为同一项工作付费两次。请在生产中保留它们。
从一个短提示词和 curl 请求开始,端到端确认任务生命周期,然后将同样的流程移入你的 Python 流水线。完整的端点参考位于 HiAPI 关于 veo-3.1-lite/text-to-video 的模型文档页面。