HiAPI
ブログ

チュートリアル

Happyhorse 画像から動画へのAPI:利用可能なcurlとPythonの例(hiapi)

Jun 27, 2026
Happyhorse 画像から動画へのAPI:利用可能なcurlとPythonの例(hiapi)

自分のコードから静止画像を一貫性のある短い動画に変換したいと考えているなら、hiapi の happyhorse-1.1/image-to-video モデルがまさにその役割を果たします。画像URLとオプションの動きのプロンプトを渡すと、数秒以内に 720p または 1080p の MP4 が返されます。このガイドは最短の実用ルートです。実際のリクエストボディ、実際のエラーメッセージを curl と Python の両方で示すので、フィールド名で悩むことなく今日から本番投入できます。

始める前に必要なもの

  • hiapi の API キー。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 秒に 1 回で十分です。ほとんどのクリップは 2 分以内に完了します):

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:成功するまでポーリング

これは完全に動作するスクリプトです。あなたのキーと画像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 など)をあなたの URL に POST します。when: "final" は、成功または失敗のコールバックのみを受け取ることを意味します。進捗のノイズはありません。

実際のデバッグ時間を節約できるいくつかのルール:

  • webhook を冪等にしましょう。 同じ taskId が二度届く可能性があります(ネットワークリトライは現実に起こります)。処理前にデータベースでこのタスクを確認してください。
  • 処理関数内で MP4 を再ホストしましょう。 hiapi が返す URL は expireAt フィールドを持つ一時ストレージを指します。最初の受信時に自分のバケットへストリーム保存しないと、失ってしまいます。
  • 素早く確認応答しましょう。 すぐに 2xx を返し、重い処理は非同期で行います。遅い webhook はリトライのリスクがあります。
  • ポーリングをフォールバックとして使いましょう。 taskId を遅延タスクに包み、10 分後にポーリングして webhook の欠落に備えます。これが頻発する場合は、hiapi のタスクコールバックが発火しない可能性がある理由 で一般的な原因を確認してください。

遭遇するエラーメッセージ

これらは API が返す正確な文字列です。コード内でこれらにマッチさせる方が推測より信頼できます。

状況 HTTP レスポンスボディの形式
誤った、または取り消されたキー 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)は、小さなバックオフ付きのリトライに適しています。

出力のチューニング

実際に影響する調整項目は 4 つだけです。それ以外(seed、aspect_ratio、fps、cfg_scale、negative_prompt)は現時点では未知のフィールドとして拒否されます。この集合内にとどまってください:

  • image_urls——HTTPS URL を 1 つ。出力のアスペクト比と構図はこの画像に従います。
  • prompt——短い動きの説明(「彼女は微笑んで首をかしげる」など)。映画的な、動詞駆動のプロンプトは形容詞のリストより優れています。
  • resolution——プロトタイピングやサムネイルには "720p"、納品には "1080p"。
  • duration——整数秒で 3 から 15。長いクリップはより多くの時間とコストがかかります。ストーリーを伝えられる最短の長さを選んでください。

開始フレームと終了フレームが必要なプロジェクトについては、happyhorse-1.1 reference-to-video を参照してください。ソース画像を必要としない純粋なテキストから動画への変換は、happyhorse-1.1 text-to-video が姉妹編です。残りの内容は hiapi models catalogue または ブログのレシピ欄 でご覧ください。

FAQ

最初のフレームと最後のフレームを制御するために 2 枚の画像を渡せますか? happyhorse-1.1/image-to-video ではできません。image_urls は最大 1 要素です(maxItems: 1)。これが必要な場合は happyhorse-1.1/reference-to-video を使うか、happyhorse-1.1 reference-to-video で構築してください。

実際に返ってくる MP4 の解像度は何ですか? 出力は入力画像のアスペクト比と設定した resolution フラグに従います。"1080p" は両者のうち高い方を与えます。"720p" はテストにおいてより速く安価です。

キーが他のモデルでは使えるのに 401 permission_denied になるのはなぜですか? 一部のモデルはキーごとにゲートされています。hiapi コンソール でモデルページを確認してください。happyhorse-1.1/image-to-video が許可リストにない場合は、新しいキーを生成するか、エラー内の request_id を添えてサポートに連絡してください。

ストリーミング/SSE レスポンスはありますか? ありません。すべての動画モデルはこの記事で説明した非同期タスクパターンを使用します。サーバーを制御できるかどうかで、ポーリングかコールバックを選んでください。

クリップごとの実際のコストはどこで確認できますか? hiapi の料金ページ です。コストは動画の秒数で計算されるため、渡す duration が直接コストを決めます。予算を節約するために開発/テスト用クリップは duration: 3 に保ってください。

出力 URL が 1 日後に使えなくなります。 これは想定された動作です。その URL は一時ストレージを指し、レスポンスには expireAt Unix タイムスタンプが含まれます。常にタスク成功の瞬間に MP4 をダウンロードしてください(または独自の CDN 経由でストリーミングしてください)。

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

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

APIドキュメントを読む