HiAPI
ブログ

チュートリアル

HiAPI API で veo-3.1-lite/text-to-video を呼び出す方法:curl と Python

そのままコピーできるリクエスト例、プロンプトから動画を生成する流れ、そして非同期ポーリングのパターン

Nov 3, 2026
深炭底色上,一道琥珀色光带横向掠过,构图克制,是这篇教程文章的封面图。

veo-3.1-lite/text-to-video でできること

veo-3.1-lite/text-to-video は HiAPI 上のテキストから動画を生成するエンドポイントです。テキストプロンプトを送信すると、モデルが生成した動画クリップを返します。名前の中の「lite」は、モデルを選ぶ際に最も重要なシグナルです。軽量版の階層は最高の忠実度を追求するのではなく、スループットと素早い反復のために作られているため、大量の短いクリップが必要な場合、より重いモデルを採用する前に機能をプロトタイピングする場合、あるいはクリップがより大きな自動化パイプラインの一部にすぎず、クリップ単体のレイテンシやバッチ規模が映画品質よりも重要である場合に、優れたデフォルトの選択肢となります。

クリップそのものが製品である場合——たとえばメインビジュアルのショット、マーケティング素材、あるいは人がフレーム単位で精査するコンテンツなど——は、より重いテキストから動画へのモデルに切り替えるべきです。一方、クリップが単なる構成要素にすぎない場合——たとえばドラフトプレビュー、背景ループ、絵コンテのラフ、あるいは後で仕上げる初回レンダリングなど——は veo-3.1-lite/text-to-video を選んでください。

このエンドポイントの 2 つの特性が、後述するすべての内容を決定づけます:

  • 非同期である。 1 回の生成リクエストが同じレスポンス内で再生可能なファイルを直接返すことはありません。ジョブを送信し、そのジョブへの参照を受け取り、ジョブが完了して出力 URL が利用可能になるまでポーリングします。
  • プロンプト駆動である。 このフローには入力画像やソース動画はありません。送信するテキストが完全なクリエイティブブリーフであるため、プロンプトの品質があなたが握る主要なレバーとなります。

本ガイドの残りの部分は実践的な演習です。リクエストを構築し、curl で送信し、Python に移植し、正しくポーリングし、実際に遭遇する失敗ケースに対処します。

前提条件と認証

初回呼び出しの前に、以下を用意してください:

  • HiAPI アカウントの API キー。 データベースのパスワードと同じくらい慎重に、機密情報として扱ってください。
  • HTTP リクエストを送る手段: 簡単な確認には curl、スクリプト化された処理には requests ライブラリ入りの Python 3 を使います。
  • 最終クリップを保存する場所。 出力は URL として届くため、ダウンロードする必要があります。

キーはすべてのリクエストで HTTP ヘッダーを通じて渡されます。示されているヘッダー名と Bearer プレフィックスを厳密にそのまま使ってください。ヘッダーの欠落や形式の誤りは、初回呼び出しが失敗する最も一般的な原因です。

キーをソース管理から遠ざける

実際のキーをコミット対象のファイルに貼り付けないでください。環境変数を使います:

export HIAPI_API_KEY="your-key-here"

そして実行時に読み取ります。チームや CI パイプラインで作業する場合は、キーをシークレットマネージャーに保管し、デプロイ時に環境変数として注入してください。キーが一度でも露出した場合——誤ってコミットした、チケットに貼り付けた、ログに出力されたなど——は、誰も気づかないだろうと期待せず、直ちにローテーションしてください。

他の何かをデバッグする前に、環境変数が設定されているか簡単に確認しましょう:

test -n "$HIAPI_API_KEY" && echo "key is set" || echo "key is missing"

動作する curl リクエスト

以下はテキストから動画への送信リクエストの形です。プレースホルダーのエンドポイントベース URL を、HiAPI のリファレンスドキュメントに記載されたアドレスに置き換え、モデル識別子 veo-3.1-lite/text-to-video は厳密に変更しないでください。

curl -X POST "$HIAPI_BASE_URL/v1/videos/generations" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "veo-3.1-lite/text-to-video",
    "prompt": "夜、雨に濡れたネオンサインへゆっくりとドリーインするショット、浅い被写界深度、穏やかなカメラの揺れ、シネマティックな色調"
  }'

送信に成功しても動画のバイト列は返りません。返るのはジョブオブジェクトです。これにはジョブの識別子、初期状態が queued や processing などの status フィールド、作成時刻に関するメタデータが含まれます。この識別子が以降のすべての操作のハンドルになります。状態が終端状態になるまでポーリングし、そのときになって初めてレスポンスに生成クリップの URL が含まれます。

