HiAPI
ブログ

チュートリアル

Happyhorse 1.1 テキスト・トゥ・ビデオ API:実行可能な curl と Python チュートリアル

Jun 26, 2026
Happyhorse 1.1 テキスト・トゥ・ビデオ API:実行可能な curl と Python チュートリアル

このページでは、happyhorse-1.1/text-to-video を使って動画を生成する最小限のリクエストを最後まで実行し、各入力フィールドが実際に受け付ける値と、結果を待つ2つの方法(ポーリングとコールバック)について説明します。以下の内容はすべて実際の API に対して検証済みで、リクエスト構造とエラーメッセージは原文をそのまま掲載しています。

構築するもの

テキストプロンプトを受け取り、ダウンロード可能な MP4 の URL を返す関数です。モデルは happyhorse-1.1/text-to-video で、統一された /v1/tasks エンドポイント経由で提供されます。タスクを送信するとすぐに taskId が返り、その後は完了までポーリングするか、hiapi に最終結果を webhook へ POST させます。

前提条件:

  • hiapi の API キー。新規アカウントには 1 ドルの無料クレジットが付与されます——API Keys ページで取得してください。
  • HTTPS リクエストを送信する手段。SDK のインストールは不要です。

キーをクライアントコードに入れたり、git にコミットしたりしないでください。環境変数から読み込みます:

export HIAPI_API_KEY="sk-..."

最小限の動作するリクエスト

必須の入力フィールドは prompt だけです。その他のフィールドにはすべてデフォルト値があります。

curl

curl -X POST https://api.hiapi.ai/v1/tasks \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "happyhorse-1.1/text-to-video",
    "input": {
      "prompt": "a calm ocean wave at sunset, slow motion, golden hour"
    }
  }'

レスポンス:

{
  "code": 200,
  "data": { "taskId": "tk-hiapi-01KW23VDG2EZ20J7RQH8S67MNC" },
  "message": "success"
}

この taskId がタスクを操作するための資格情報です。大切に保管してください。

Python(標準ライブラリのみ)

import os
import urllib.request
import json

API_KEY = os.environ["HIAPI_API_KEY"]

def submit_task(prompt: str, **input_overrides) -> str:
    body = {
        "model": "happyhorse-1.1/text-to-video",
        "input": {"prompt": prompt, **input_overrides},
    }
    req = urllib.request.Request(
        "https://api.hiapi.ai/v1/tasks",
        method="POST",
        headers={
            "Authorization": f"Bearer {API_KEY}",
            "Content-Type": "application/json",
        },
        data=json.dumps(body).encode("utf-8"),
    )
    with urllib.request.urlopen(req) as resp:
        payload = json.loads(resp.read())
    if payload.get("code") != 200:
        raise RuntimeError(payload)
    return payload["data"]["taskId"]

task_id = submit_task("a calm ocean wave at sunset, slow motion, golden hour")
print(task_id)  # e.g. tk-hiapi-01KW23VDG2EZ20J7RQH8S67MNC

完全なパラメータ表

パラメータ構造は小さく厳格です——未知のフィールドは黙って無視されるのではなく、400 で拒否されます。以下は API が実際に受け付ける input 内のフィールドで、バリデーションエラーメッセージから直接引用したものです:

フィールド 型 必須 許可値 デフォルトの挙動
prompt string はい シーンを説明する任意の非空テキスト —
aspect_ratio string いいえ 16:9, 9:16, 3:4, 4:3, 4:5, 5:4, 1:1, 9:21, 21:9 モデルのデフォルト値
resolution string いいえ 720p, 1080p モデルのデフォルト値
duration integer いいえ 3 から 15(秒) モデルのデフォルト値

当然のように見えても、現時点では 受け付けられない フィールド:size、audio、seed、negative_prompt。これらいずれかを送ると additional properties '<field>' not allowed が返ります。他の hiapi モデルを使ったことがある場合——qwen-image-2.0 は size を使い、z-image は aspect_ratio を使います——happyhorse-1.1/text-to-video は明示的なサイズではなくアスペクト比の系統に属することに注意してください。

サポートされるすべてのパラメータを使ったリクエスト:

curl -X POST https://api.hiapi.ai/v1/tasks \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "happyhorse-1.1/text-to-video",
    "input": {
      "prompt": "a vintage paper plane gliding above neon Tokyo at night, cinematic, 24fps",
      "aspect_ratio": "16:9",
      "resolution": "1080p",
      "duration": 8
    }
  }'

