HiAPI
ブログ

チュートリアル

HiAPI 上的 kandinsky-6.0-pro/image-to-video 教程:curl + Python

在 HiAPI 上发起一次可用的图生视频请求:从首帧上传到异步轮询再到最终下载

Nov 3, 2026
深色底上青绿与石板灰的扁平液态色块相互交叠,构成 kandinsky-6.0-pro 视频教程的封面图。

HiAPI 上的 image-to-video 流程概览

HiAPI 上的每次图生视频调用都遵循相同的三阶段生命周期。提前了解这一点,接下来的教程就会显得按部就班,而不是玄妙莫测。

阶段 1:提交任务

你向 kandinsky-6.0-pro/image-to-video 端点发送一个 POST 请求,附上首帧图像和一段描述你期望运动的简短提示词。该响应不会返回视频字节流,而是接受任务并返回一个任务标识符——后续所有操作都靠这个句柄。

阶段 2:轮询状态

视频生成需要时间,因此该端点是异步的。你用任务标识符调用状态路由,它会报告任务的当前状态:排队中、处理中、成功或失败。你每隔一小段时间重复检查,直到状态进入终态。

阶段 3:获取输出

一旦任务报告成功,状态载荷会携带成片的位置。你下载它并写入磁盘。这就是全部契约。

为什么这种形态很重要

提交与轮询的分离并非偶然。这意味着你的 HTTP 客户端无需在整个渲染期间保持套接字打开,也意味着你可以持久化任务标识符,离开后再恢复。出错时,状态字段会告诉你哪里出错,这比一个不透明的超时有用得多。

记住这三个阶段:提交、轮询、下载。下面的 curl 和 Python 示例只是同一序列的两种写法。

准备首帧和 API 凭据

大多数首次调用之前出问题的地方只有两个:密钥和图像。现在把两者都处理好,教程的其余部分就会顺利。

将 API 密钥设为环境变量

不要把密钥粘贴到源文件中。在 shell 中导出它,让 curl 和 Python 都能获取,而无需硬编码任何内容:

export HIAPI_API_KEY="your_api_key_here"

在 Windows PowerShell 中,等价写法是 $env:HIAPI_API_KEY="your_api_key_here"。继续之前,请确认 shell 能读到它:

echo "$HIAPI_API_KEY" | head -c 4; echo

你应该能看到密钥的前几个字符。如果什么都没看到,说明变量没有持久化到当前 shell 会话——这是后续出现 401 响应的常见原因。

选择有效的首帧图像

首帧决定了整个视频的主体、构图和宽高比。几个实际约束可以避免大多数拒绝:

  • 格式:使用标准 Web 图像格式,如 JPEG 或 PNG。避免冷门编解码器、渐进式边缘情况或 CMYK 色彩配置文件。
  • 可访问性:API 需要真正读到这些字节。如果你传 URL,确保它可公开访问或已正确鉴权;如果你传内联数据,确保编码完整。
  • 尺寸:大小合理的图像验证最快。极大文件会拖慢请求,且经常是上传错误的根源。
  • 内容:选择作为运动起点合理的帧——主体清晰、背景简洁。运动提示词描述的是如何远离这一帧,因此杂乱的帧会产生杂乱的视频。

决定如何引用图像

请求体允许你通过 URL 或内联编码数据指向首帧。根据图像所在位置选择:

  • 如果文件已托管且可公开读取,URL 是最简洁的选项。
  • 如果图像在本地或私有,内联编码能让整个请求自包含,代价是载荷更大。

无论哪种方式,在 curl 和 Python 中保持图像引用使用相同的变量形态,这样两个示例保持直接可比。

用 curl 发起请求

这是一个完整的提交请求。用你自己的图像引用和提示词替换后运行。

curl -sS -X POST "https://api.hiapi.ai/v1/kandinsky-6.0-pro/image-to-video" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "image": "https://your-host.example/first-frame.jpg",
    "prompt": "被写体へのゆっくりとしたドリーイン、穏やかな環境の動き、安定したカメラ",
    "duration": 5,
    "aspect_ratio": "16:9"
  }'

