教程
如何使用 hailuo 2.3 Fast 图生视频 API:curl、Python 与可运行的请求
你手上有一张静态图片,想把它变成一段短视频。本指南将通过 hiapi 任务 API 接入 hailuo-2.3-fast/image-to-video:精确的输入 schema(已对照线上校验器验证)、可直接复制的 curl 请求、一个能轮询到任务完成的完整 Python 脚本,以及生产环境细节——回调、幂等重试,还有你实际会遇到的错误响应。
你需要准备
- 一个 hiapi API 密钥——从你的控制台获取。密钥形如
sk-...,放在Authorization: Bearer请求头中。 - 一张托管在公网可访问的 HTTPS URL 上的源图片。API 接收的是 URL,不是上传文件。
- 任意 HTTP 客户端。下面的示例使用 curl 和 Python 的
requests。
该模型的按秒计费价格列在定价页面上。
输入 schema(已验证)
hailuo-2.3-fast/image-to-video 使用统一的异步任务端点:POST /v1/tasks 创建任务,GET /v1/tasks/<taskId> 轮询任务。输入 schema 很严格——未知字段会被拒绝并返回 400,而不是被静默忽略。
| 字段 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
prompt |
string | ✅ | 描述你想要的运动 |
image_url |
string | ✅ | 单数形式——一个 URL 字符串,不是 image_urls 数组 |
duration |
string | 可选 | "6" 或 "10"——是字符串,不是数字 |
prompt_optimizer |
boolean | 可选 | 让后端为运动改写你的提示词 |
校验器会抓住你的三个坑:
duration必须是字符串。 发送"duration": 6会返回400,并提示duration: got number, want string。请发送"duration": "6"。- 字段是单数形式的
image_url。 平台上许多其他视频模型(Grok Imagine、Kling)接收的是image_urls数组。这个模型接收单个字符串。 - 没有
resolution、aspect_ratio、seed、watermark或end_image_url。 它们都会返回additional properties ... not allowed。输出画幅跟随你的源图片;运动控制只能通过提示词。
最小可运行请求:curl
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer sk-YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "hailuo-2.3-fast/image-to-video",
"input": {
"prompt": "镜头缓慢推进,咖啡杯上蒸汽升腾,柔和的晨光",
"image_url": "https://your-cdn.example.com/coffee.jpg",
"duration": "6"
}
}'
成功响应:
{
"code": 200,
"data": { "taskId": "tk-hiapi-01KX4W3SVW8S8EHD5VX45JH95V" },
"message": "success"
}
然后轮询:
curl -s https://api.hiapi.ai/v1/tasks/tk-hiapi-01KX4W3SVW8S8EHD5VX45JH95V \
-H "Authorization: Bearer sk-YOUR_KEY"
视频渲染期间,data.status 为 "handling":
{
"code": 200,
"data": {
"taskId": "tk-hiapi-01KX4W3SVW8S8EHD5VX45JH95V",
"model": "hailuo-2.3-fast/image-to-video",
"status": "handling",
"storage": "temp",
"completed": 0,
"created": 1783648872
},
"message": "success"
}
终态为 "success"(视频 URL 在 data.output[0].url)和 "fail"(详情在 data.error)。
完整的 Python 脚本
import os
import time
import requests
API_BASE = "https://api.hiapi.ai/v1/tasks"
TOKEN = os.environ["HIAPI_API_KEY"] # sk-...
HEADERS = {"Authorization": f"Bearer {TOKEN}"}
def create_task(prompt: str, image_url: str, duration: str = "6") -> str:
resp = requests.post(
API_BASE,
headers={**HEADERS, "Content-Type": "application/json"},
json={
"model": "hailuo-2.3-fast/image-to-video",
"input": {
"prompt": prompt,
"image_url": image_url,
"duration": duration, # 字符串:"6" 或 "10"
},
},
timeout=60,
)
body = resp.json()
if resp.status_code != 200 or not body.get("data", {}).get("taskId"):
raise RuntimeError(f"create failed: {resp.status_code} {body}")
return body["data"]["taskId"]
def wait_task(task_id: str, timeout_s: int = 600, poll_s: int = 5) -> dict:
deadline = time.time() + timeout_s
while time.time() < deadline:
task = requests.get(f"{API_BASE}/{task_id}", headers=HEADERS, timeout=30).json()["data"]
status = task.get("status")
if status == "success":
return task
if status == "fail":
err = task.get("error") or {}
raise RuntimeError(f"task failed: {err.get('code')} {err.get('message')}")
time.sleep(poll_s)
raise TimeoutError(f"task {task_id} still running after {timeout_s}s")
if __name__ == "__main__":
task_id = create_task(
prompt="猫眨了眨眼,把头转向窗户,微风拂过它的毛",
image_url="https://your-cdn.example.com/cat.jpg",
duration="6",
)
print("task:", task_id)
task = wait_task(task_id)
video_url = task["output"][0]["url"]
print("video:", video_url)
# URL 位于带有效期的临时存储上——立即下载,不要直接外链它。
mp4 = requests.get(video_url, timeout=120).content
with open("output.mp4", "wb") as f:
f.write(mp4)
print(f"saved output.mp4 ({len(mp4)} bytes)")
这个脚本做对了两件快速拼凑的方案常忽略的事:
- 它立即下载视频。
storage: "temp"意味着输出 URL 带有有效期(expireAt)。请把字节持久化到你自己的存储;绝不要把返回的 URL 存入你的数据库。 - 它把
fail视为终态。 重新轮询失败的任务不会让它复活——请创建新任务。
生产环境注意事项
用回调代替轮询
对于脚本之外的使用场景,在创建任务时注册 webhook,完全跳过轮询循环:
{
"model": "hailuo-2.3-fast/image-to-video",
"input": { "prompt": "...", "image_url": "https://...", "duration": "6" },
"callback": { "url": "https://your-app.example.com/hooks/hiapi", "when": "final" }
}
使用 "when": "final" 时,当任务到达 success 或 fail,你的端点会被调用一次。经验法则:轮询适合 CLI、批处理任务和任何临时性场景;一旦你有一个已经在监听的服务器,回调更胜一筹——视频任务要运行几分钟,而轮询循环在那几分钟里就是一个被占用的连接槽和一个被浪费的 worker。
幂等性
创建调用本身并不幂等——每次通过校验的 POST /v1/tasks 都会创建一个新的(计费)任务。如果你的任务运行器在超时时重试,请在处理结果之前按你自己的请求 id 为键存储拿到的 taskId。重试时,如果该键已有 taskId,就轮询它,而不是创建重复任务。
你实际会遇到的错误
| 症状 | 含义 | 修复 |
|---|---|---|
401 — {"error":{"code":"permission_denied","type":"hiapi_error"}} |
密钥无效,或该密钥无法使用此模型 | 在控制台检查密钥;响应中包含供支持使用的 request_id |
400 INVALID_REQUEST — duration: got number, want string |
用了 JSON 数字而不是字符串 | "duration": "6" |
400 INVALID_REQUEST — additional properties '...' not allowed |
你发送了 schema 之外的字段 | 去掉它——消息会指明每个违规字段 |
400 INVALID_REQUEST — missing required field "prompt" / "image_url" |
输入为空或字段名错误 | 两个字段都是必填的 |
任务创建后到达 fail |
运行时失败——常见原因是后端无法抓取的图片 URL | 校验器在创建时不会抓取你的图片;请确保 URL 公网可访问 |
最后一行值得强调:创建时的校验只针对 schema。一个拼错或私有的 image_url 会给你一个看起来正常的 taskId 和一个几分钟后死掉的任务——所以要针对 fail 告警,而不只是 HTTP 错误。
相关阅读
- hailuo-2.3-fast/image-to-video 模型页面 — 演练场与按秒定价
- 图生视频 API 工作流:生产指南 — 围绕 i2v 端点的批处理、存储与重试架构
- 文生视频 vs 图生视频 API — 何时应基于源图片锚定生成
- Kling 图生视频 API 指南 — 相同的任务 API,不同的输入 schema,有用的对照
常见问题
我可以设置分辨率或宽高比吗?
不可以。hailuo-2.3-fast/image-to-video 的 schema 会把 resolution 和 aspect_ratio 当作未知字段拒绝。画幅跟随你的源图片;如果你需要特定宽高比,请先裁剪输入图片。
支持哪些时长?
恰好两种:"6" 和 "10" 秒,以字符串传入。其他任何值都会返回 400,并提示 value must be one of '6', '10'。
我可以传多张参考图片吗?
不可以——该字段是单个 image_url 字符串。接受图片数组的模型(例如 Grok Imagine 的 image_urls)使用不同的 schema;不检查就不要在模型之间复制请求体。
如何控制运动?
只能通过 prompt——没有运动强度或镜头参数。请明确描述运动(“镜头缓慢推进”、“头发在风中摇摆”)。可选地设置 "prompt_optimizer": true 让后端扩展你的提示词。
有更慢、更高质量的变体吗?
有——hailuo-2.3/image-to-video(不带 -fast)也已上线平台,hailuo-2.3/text-to-video 覆盖无源图片的场景。切换前请检查每个变体的 schema;输入字段不保证一致。
费用是多少? 计费按生成视频的秒数计算,并因模型而异;请查看定价页面上的当前费率。
试用需要 API 密钥吗?
需要——所有任务 API 调用都需要 Bearer sk-... 密钥。你可以在控制台创建一个,并先在模型页面演练场中测试。