HiAPI
ブログ

チュートリアル

hailuo 2.3 Fast 画像-to-動画 API の使い方:curl、Python、実行可能なリクエスト

Jul 10, 2026
hailuo 2.3 Fast 画像-to-動画 API の使い方:curl、Python、実行可能なリクエスト

手元に静止画像があり、それを短い動画にしたいとします。このガイドでは hiapi タスク API を通じて hailuo-2.3-fast/image-to-video に接続します。正確な入力スキーマ(オンラインのバリデータで検証済み)、そのままコピーできる curl リクエスト、タスク完了までポーリングできる完全な Python スクリプト、そして本番環境の詳細——コールバック、冪等なリトライ、実際に遭遇するエラーレスポンス——を扱います。

準備するもの

  • hiapi API キー——コンソールから取得します。キーは sk-... の形式で、Authorization: Bearer リクエストヘッダーに含めます。
  • 公的にアクセス可能な HTTPS URL 上でホストされたソース画像。API が受け取るのは URL であり、ファイルのアップロードではありません。
  • 任意の HTTP クライアント。以下の例では curl と Python の requests を使用します。

このモデルの秒単位の課金価格は料金ページに記載されています。

入力スキーマ(検証済み)

hailuo-2.3-fast/image-to-video は統一された非同期タスクエンドポイントを使用します:POST /v1/tasks でタスクを作成し、GET /v1/tasks/<taskId> でタスクをポーリングします。入力スキーマは厳格で、未知のフィールドは黙って無視されるのではなく、400 を返して拒否されます。

フィールド 型 必須 説明
prompt string ✅ 望む動きを記述する
image_url string ✅ 単数形——1 つの URL 文字列であり、image_urls 配列ではない
duration string 任意 "6" または "10"——数値ではなく文字列
prompt_optimizer boolean 任意 バックエンドに動きのためのプロンプトを書き換えさせる

バリデータが捕まえる 3 つの落とし穴:

  1. duration は文字列でなければならない。 "duration": 6 を送ると 400 が返り、duration: got number, want string と表示されます。"duration": "6" を送ってください。
  2. フィールドは単数形の image_url。 プラットフォーム上の他の多くの動画モデル(Grok Imagine、Kling)は image_urls 配列を受け取ります。このモデルは単一の文字列を受け取ります。
  3. 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)")

このスクリプトは、素早く継ぎ合わせた実装が見落としがちな 2 つのことを正しく行っています:

  • 動画をすぐにダウンロードする。 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 に到達したとき、エンドポイントが 1 回呼び出されます。経験則:ポーリングは CLI、バッチ処理、その他あらゆる一時的な用途に適しています。すでに待ち受けているサーバーがある場合はコールバックの方が優れています——動画タスクは数分間実行され、ポーリングループはその数分間、接続スロットを 1 つ占有し、ワーカーを 1 つ浪費するからです。

冪等性

作成呼び出し自体は冪等ではありません——検証を通るたびに POST /v1/tasks は新しい(課金対象の)タスクを作成します。タスクランナーがタイムアウトでリトライする場合は、結果を処理する前に、取得した taskId を独自のリクエスト id をキーとして保存してください。リトライ時、そのキーにすでに 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 スキーマ外のフィールドを送信した それを削除する——メッセージが違反している各フィールドを指し示す
400 INVALID_REQUEST — missing required field "prompt" / "image_url" 入力が空、またはフィールド名が誤っている 両方のフィールドが必須
タスク作成後に fail に到達 実行時の失敗——よくある原因はバックエンドが取得できない画像 URL バリデータは作成時に画像を取得しない。URL が公的にアクセス可能であることを確認する

最後の行は強調に値します:作成時の検証はスキーマのみを対象とします。スペルミスやプライベートな image_url は、一見正常な taskId と、数分後に死ぬタスクを返します——ですから HTTP エラーだけでなく、fail に対してもアラートを出すべきです。

関連記事

よくある質問

解像度やアスペクト比を設定できますか? いいえ。hailuo-2.3-fast/image-to-video のスキーマは resolution と aspect_ratio を未知のフィールドとして拒否します。画角はソース画像に従います。特定のアスペクト比が必要な場合は、入力画像を事前にトリミングしてください。

対応している長さは? ちょうど 2 つ:"6" と "10" 秒で、文字列として渡します。それ以外の値は 400 を返し、value must be one of '6', '10' と表示されます。

複数の参照画像を渡せますか? いいえ——そのフィールドは単一の image_url 文字列です。画像配列を受け入れるモデル(例えば Grok Imagine の image_urls)は異なるスキーマを使用します。確認せずにモデル間でリクエストボディをコピーしないでください。

動きを制御するには? prompt によってのみ可能です——動きの強さやカメラのパラメータはありません。動きを明示的に記述してください(「カメラがゆっくりと寄る」、「髪が風に揺れる」)。任意で "prompt_optimizer": true を設定すると、バックエンドがプロンプトを拡張します。

より遅く、高品質なバリエーションはありますか? はい——hailuo-2.3/image-to-video(-fast なし)もプラットフォームに公開されており、hailuo-2.3/text-to-video はソース画像なしのシナリオをカバーします。切り替える前に各バリエーションのスキーマを確認してください。入力フィールドが一致する保証はありません。

料金はいくらですか? 課金は生成された動画の秒数に基づいて計算され、モデルごとに異なります。現在の料金は料金ページを確認してください。

試用に API キーは必要ですか? はい——すべてのタスク API 呼び出しには Bearer sk-... キーが必要です。コンソールで作成でき、まずモデルページのプレイグラウンドでテストできます。

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

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

APIドキュメントを読む