HiAPI
博客

教程

HappyHorse 1.1 参考生视频 API:可直接运行的 curl + Python 教程

2026年11月3日
HappyHorse 1.1 参考生视频 API:可直接运行的 curl + Python 教程

HappyHorse 1.1 R2V 可将最多九张参考图加上一段文本提示词转化为一段短视频,并在多个镜头之间保持主体、服装和场景风格一致。在 HiAPI 上,该模型通过统一的异步任务端点提供服务,对应的模型 ID 为 happyhorse-1.1/reference-to-video。本教程先演示能跑通的最小请求,再展示带回调的生产级版本。

目标与前置条件

你将完成以下操作:

  1. 将 1-9 张参考图上传(或已托管)到 API 可访问的 HTTPS URL。
  2. 向 https://api.hiapi.ai/v1/tasks POST 一个引用这些 URL 的任务。
  3. 当任务达到 success 状态后,从 data.output[0].url 中读取最终的 MP4 地址。

前置条件:

  • 一个 HiAPI 账号,以及以 sk- 开头的 API Key。你可以从 HiAPI 控制台 获取,并阅读 认证 文档了解请求头规则。密钥需以 Authorization: Bearer sk-... 形式传递,绝不可放在客户端代码中。
  • 图片托管要求:每张参考图须为 JPEG、PNG 或 WEBP 格式,最短边 ≥ 400px,≤ 20MB。公共 CDN URL 可用;签名 S3 URL 也可用,只要任务执行器能够访问。

最小可用请求(curl)

这是 schema 所能接受的最小请求体。它使用默认的 1080p / 16:9 / 5 秒输出,因此你只需设置 prompt 和 reference_image 两个字段。

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/reference-to-video",
    "input": {
      "prompt": "[Image 1] 中的角色行走在 [Image 2] 风格的场景中,电影感镜头",
      "reference_image": [
        "https://static.hiapi.ai/example/ref-1.jpg",
        "https://static.hiapi.ai/example/ref-2.jpg"
      ]
    }
  }'

响应是任务信封;你实际需要的唯一字段是 data.taskId:

{
  "code": 200,
  "data": { "taskId": "task_xxx", "status": "queued" }
}

轮询直到终态:

curl https://api.hiapi.ai/v1/tasks/task_xxx \
  -H "Authorization: Bearer sk-YOUR_KEY"

当 data.status 变为 success 时,视频 URL 位于 data.output[0].url。该 URL 带有 expireAt 时间戳,因此请立即下载文件,或 将其转存至持久存储——不要在你的网站上直接热链该地址。

参数

R2V 端点会严格校验输入;未知字段会返回 400 INVALID_REQUEST。完整 schema(已针对 POST /v1/tasks 实时验证):

字段 类型 必填 可选值 默认值
model string 是 happyhorse-1.1/reference-to-video(固定值) -
input.prompt string 是 用 [Image 1]、[Image 2]…… 引用参考图,顺序与 reference_image 一致 -
input.reference_image string[] 是 1-9 个 HTTPS URL,JPEG/PNG/WEBP 格式,最短边 ≥ 400px,每张 ≤ 20MB -
input.resolution enum 否 720p、1080p 1080p
input.aspect_ratio enum 否 16:9、9:16、3:4、4:3、4:5、5:4、1:1、9:21、21:9 16:9
input.duration integer 否 3-15(秒) 5
callback.url string 否 接收任务终态事件的 HTTPS URL -
callback.when enum 否 final(目前唯一值) final

以下几项不太直观的校验规则,在它们给你带来麻烦之前值得了解:

  • 即使只有一张参考图 URL,也要将 reference_image 作为数组传入。直接传字符串会返回 reference_image: got string, want array。
  • duration 必须是 3 到 15(含)之间的整数。999 和 1 都会返回明确的边界错误信息。
  • 顶层不存在 seed、negative_prompt、fps、cfg_scale 或 steps。API 会拒绝未知属性。

按分辨率和时长组合的完整价格,请参阅 HiAPI 定价页面——费用随 resolution 和 duration 两个参数同时变化。

Python:端到端 + 轮询

下面的代码块是一个自包含的生成器,提交任务、轮询至终态,并下载视频。它刻意做到零依赖(仅使用 requests),方便你复制到任何服务中。更完整的请求生命周期说明请参阅 快速入门。

import os
import time
import requests

API = "https://api.hiapi.ai/v1/tasks"
HEADERS = {"Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}"}


def submit_r2v(prompt: str, reference_images: list[str], duration: int = 5,
               resolution: str = "1080p", aspect_ratio: str = "16:9") -> str:
    if not 1 <= len(reference_images) <= 9:
        raise ValueError("reference_image must contain 1-9 URLs")
    payload = {
        "model": "happyhorse-1.1/reference-to-video",
        "input": {
            "prompt": prompt,
            "reference_image": reference_images,
            "resolution": resolution,
            "aspect_ratio": aspect_ratio,
            "duration": duration,
        },
    }
    r = requests.post(API, json=payload, headers=HEADERS, timeout=60)
    r.raise_for_status()
    data = r.json()
    if data.get("code") != 200:
        raise RuntimeError(f"create failed: {data}")
    return data["data"]["taskId"]