コストと尺について

このモデルは生成される動画の秒数で課金されます——現在の料金は 料金ページを参照してください。コストを左右するのは duration であり、resolution ではありません。5 秒から 15 秒にすると費用は3倍になりますが、本稿執筆時点では 720p から 1080p にしても変わりません。開発中は duration: 3 と低い解像度をデフォルトにし、最終確定したプロンプトでのみ 1080p に上げてください。

結果の取得:ポーリング

出力を取り戻す最も簡単な方法は、status が終端状態になるまで id でタスクを GET することです。

import time
import urllib.request
import json

def get_task(task_id: str) -> dict:
    req = urllib.request.Request(
        f"https://api.hiapi.ai/v1/tasks/{task_id}",
        headers={"Authorization": f"Bearer {API_KEY}"},
    )
    with urllib.request.urlopen(req) as resp:
        return json.loads(resp.read())["data"]

def wait_for_task(task_id: str, timeout_s: int = 600, poll_interval_s: int = 10) -> dict:
    deadline = time.time() + timeout_s
    while time.time() < deadline:
        task = get_task(task_id)
        status = task["status"]
        if status == "success":
            return task
        if status == "fail":
            raise RuntimeError(f"task failed: {task}")
        time.sleep(poll_interval_s)
    raise TimeoutError(f"task {task_id} still {status} after {timeout_s}s")

task = wait_for_task(submit_task("a quiet coffee shop on a rainy afternoon"))
video_url = task["output"][0]["url"]
print(video_url)

いくつかの重要なポイント:

  • status は queued → handling → archiving → success(または fail)の順に遷移します。success または fail より前の状態は終端と見なすべきではありません。
  • 生成時間は duration と現在のキューの深さに応じて長くなります。5 秒 720p のタスクは通常数分で完了しますが、1080p 15 秒にはより多くの時間を見込む必要があります。
  • output[].url は一時ストレージに保存されます。すぐにバイトをダウンロードして自前で再ホストしてください。URL だけを保存してエンドユーザーに提供しないでください——期限切れになります。
import urllib.request

def download(video_url: str, out_path: str) -> None:
    with urllib.request.urlopen(video_url) as resp, open(out_path, "wb") as f:
        f.write(resp.read())

download(video_url, "ocean.mp4")

結果の取得:コールバック(本番モード)

ポーリングはスクリプトや notebook には向いていますが、実際のバックエンドでは、動画ごとに worker を数分間占有したくはないでしょう。送信時に callback オブジェクトを渡すと、hiapi が完了したタスクのボディをあなたの URL にちょうど1回 POST します。

curl -X POST https://api.hiapi.ai/v1/tasks \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "happyhorse-1.1/text-to-video",
    "input": {
      "prompt": "an astronaut planting a flag on a glass beach",
      "aspect_ratio": "16:9",
      "resolution": "1080p",
      "duration": 6
    },
    "callback": {
      "url": "https://example.com/hooks/hiapi",
      "when": "final"
    }
  }'

現在サポートされているのは when: "final" のみです——他の値を送ると次が返ります:

{ "code": 400, "error_code": "INVALID_REQUEST",
  "message": "invalid callback.when: only 'final' is supported" }

堅牢な受信側の設計

3つのルールでコールバック経路の安全性を確保します:

  1. 冪等性。 taskId ごとに永続化します。同じコールバックを2回受け取った場合——リトライにより最終的には必ずそうなります——重複処理は no-op でなければなりません。
  2. 確認と処理を分離する。 payload をキューに入れたらすぐに webhook から 200 OK を返します。実際のダウンロード / 再アップロード / DB 書き込みはバックグラウンドタスクで行います。遅い webhook はタイムアウトと重複配信を招きます。
  3. ポーリングにフォールバックする。 送信したすべての taskId を created_at とともに自前の DB に保存します。約 30 分を超えてまだ終端状態にない行に対して /v1/tasks/{id} をポーリングする掃除処理を走らせます。コールバックは高速経路であり、唯一の経路ではありません。

最小限の FastAPI 受信側:

from fastapi import FastAPI, Request

app = FastAPI()