手動でテストする場合は、目で見るだけでなくレスポンス全体を取得してください:

curl -s -X POST "$HIAPI_BASE_URL/v1/videos/generations" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"veo-3.1-lite/text-to-video","prompt":"日差しの当たる側溝を流れていく紙のボート、マクロレンズ"}' \
  | tee submission.json

今すぐ身につけるべき習慣が 2 つあります。第一に、常に Content-Type: application/json を送信してください。JSON のように見えても JSON として宣言されていないリクエストボディは拒否されます。第二に、呼び出しが失敗した場合は、何かを変更する前にまずエラーレスポンスボディを読んでください。HiAPI は構造化されたメッセージを返すため、ステータスコードだけを見るよりも問題(キーの誤り、不明なモデル、フィールドの欠落)を正確に特定できることがほとんどです。

ここで 2xx 以外のステータスコードが返った場合、ジョブは受け付けられていません。失敗した送信に対してポーリングしないでください。まずリクエストを修正してください。

同じリクエストを Python で実装する

curl 呼び出しはそのまま移植できます。以下のスクリプトはプロンプトを送信し、送信が成功したか確認し、ポーリング手順で使うジョブ参照を保存します。

import os
import requests

BASE_URL = os.environ["HIAPI_BASE_URL"]
API_KEY = os.environ["HIAPI_API_KEY"]

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
}

payload = {
    "model": "veo-3.1-lite/text-to-video",
    "prompt": (
        "静かな黒い水面に浮かぶ一つの灯籠、"
        "柔らかな波紋、冷たい暗闇に対比する暖かな光、ゆっくりとしたプッシュイン"
    ),
}

response = requests.post(
    f"{BASE_URL}/v1/videos/generations",
    headers=headers,
    json=payload,
    timeout=30,
)

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

job = response.json()
job_id = job["id"]
print(f"submitted job {job_id} with status {job.get('status')}")

このコード断片には意図的な選択がいくつかあります:

  • 手動での文字列結合ではなく json=payload を使う。 シリアライズとコンテンツタイプの設定はライブラリに任せます。これにより引用符関連のバグの一連を避けられます。
  • timeout を明示的に設定する。 設定しないと、接続の停滞でスクリプトが無限にハングします。送信呼び出しは高速なので、ここで緩めのタイムアウトを設定しても十分短く済みます。
  • 非 2xx で即座に失敗させる。 直ちに例外を送出すれば、壊れたジョブ ID がポーリングループに伝播するのではなく、エラーをその原因の近くに留められます。
  • キーは os.environ から取得する。 ファイル自体には機密情報を一切書き込みません。

これを job_id を返す関数にまとめれば、あらゆるパイプラインの送信部分が手に入ります。

非同期ジョブをポーリングしてクリップをダウンロードする

動画生成には時間がかかります。送信呼び出しは 1〜2 秒で返りますが、実際のレンダリングはそうではありません。最初のレスポンスに動画 URL が含まれていると仮定したコードを書くと失敗します。API が壊れているからではなく、まだ完了していないジョブを読んでいるからです。

正しいパターンは上限付きポーリングです。チェックの間にスリープし、待ち時間の増加に合わせてバックオフし、最大試行回数に達したら無限ループせずに諦めます。

import time
import requests

def wait_for_video(job_id, headers, base_url, max_attempts=60, initial_delay=5):
    delay = initial_delay
    for attempt in range(1, max_attempts + 1):
        r = requests.get(
            f"{base_url}/v1/videos/generations/{job_id}",
            headers=headers,
            timeout=30,
        )
        if r.status_code >= 400:
            raise RuntimeError(f"Poll failed: {r.status_code} {r.text}")

        data = r.json()
        status = data.get("status")
        print(f"attempt {attempt}: {status}")

        if status == "completed":
            return data
        if status in {"failed", "cancelled"}:
            raise RuntimeError(f"Job {job_id} ended as {status}: {data}")

        time.sleep(delay)
        delay = min(delay * 1.5, 30)

    raise TimeoutError(f"Job {job_id} did not finish within polling budget")

ジョブが completed に達すると、レスポンスには出力の場所、すなわち完成したクリップの URL が含まれます。クリップが大きい場合は、メモリにストリーミングするのではなく、ダウンロードしてください:

result = wait_for_video(job_id, headers, BASE_URL)
video_url = result["output"]["url"]

with requests.get(video_url, stream=True, timeout=120) as r:
    r.raise_for_status()
    with open(f"{job_id}.mp4", "wb") as f:
        for chunk in r.iter_content(chunk_size=1 << 16):
            f.write(chunk)

