教程
Happyhorse 图生视频 API:可用的 curl 和 Python 示例(hiapi)
你想从自己的代码里把一张静态图片变成一段连贯的短视频。hiapi 上的 happyhorse-1.1/image-to-video 模型正是做这件事的:传入一个图片 URL 和一个可选的动态提示词,几秒钟内就会返回 720p 或 1080p 的 MP4。这份指南是最短的可用路径——真实的请求体、真实的错误信息,curl 和 Python 都有——这样你今天就能上线,而不用在字段名上折腾。
开始前你需要准备什么
- 一个 hiapi API key。从 hiapi 控制台 获取——它看起来像
sk-...,放在Authorization: Bearer ...请求头里。 - 一个可访问的图片 URL(公网 HTTPS)。本地文件不行;模型会自己去拉取这个 URL。
- 用文字表达的运动意图(可选但推荐)。动词越清晰,运动效果越好。
模型按视频秒数计价,价格列在 hiapi 定价页;其余模型目录在 hiapi models——在提高批量之前,两处都看一下。
最小的可用请求(curl)
hiapi 上的每个视频都走异步的 /v1/tasks 端点:你先 POST 一个创建请求,拿回一个 taskId,然后要么轮询 GET /v1/tasks/{taskId},要么在完成时接收回调。
创建任务:
curl -X POST "https://api.hiapi.ai/v1/tasks" \
-H "Authorization: Bearer sk-your-key" \
-H "Content-Type: application/json" \
-d '{
"model": "happyhorse-1.1/image-to-video",
"input": {
"image_urls": ["https://example.com/your-still.jpg"],
"prompt": "the cat slowly turns its head toward the camera and blinks",
"resolution": "1080p",
"duration": 5
}
}'
成功的响应长这样:
{
"code": 200,
"data": { "taskId": "tk-hiapi-01KW3D..." },
"message": "success"
}
现在轮询结果(每 5 秒一次足够——大多数片段两分钟内完成):
curl "https://api.hiapi.ai/v1/tasks/tk-hiapi-01KW3D..." \
-H "Authorization: Bearer sk-your-key"
运行期间你会得到 "status": "handling"。完成时你会得到:
{
"code": 200,
"data": {
"status": "success",
"model": "happyhorse-1.1/image-to-video",
"output": [
{
"type": "video",
"url": "https://temp.hiapi.ai/.../01KW....mp4",
"expireAt": 1783131245
}
],
"taskId": "tk-hiapi-01KW3D..."
}
}
那个 url 就是你要下载的——而且你应该立刻下载,因为 expireAt 是一个 Unix 时间戳,标记临时存储何时会删除它。
Python:轮询直到成功
这是一个完整、可运行的脚本——填入你的 key 和图片 URL 即可。
import os
import time
import requests
API = "https://api.hiapi.ai/v1/tasks"
TOKEN = os.environ["HIAPI_KEY"] # sk-...
def create_video(image_url: str, prompt: str, duration: int = 5,
resolution: str = "1080p") -> str:
"""Submit an image-to-video task. Returns the taskId."""
r = requests.post(
API,
headers={"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json"},
json={
"model": "happyhorse-1.1/image-to-video",
"input": {
"image_urls": [image_url], # exactly one URL
"prompt": prompt, # optional
"resolution": resolution, # "720p" or "1080p"
"duration": duration, # int, 3..15
},
},
timeout=60,
)
body = r.json()
if body.get("code") != 200:
raise RuntimeError(f"create failed: {body}")
return body["data"]["taskId"]
def wait_for_video(task_id: str, timeout_s: int = 600,
poll_s: int = 5) -> str:
"""Block until the task succeeds, fails, or times out. Returns the MP4 URL."""
deadline = time.time() + timeout_s
while time.time() < deadline:
r = requests.get(
f"{API}/{task_id}",
headers={"Authorization": f"Bearer {TOKEN}"},
timeout=30,
)
task = r.json().get("data") or {}
status = task.get("status")
if status == "success":
return task["output"][0]["url"]
if status == "fail":
raise RuntimeError(f"task failed: {task.get('error')}")
time.sleep(poll_s)
raise TimeoutError(f"task {task_id} did not finish in {timeout_s}s")
def download(url: str, path: str) -> None:
with requests.get(url, stream=True, timeout=120) as r:
r.raise_for_status()
with open(path, "wb") as f:
for chunk in r.iter_content(1 << 16):
f.write(chunk)
if __name__ == "__main__":
task_id = create_video(
image_url="https://example.com/your-still.jpg",
prompt="the dancer spins once and ends in a pose",
duration=5,
resolution="1080p",
)
print("submitted:", task_id)
mp4_url = wait_for_video(task_id)
download(mp4_url, "out.mp4")
print("saved out.mp4")
整个东西约 50 行,就是全部生产可用的接口面——没有 SDK,没有后台队列,没有流式协议。用 HIAPI_KEY=sk-... python video.py 运行它。
生产环境:用回调代替轮询
轮询对于一次性任务和测试没问题。对于服务器负载,你希望平台在片段就绪时回调你——省掉循环,无需额外工作即可扩展,而且请求流会立即返回。
在请求体的根层级(不是 input 内部)添加一个 callback 对象:
curl -X POST "https://api.hiapi.ai/v1/tasks" \
-H "Authorization: Bearer sk-your-key" \
-H "Content-Type: application/json" \
-d '{
"model": "happyhorse-1.1/image-to-video",
"input": {
"image_urls": ["https://example.com/still.jpg"],
"prompt": "slow zoom out from the subject",
"resolution": "1080p",
"duration": 6
},
"callback": {
"url": "https://your-app.example.com/webhooks/hiapi",
"when": "final"
}
}'
当任务到达终态时,hiapi 会把你本可以轮询到的那个任务对象(status、output[].url、error 等)POST 到你的 URL。when: "final" 意味着你只会收到成功或失败的回调——没有进度噪音。
几条能省下真实调试时间的规则:
- 让 webhook 幂等。 同一个
taskId可能到达两次(网络重试是真实存在的)。处理前先在数据库里查一下这个任务。 - 在处理函数内重新托管 MP4。 hiapi 给你的 URL 指向临时存储,带有一个
expireAt字段;在第一次收到时就把它流式存入你自己的 bucket,否则你会丢失它。 - 快速确认。 快速返回
2xx,然后异步做重活。慢 webhook 有重试风险。 - 用轮询作为兜底。 把
taskId包进一个延迟任务,10 分钟后再轮询,以防 webhook 丢失。如果你一直遇到这个问题,查看 hiapi 任务回调可能不触发的原因 了解常见原因。
你会遇到的错误信息
这些是 API 返回的确切字符串;在你的代码里匹配它们比猜测更可靠。
| 情况 | HTTP | 响应体形态 |
|---|---|---|
| 错误或已撤销的 key | 401 | {"error":{"code":"permission_denied","message":"This API key cannot use the selected model...","type":"hiapi_error"}} |
| 缺少必填字段 | 400 | {"code":400,"error_code":"INVALID_REQUEST","message":"invalid input: image_urls: missing required field \"image_urls\""} |
resolution 无效 |
400 | ... resolution: value must be one of '720p', '1080p' |
| 时长超出范围 | 400 | ... duration: minimum: got 2, want 3 / duration: maximum: got 30, want 15 |
image_urls 长度错误 |
400 | ... image_urls: maxItems: got 2, want 1(或 minItems: got 0, want 1) |
根层级或 input 内有未知字段 |
400 | ... additional properties 'identity_preserving' not allowed |
| 提交后任务本身失败 | 200(任务 GET)且 data.status == "fail" 以及 data.error.{code,message} |
认证和验证错误意味着你不应该重试;其他所有情况(平台的 5xx、轮询途中的网络超时、带有可重试代码的暂时性 task fail)都适合做小幅退避重试。
调优输出
真正有影响的旋钮有四个——其他任何东西(seed、aspect_ratio、fps、cfg_scale、negative_prompt)目前都会作为未知字段被拒绝。请留在这个集合内:
image_urls——一个 HTTPS URL。输出的宽高比和构图跟随这张图片。prompt——简短的动态描述("she smiles and tilts her head")。电影感的、以动词驱动的提示词胜过形容词列表。resolution——"720p"用于原型制作和缩略图,"1080p"用于交付。duration——整数秒,从3到15。更长的片段花费更多时间和金钱;选择能讲清故事的最短时长。
需要起始帧和结束帧的项目,请参阅 happyhorse-1.1 reference-to-video。不需要源图片的纯文生视频,happyhorse-1.1 text-to-video 是姊妹篇。在 hiapi models catalogue 或 博客的配方栏目 浏览其余内容。
FAQ
我能否传两张图片来控制首帧和尾帧?
在 happyhorse-1.1/image-to-video 上不行——image_urls 最多一个元素(maxItems: 1)。如果你需要这个,使用 happyhorse-1.1/reference-to-video,或者用 happyhorse-1.1 reference-to-video 构建。
我实际拿回的 MP4 是什么分辨率?
输出遵循你输入图片的宽高比和你设置的 resolution 标志。"1080p" 给出两者中更高的那一个;"720p" 对测试来说更快更便宜。
为什么我的 key 在其他模型上能用,却得到 401 permission_denied?
有些模型是按 key 门控的。在 hiapi 控制台 查看模型页面——如果 happyhorse-1.1/image-to-video 不在你的允许列表里,生成一个新 key 或带上错误里的 request_id 联系支持。
有流式/SSE 响应吗? 没有。每个视频模型都使用本文中的异步任务模式。根据你是否控制服务器来选择轮询或回调。
我在哪里看到每个片段的实际成本?
在 hiapi 定价页。成本按视频秒数计,所以你传入的 duration 直接决定花费——把开发/测试片段保持在 duration: 3 以节省预算。
输出 URL 一天后就不能用了。
这是预期行为——该 URL 是临时存储,响应包含一个 expireAt Unix 时间戳。始终在任务成功的那一刻下载 MP4(或通过你自己的 CDN 流式传输)。