@app.post("/hooks/hiapi")
async def hiapi_callback(req: Request):
    body = await req.json()
    task = body.get("data", body)  # callback wraps the same shape as GET
    task_id = task["taskId"]
    if task["status"] == "success":
        url = task["output"][0]["url"]
        # enqueue, don't download here
        await queue.put({"task_id": task_id, "url": url})
    return {"ok": True}

実際に遭遇するエラー

エラーエンベロープの形状は常に一貫しています——{code, error_code, message, data}——ので、ハンドラを1つ書くだけで済みます。このモデルで特別に対処する価値のあるものをいくつか挙げます:

HTTP error_code 発生条件 修正方法
400 INVALID_REQUEST 未知のフィールド、列挙値の誤り、duration が 3–15 の範囲外 message を確認してください。どのフィールドかを示します——例:aspect_ratio: value must be one of '16:9'...
401 — キーの欠落または誤り、あるいは permission_denied Authorization: Bearer sk-... ヘッダーを再確認してください
402 — 残高が推定コストをカバーできない hiapi.ai/en/pricing でチャージしてから再試行してください
429 — そのエンドポイントに過剰にリクエストしている バックオフとジッターを付けて再試行してください

実際のバリデーション失敗の例は次のとおりです——テストフィクスチャとしてコピーできます:

{
  "code": 400,
  "data": null,
  "error_code": "INVALID_REQUEST",
  "message": "invalid input: aspect_ratio: value must be one of '16:9', '9:16', '3:4', '4:3', '4:5', '5:4', '1:1', '9:21', '21:9'; resolution: value must be one of '720p', '1080p'; duration: maximum: got 99, want 15"
}

すべてのフィールドが同じレスポンス内で報告されることに注目してください。可能ならローカルでバリデーションし、できない場合はこのレスポンスに頼ってください。

hiapi 上の関連コンテンツ

よくある質問

happyhorse-1.1/text-to-video は音声を生成しますか? タスク API 経由では生成しません——リクエスト構造に現在 audio フィールドはなく、送ると additional properties 'audio' not allowed が返ります。出力は無音の MP4 です。BGM は下流で重ねてください。

縦向き / ショート動画にはどのアスペクト比が使えますか? TikTok や Reels 向けの出力には 9:16 が標準的な選択です。API は 4:5(Instagram フィードの縦向き)や 9:21(超縦長)も受け付けますので、より細い画角が必要な場合に利用してください。

最短と最長の尺は? duration は 3 から 15 秒(両端を含む)の整数です。0 を送ると duration: minimum: got 0, want 3 が返り、99 を送ると duration: maximum: got 99, want 15 が返ります。必要な動きを捉えられる最小値を選んでください——尺こそが課金対象です。

再現性のある出力を得るために seed を渡せますか? このモデルでは現在サポートされていません。{"input": {"prompt": "...", "seed": 42}} をリクエストすると additional properties 'seed' not allowed で失敗します。再現性が重要な場合は、リクエストボディ全体を記録し、同じプロンプトで再試行してください。似た——ただしピクセル単位では同一でない——結果が得られます。

negative_prompt はサポートされていますか? いいえ。prompt 自体に制約を加えることで出力を誘導してください(例:"...clean composition, no text, no watermarks")。

出力 URL の有効期間はどれくらいですか? デフォルトプランではタスクの storage フィールドは temp で、output[].url は短期間有効な署名付きリンクです。タスクが success になったらすぐにファイルをダウンロードして再ホストしてください。生の URL を保存して後で提供しないでください。

開発中にコストを抑えるには? 節約のための2つの習慣:プロンプトを確定するまでは duration: 3 を保ち、絵コンテを確認するまでは resolution: "720p" で送信する。最終レンダリングのみ 1080p とより長い尺に上げてください。

ポーリングとコールバック、どちらを使うべきですか? 両方を、この順序で使ってください。まずは可動部品が少ないポーリングから。worker を wait_for_task でブロックさせる余裕のないバックエンドを初めて持ったときに、コールバックに移行します。いずれにせよ、取りこぼしたコールバックのフォールバックとしてポーリングの掃除処理を残してください。コールバックはベストエフォートであり、配信保証ではありません。

クレジットがあるのに 402 が返るのはなぜですか? API はタスクのコスト上限——尺 × 秒単価——を見積もり、事前に予約します。残高がその見積もりをカバーできない場合、直ちに 402 が返ります。チャージして再試行すると、成功時には実際のコストのみが差し引かれます。

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

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

APIドキュメントを読む