各部分的作用

  • URL 专门指向 kandinsky-6.0-pro/image-to-video 模型。模型选择在路径中,而不是请求体里,因此这里拼错会得到 404,而不是友好的验证消息。
  • Authorization 携带你导出的环境变量中的 bearer 令牌。没有其他东西对调用进行鉴权。
  • Content-Type: application/json 对任何带 JSON 体的 POST 都很重要;省略它,某些代理会在载荷到达 API 之前将其弄乱。
  • image 是你的首帧引用——这里用 URL 形式,如果图像私有则用内联编码字符串。
  • prompt 描述运动。保持它关于运动和镜头行为,而不是复述图像已包含的内容。
  • duration 和 aspect_ratio 塑造输出。宽高比要与首帧匹配;不匹配会裁剪或加黑边。计费按你生成的输出计量,因此这些设置也决定你的费用。

读取响应

成功提交返回任务载荷,而不是视频。从中捕获任务标识符——稍后你会把它传给状态端点:

curl -sS -X POST "https://api.hiapi.ai/v1/kandinsky-6.0-pro/image-to-video" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d @body.json | jq -r '.id'

使用 body.json 文件代替内联 -d,可以让长提示词和编码图像保持可读。jq 是可选的,但它能把响应变成一行,可直接粘贴到下一次调用。

用 Python 调用同一端点

Python 版本是相同的请求、相同的请求体,只是包在函数里以便复用。保持请求体与 curl 版本一致——这正是同时展示两者的意义。

import os
import json
import requests

API_BASE = "https://api.hiapi.ai/v1"
ENDPOINT = f"{API_BASE}/kandinsky-6.0-pro/image-to-video"

def submit_video_job(image_ref: str, prompt: str, duration: int = 5, aspect_ratio: str = "16:9") -> str:
    """Submit a first-frame image-to-video job and return the job id."""
    api_key = os.environ.get("HIAPI_API_KEY")
    if not api_key:
        raise RuntimeError("HIAPI_API_KEY is not set in this environment")

    body = {
        "image": image_ref,
        "prompt": prompt,
        "duration": duration,
        "aspect_ratio": aspect_ratio,
    }

    response = requests.post(
        ENDPOINT,
        headers={
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json",
        },
        json=body,
        timeout=60,
    )

    if response.status_code >= 400:
        raise RuntimeError(f"submit failed: {response.status_code} {response.text}")

    payload = response.json()
    return payload["id"]

if __name__ == "__main__":
    job_id = submit_video_job(
        image_ref="https://your-host.example/first-frame.jpg",
        prompt="被写体へのゆっくりとしたドリーイン、穏やかな環境の動き、安定したカメラ",
    )
    print(json.dumps({"job_id": job_id}, indent=2))

关于 Python 版本的说明

  • 密钥缺失时快速失败。显式的环境检查把最常见的错误变成本地清晰报错,而不是远端 401。
  • json=body 与 data=json.dumps(body)。传 json= 让 requests 为你设置内容类型,从而保持请求头最简。如果改用 data=,请显式设置 Content-Type。
  • 宽松的超时。视频提交有时涉及传输大图像载荷。过短的超时会产生虚假失败。
  • 出错时读取响应体。同时抛出状态码和响应体,意味着失败消息实际告诉你 API 反对的是什么。

如果首帧是本地文件,读取它、进行 base64 编码,并用该字符串替换 submit_video_job 内的 image_ref。函数签名不变,这正是两种调用风格共享同一请求体定义的原因。

轮询任务并下载视频

已提交的任务不是成片。轮询直到任务到达终态,然后下载。

轮询循环

import time
import requests

def wait_for_video(job_id: str, interval: float = 10.0, max_wait: float = 600.0) -> str:
    """Poll the job until it finishes; return the output URL."""
    import os
    api_key = os.environ["HIAPI_API_KEY"]
    url = f"https://api.hiapi.ai/v1/kandinsky-6.0-pro/image-to-video/{job_id}"
    headers = {"Authorization": f"Bearer {api_key}"}

    deadline = time.time() + max_wait
    while time.time() < deadline:
        response = requests.get(url, headers=headers, timeout=30)
        response.raise_for_status()
        payload = response.json()
        status = payload.get("status")

        if status == "succeeded":
            return payload["output"]
        if status == "failed":
            raise RuntimeError(f"job failed: {payload.get('error', 'unknown error')}")

        time.sleep(interval)

    raise TimeoutError(f"job {job_id} did not finish within {max_wait}s")