def wait_for_video(task_id: str, timeout_s: int = 600, poll_s: int = 5) -> str:
    deadline = time.time() + timeout_s
    while time.time() < deadline:
        r = requests.get(f"{API}/{task_id}", headers=HEADERS, timeout=30)
        r.raise_for_status()
        task = r.json()["data"]
        status = task["status"]
        if status == "success":
            return task["output"][0]["url"]
        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} did not finish in {timeout_s}s")


def download(url: str, path: str) -> None:
    with requests.get(url, stream=True, timeout=300) 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 = submit_r2v(
        prompt="保持 [Image 1] 中的主体设计,行走在 [Image 2] 的氛围中",
        reference_images=[
            "https://static.hiapi.ai/example/char.jpg",
            "https://static.hiapi.ai/example/mood.jpg",
        ],
        duration=5,
        resolution="720p",
        aspect_ratio="9:16",
    )
    print("submitted", task_id)
    video_url = wait_for_video(task_id)
    print("ready", video_url)
    download(video_url, "out.mp4")

这段代码有意做了以下几件事:

  • 从 HIAPI_API_KEY 环境变量读取密钥,使其永远不会进入 git 或容器日志。
  • 使用 r.raise_for_status() 在解析 JSON 之前捕获传输错误。
  • 只将 success 和 fail 视为终态——其他所有状态(queued、processing 等)都会让循环继续。
  • 立即下载输出 URL。输出 URL 是签名地址且会过期;如果你稍后需要该文件,请 将其转存至持久存储 或复制到你自己的存储桶中。

生产环境:从轮询切换为 webhook

每 5 秒轮询一次对于笔记本或低流量 worker 而言没问题。对于真实服务,请在创建任务时注册回调,让 HiAPI 将终态事件推送到你的端点——这样你的 worker 可以保持空闲,也无需再为空 GET 请求付费。完整的请求 schema 请参阅 创建任务,兜底轮询请参阅 获取任务详情。

payload = {
    "model": "happyhorse-1.1/reference-to-video",
    "input": {
        "prompt": "[Image 1] 中的角色走过 [Image 2] 中的城市街道",
        "reference_image": [
            "https://cdn.example.com/char.jpg",
            "https://cdn.example.com/street.jpg",
        ],
        "resolution": "1080p",
        "aspect_ratio": "16:9",
        "duration": 8,
    },
    "callback": {
        "url": "https://your-domain.com/hiapi/callback",
        "when": "final",
    },
}

接收端点需要接入两件事:

  1. 按 taskId 做幂等处理。 success 和 fail 都是终态,HiAPI 的重试(或你自己的重试层产生的重复)会重新投递相同的 taskId。在触发任何副作用(S3 复制、Slack 消息、计费事件)之前先去重。
  2. 兜底核对轮询。 如果你的端点宕机,回调就丢失了。请保留一个轻量的核对任务,对所有处于 processing 状态超过最坏生成时间的任务调用 GET /v1/tasks/<id>。

实际会遇到的错误

以下这些值得显式处理;其余都是暂时性的 5xx,应由 SDK 或你的 HTTP 客户端以退避方式重试。

  • 401 permission_denied——Authorization 头缺失、格式错误或密钥已被吊销。请从控制台重新签发密钥,而不是修补请求头。
  • 400 INVALID_REQUEST 并附带字段路径——错误信息会准确告诉你哪里失败,例如 reference_image: maxItems: got 10, want 9 或 aspect_ratio: value must be one of ...。请在 worker 提交前校验输入,以便在本地快速失败。
  • task failed(终态)——轮询/回调返回 status: "fail",并附带 error.code 和 error.message。常见原因:worker 无法获取的参考图 URL、触发了 NSFW 审核、或图片最短边低于 400px 阈值。

常见问题

可以引用超过 9 张图片吗? 不可以。schema 将 reference_image 限制为最多 9 张。如果你需要更多变化,请在平台外合成一张更高密度的参考图(网格图或分镜图),并作为单张输入使用。

提示词中必须使用 [Image N] 标记吗? 这些标记是你指向特定参考图的方式。如果提示词中从不指明图片,也是合法的,但模型会自由混合所有参考图——对于多镜头一致性场景,通常不是你想要的效果。

为什么收到 503 nsfw_moderation_unavailable? 这是审核服务导致的平台暂时性错误,与你的请求无关。请在短暂退避后重试。

应该用 720p 还是 1080p? 1080p 是默认值且画质更好,但费用更高、渲染更慢。对于播放器本来就会降采样的竖版社交媒体切片,720p 配合 aspect_ratio: 9:16 通常是更合适的权衡。

可以传入 seed 来使输出可复现吗? 目前不可以。API 会将 seed 视为未知属性并拒绝。如果你需要可复现性,请精确固定 prompt、reference_image、aspect_ratio、resolution 和 duration,并接受一定的运行间差异。

输出 URL 存放在哪里,有效期多久? 它们是带短 TTL 的签名 URL;响应中包含 expireAt。任务成功后请立即下载或 将其转存至持久存储。

立即开始构建持久 AI 产物

获取 API 密钥,通过一个基于任务(task)的 API 调用图像、视频和音频模型。

阅读 API 文档