教程
如何使用 hailuo 2.3 图生视频 API:curl、Python 与一份可运行的请求示例
摘要
- 这是什么:一份在 hiapi 上使用 hailuo-2.3/image-to-video API 的可运行配方——把一张静态图片变成一段短视频:创建任务、轮询任务、下载
.mp4。 - 端点:
POST https://api.hiapi.ai/v1/tasks,请求体里写"model": "hailuo-2.3/image-to-video",然后用GET /v1/tasks/<taskId>轮询,直到任务进入终态。 - 两个必填输入字段:
prompt和image_url——指向你首帧的单个公开 URL(不是数组)。 duration是字符串,不是数字:"6"或"10"。传整数6会被 400 拒绝。- Schema 是严格的。没有
motion_strength、camera_movement或resolution参数——传了会被 400 拒绝。运动完全通过 prompt 控制。
你要构建的是什么,以及需要准备什么
你有一张静态图片——产品图、角色渲染图、照片——你想让 hailuo-2.3 把它动起来,生成一段正好从这一帧开始的视频片段。hailuo-2.3 以物理上可信的运动(布料、头发、身体力学)著称,因此非常适合让静态图片活起来。
你需要两样东西:
- 一个 hiapi API key。在 dashboard 中创建一个。Key 形如
sk-...,放在Authorization: Bearer请求头里。 - 一个可公开访问的首帧图片 URL。API 会从
image_url拉取图片,所以它必须无需认证即可访问——S3/R2/CDN 链接可以;localhost路径或已过期的签名 URL 不行。
和 hiapi 上每个生成模型一样,这个模型也走异步任务接口:你 POST 一个任务,立即拿到 taskId,之后通过轮询或回调收取结果。视频没有同步端点——片段渲染需要时间,会超过典型 HTTP 超时。
请求 schema
hailuo-2.3/image-to-video 的完整输入 schema——已对照线上 API 验证:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
prompt |
string | ✅ | 描述运动,而不是场景——场景来自你的图片。 |
image_url |
string | ✅ | 一个公开 URL,即首帧。是单数——不是 image_urls。 |
duration |
string | 可选 | "6" 或 "10"(秒)。必须是字符串。 |
prompt_optimizer |
boolean | 可选 | 让平台在生成前扩展你的 prompt。 |
Schema 是严格的:不在此表中的任何内容都会被拒绝。发送 motion_strength、camera_movement 或 resolution,你会原样收到:
{
"code": 400,
"error_code": "INVALID_REQUEST",
"message": "invalid input: <root>: additional properties 'resolution', 'camera_movement', 'motion_strength' not allowed"
}
有两个后果值得记住:
- 没有运动调节旋钮。hailuo-2.3 的运动物理只由 prompt 引导——见下面的提示词编写部分。
- 这个端点上没有分辨率或宽高比控制。不要为 API 会返回 400 的参数构建 UI。
最小可运行示例:curl
创建任务:
curl -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer sk-<your-key>" \
-H "Content-Type: application/json" \
-d '{
"model": "hailuo-2.3/image-to-video",
"input": {
"prompt": "The woman turns toward the camera and smiles; wind gently moves her hair; slow cinematic push-in",
"image_url": "https://your-cdn.example.com/first-frame.jpg",
"duration": "6",
"prompt_optimizer": true
}
}'
响应(节选)在 data.taskId 下包含任务 id:
{ "data": { "taskId": "..." } }
轮询它:
curl -s https://api.hiapi.ai/v1/tasks/<taskId> \
-H "Authorization: Bearer sk-<your-key>"
片段渲染期间,data.status 报告非终态。当它翻转为 "success" 时,视频 URL 在 data.output[0].url;如果是 "fail",data.error.code 和 data.error.message 会说明原因。
任务成功后立即下载文件。输出 URL 是签名的并带有过期时间(expireAt)——把字节存到你自己的存储中,绝不要热链任务 URL。
最小可运行示例:Python
相同流程的完整脚本——创建、轮询、下载:
import time
import requests
API_BASE = "https://api.hiapi.ai/v1/tasks"
API_KEY = "sk-<your-key>"
HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
def create_task() -> str:
payload = {
"model": "hailuo-2.3/image-to-video",
"input": {
"prompt": (
"The cat stretches, hops off the windowsill, and walks toward "
"the camera; soft afternoon light, subtle handheld camera sway"
),
"image_url": "https://your-cdn.example.com/first-frame.jpg",
"duration": "6", # string, not int: "6" or "10"
"prompt_optimizer": True,
},
}
r = requests.post(API_BASE, json=payload, headers=HEADERS, timeout=30)
task_id = (r.json().get("data") or {}).get("taskId")
if not task_id:
raise RuntimeError(f"create failed ({r.status_code}): {r.text[:300]}")
return task_id
def wait_task(task_id: str, timeout_s: int = 900, poll_s: int = 10) -> dict:
deadline = time.time() + timeout_s
while time.time() < deadline:
r = requests.get(f"{API_BASE}/{task_id}", headers=HEADERS, timeout=30)
task = r.json().get("data") or {}
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} not finished after {timeout_s}s")
def main() -> None:
task_id = create_task()
print("task created:", task_id)
task = wait_task(task_id)
url = task["output"][0]["url"]
# The URL is signed and expires -- download immediately, store the bytes yourself.
video = requests.get(url, timeout=120).content
with open("hailuo-output.mp4", "wb") as f:
f.write(video)
print("saved hailuo-output.mp4")
if __name__ == "__main__":
main()
视频任务比图片任务运行时间明显更长,所以脚本每 10 秒轮询一次,并设置 15 分钟的宽裕上限。不要比每 ~5 秒更激进地轮询——这不会让渲染更快。
编写能控制运动的 prompt
因为没有任何运动参数,prompt 就是你唯一的方向盘——而对 hailuo-2.3 来说这已经够了。三条经验法则:
- 描述运动,而不是外观。模型已经看到了你的图片。
"a beautiful woman in a red dress"浪费了 prompt;"she turns her head slowly to the left as the camera pushes in"才用好了它。 - 用平实语言说明镜头运动。
"slow push-in"、"orbit right"、"handheld sway"、"static camera"——这些应写在 prompt 文本里,而不是写在(并不存在的)camera_movement字段里。 - 拿不准时让
prompt_optimizer帮忙。设置"prompt_optimizer": true后,平台会在生成前扩展简短的 prompt——对短输入是很好的默认值。当你希望严格遵循精心设计的措辞时,就关掉它。
生产环境注意事项
优先用回调而不是轮询。创建任务时附上回调,hiapi 会把终态结果 POST 到你的端点,这样你无需保持开放循环:
{
"model": "hailuo-2.3/image-to-video",
"input": { "...": "..." },
"callback": { "url": "https://your-app.example.com/hooks/hiapi", "when": "final" }
}
"when": "final" 只在终态触发一次。轮询适合脚本和 notebook;回调才是服务器的正确形态——视频渲染足够长,一个阻塞在轮询上的 worker 是实打实的成本。
显式处理这些错误:
401且"code": "permission_denied"——key 有问题,或该 key 无权使用此模型。消息中包含一个request_id,可以发给支持团队。400 INVALID_REQUEST——schema 违规。消息很精确,例如duration: got number, want string或image_url: missing required field "image_url"。原样记录它。- 创建任务时
402——余额不足。注意视频每个任务的计费高于图片,所以一个仍能正常跑图片任务的 key 可能先开始创建视频失败。充值,或在 dashboard 中检查用量。 - 任务进入
"fail"——生成侧失败;检查data.error。特别是图生视频的一个常见原因:image_url无法拉取(私有 bucket、过期的签名 URL)。
围绕重试做到幂等。如果你的进程在创建与下载之间死掉,天真的重启会创建(并付费)第二次渲染。在 create_task 返回后立即持久化 taskId,重启时继续轮询而不是重新创建。
相关阅读
- hailuo-2.3/image-to-video 模型页——playground、按秒计费与实时 schema。
- hailuo-2.3-fast/image-to-video——低延迟档位,相同的任务工作流。
- hailuo-2.3/text-to-video——没有源图片,仅凭 prompt 生成。
- hiapi 文档——完整的任务接口说明,包括回调。
- 定价——所有模型当前的按秒视频费率。
常见问题
hailuo-2.3 图生视频支持哪些时长?
两个:"6" 和 "10" 秒,以字符串传入。任何其他值——或把相同值写成数字——都会返回 400 INVALID_REQUEST。
为什么 "duration": 6 会失败,而 "duration": "6" 可以?
Schema 把 duration 定义为字符串枚举。API 会明确告诉你:duration: got number, want string。这是此端点上最常见的 400。
我能设置输出分辨率或宽高比吗?
不能。端点不暴露 resolution 或 aspect_ratio 字段,发送其中一个就是 400——schema 会拒绝未知属性,而不是忽略它们。
如何控制镜头运动或运动强度?
通过 prompt。没有 camera_movement 或 motion_strength 参数——用平实语言写镜头和运动指令("slow push-in"、"she walks toward the camera")。
我能传多张参考图片吗?
不能——image_url 是单个字符串,即首帧。这与 hiapi 上其他一些接收 image_urls 数组的模型不同;schema 是按模型定义的,所以务必查看模型页。
首帧支持哪些图片格式? 标准 Web 格式(JPEG/PNG),从可公开访问的 URL 提供。如果平台无法拉取该 URL,任务会在创建后失败——所以提交前先验证链接可公开访问。
这和 hailuo-2.3/text-to-video 有什么不同?
文生视频仅凭你的 prompt 发明场景;图生视频把首帧钉在你的图片上并从它开始动画。任务生命周期完全相同——只有输入 schema 不同(这里 image_url 是必填的,而文生视频根本没有图片字段)。
费用是多少? 视频按输出秒数计费,费率会变化——查看 hailuo-2.3/image-to-video 模型页和定价页获取当前数字。