print("saved", f"{job_id}.mp4")

本番環境では 2 つの点が重要です。第一に、出力 URL には有効期限が設定されている場合があるため、リンクを数日保存しておいて後で取得するのではなく、早めにダウンロードしてください。ファイルを永続化するか、自分のストレージにコピーします。第二に、各成果物のそばにジョブ ID を記録してください。下流で問題が起きたとき、ジョブ ID があれば自分の記録と API が実際に返した内容を照合できます。

よくあるエラーと修正方法

初回実行の失敗のほとんどは少数のカテゴリに収まります。以下にそれぞれの症状と変更すべき点を示します。

認証の失敗

送信時に 401 または 403 が返る場合は、キーが受け付けられていません。順に確認してください。そのシェルまたはプロセスで環境変数に実際に値があるか、ヘッダー名が Authorization か、Bearer プレフィックスが存在しその後にスペースがあるか、キーがローテーションまたは失効していないか。あるキーがターミナルでは動くのに CI では失敗する場合、ほぼ常にその環境にシークレットが注入されていないことを意味します。

無効なモデル識別子

モデル名を指摘する 400 または 404 が返る場合は、文字列が一致していません。識別子は veo-3.1-lite/text-to-video です。スラッシュもバージョン番号もその一部です。再入力せずコピー&ペーストしてください。余分なスペース一つや変更されたバージョン番号でも失敗するのに十分です。

プロンプトの形式エラーまたは空

空の prompt、prompt フィールドの欠落、または Content-Type: application/json なしでリクエストボディを送信すると、生成が始まる前に拒否されます。プロンプトを動的に構築する場合は、呼び出しが失敗したときに送信した正確な文字列をログに記録してください。空白や切り詰められたプロンプトを生成するテンプレートのバグはよくあり、出力するまで見えません。

ポーリングのタイムアウト

wait_for_video が予算を使い果たした場合、すぐに再送信しないでください。ジョブはまだ実行中なだけかもしれません。状態をもう一度確認し、ポーリング間隔が長すぎて予算をスリープに費やしていないか、予算が要求したクリップ長に対して現実的かを確認してください。タイムアウトのたびに再送信することこそ、同じクリップに誤って何度も課金する原因です。

レート制限

429 はバックオフが必要なことを意味します。ポーリングループの指数バックオフはすでにこれに役立ちます。送信にも同じ自制を適用し、バースト的に送るのではなく作業をキューに入れてください。迷ったら、自分のバックオフ計画を実行する前に、レスポンスヘッダーにある再試行のヒントにまず従ってください。

本番前のコストと反復に関するアドバイス

動画生成は出力、つまり生成したコンテンツの長さに基づいて課金されます。したがって高くつく誤りは、小さなアイデアをテストするために長いクリップを使うことです。最もコストを抑えられるワークフローは、不要なクリップを決して生成しないワークフローです。

最終出力ではなくプロンプト構造をテストする。 このエンドポイントがサポートする最短の長さでプロンプトを書き、調整してください。構図、動き、カメラ言語が短いクリップで正しく読めるようになったら、最終レンダリング用に長さを増やします。プロンプトの反復のほとんどは長さではなく言い回しに関するものであり、短いレンダリングでも言い回しの疑問には答えられます。

プロンプトのログを残す。 各テストのプロンプト、長さ、自分自身の評価を記録してください。記録された 10 回の反復は、記録のない 50 回の反復よりも、このモデルが何に反応するかをよく教えてくれますし、すでに却下したプロンプトを再テストすることもなくなります。

意識的にバッチ処理する。 多くのクリップが必要な場合は、それらをキューに入れ、レート制限内で並行実行します。ただし、まず少数のプロンプトでバッチ全体を試運転してください。200 個のジョブを送信した後に系統的なプロンプトバグに気づくことは、最も避けられるコストです。

控えめにポーリングする。 頻繁にポーリングしてもレンダリングが早く完了するわけではなく、レート制限に追い込まれる可能性があります。適度な初期間隔と穏やかなバックオフが正しいデフォルトです。

早期に、そして大きく失敗させる。 上記のコードにある各ガード——送信時の非 2xx 例外送出、ポーリングでの終端失敗検出、上限付き試行回数——は、同じ作業に二度課金されるのを防ぐためのものです。本番でもこれらを残してください。

短いプロンプトと curl リクエストから始め、ジョブのライフサイクルをエンドツーエンドで確認し、その後同じフローを Python パイプラインに移してください。完全なエンドポイントリファレンスは、HiAPI の veo-3.1-lite/text-to-video に関するモデルドキュメントページにあります。

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

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

APIドキュメントを読む