三个细节让这个循环表现良好:

  • 按间隔退避,而不是靠运气。固定休眠约十秒是合理起点;不要在紧循环中猛击端点。
  • 处理每个终态。succeeded 给你输出,failed 给你错误消息。两者都应视为退出。只检查成功的循环会在死任务上一直转到截止时间。
  • 限制等待时间。max_wait 保证脚本结束,而不是在卡住的任务后永远运行。

下载成片

拿到输出 URL 后,流式写入磁盘,而不是把整个文件加载到内存:

def download_video(output_url: str, path: str = "result.mp4") -> str:
    with requests.get(output_url, stream=True, timeout=120) as response:
        response.raise_for_status()
        with open(path, "wb") as handle:
            for chunk in response.iter_content(chunk_size=8192):
                if chunk:
                    handle.write(chunk)
    return path

把各部分组合起来:

job_id = submit_video_job(
    image_ref="https://your-host.example/first-frame.jpg",
    prompt="被写体へのゆっくりとしたドリーイン、穏やかな環境の動き、安定したカメラ",
)
output_url = wait_for_video(job_id)
saved = download_video(output_url, "kandinsky_result.mp4")
print(f"saved to {saved}")

在生产环境中,提交成功的那一刻就持久化 job_id。如果进程重启,你可以用存储的标识符重新进入轮询循环,而不必为新的提交付费。

排查常见的请求和任务错误

大多数失败归入三类:密钥、图像或任务状态。在改动其他任何东西之前,先隔离类别。

密钥被拒绝

症状:在创建任何任务之前出现认证或授权错误。

  • 确认变量确实在当前 shell 中导出:echo "$HIAPI_API_KEY"。
  • 检查是否随密钥粘贴了空白或换行;去除它们。
  • 验证请求头拼写为 Authorization,值以 Bearer 开头并跟一个空格。
  • 如果 curl 能用但 Python 不能,从同一解释器打印 os.environ.get("HIAPI_API_KEY")——变量可能存在于你的 shell 中,但不在脚本继承的环境中。

图像被拒绝

症状:提交时出现验证错误,或任务立即失败。

  • 格式错误或冷门。重新导出为标准 JPEG 或 PNG 并重试。CMYK 配置文件和奇异编解码器是常见元凶。
  • URL 不可达。在隐私浏览器窗口中打开你的图像 URL;如果你拿不到字节,API 也拿不到。
  • 内联编码损坏。如果你在本地进行 base64 编码,先解码一次字符串并确认它是有效图像,然后再发送。
  • 宽高比不匹配。如果视频回来被裁剪或加黑边,让 aspect_ratio 与首帧对齐,而不是事后与输出较劲。

任务不结束或失败

症状:状态始终停留在 queued/processing,或报告 failed。

  • 读取 error 字段。失败任务携带消息。它通常指出出错的字段或条件。不要猜——记录整个状态载荷。
  • 尊重异步设计。等待几秒再检查是正常的。两秒后就放弃的紧重试循环不是状态机制。
  • 限制等待时间。如果任务超过合理上限,视为失败,暴露任务 id,并提交新任务,而不是永远轮询。
  • 与已知良好的调用对比。如果自定义图像失败,用本教程前面的精确 curl 加一张小公开图像和短提示词运行。如果成功,问题在你的输入;如果也失败,问题在凭据或请求形态。

成功后的下载错误

如果任务报告 succeeded 但下载失败,输出 URL 可能已过期或受访问控制。重新轮询任务以获取新的输出位置,而不是重试过期的那个。

按顺序处理这些类别——密钥、然后图像、然后任务状态——每个错误都会快速缩小到一个原因。这就是整个循环:提交首帧、轮询至完成、下载结果。

永続的なAIアーティファクトで開発を始める

APIキーを取得して、タスクベースの1つのAPIで画像・動画・音声モデルを呼び出せます。

APIドキュメントを読む