HiAPI
ブログ

チュートリアル

Ray 3.2 画像から動画へのAPIの使い方:curl、Python、そして一度で成功するリクエスト

Oct 20, 2026
Ray 3.2 画像から動画へのAPIの使い方:curl、Python、そして一度で成功するリクエスト

Ray 3.2 画像から動画へは、1枚の静止画にアニメーションを付けます。画像と、どう動かすかを記述したプロンプトを渡すと、あなたの画像から始まる短い動画を返します。hiapi では、モデル id は ray-3.2/image-to-video で、他のすべての動画モデルと同じ統一非同期タスク API を通じて呼び出します——POST /v1/tasks を送信し、その後ポーリングするかコールバックを受け取ります。

これは Ray 3.2 テキストから動画へ とは異なるエンドポイントであり、単にプロンプトが違うだけではありません。入力スキーマはより小さく厳格で、テキストモデルで慣れているかもしれない2つの制御項目がありません。このガイドでは、一度で動作するリクエストを提供します:正確なスキーマ(2026-10-20 に本番 API と照合して検証済み)、そのままコピーできる curl、完全な Python の送信/ポーリング/ダウンロードスクリプト、そして本番環境の詳細——コールバック、冪等性、そして実際のエラー応答、その中には期待した場所に現れない失敗モードも含まれます。

何を構築し、何を準備するか

目標:最初のフレーム画像 + 動きのプロンプトを送信し、タスクをポーリングし、あなたの画像が動き出す無音の .mp4 をダウンロードします。

前提条件:

  • hiapi API キー——ダッシュボードから取得します。キーは sk-... の形式で、Authorization: Bearer ヘッダーに入れます。タスク API が初めてなら、まずクイックスタートをご覧ください。
  • 最初のフレーム画像の公開アクセス可能な画像 URL(詳細は後述)。任意の HTTP クライアント——ここでは curl と Python(requests)を示します。

Ray 3.2 画像から動画へは動画クリップ単位で課金されます。クリップの長さが固定(後述)なので、resolution がコストを制御できる唯一のレバーです——1080p クリップのコストは 540p の数倍です。現在の価格は価格を確認し、プロンプトを反復する際はまず 540p でプロトタイプを作ってください。

正確な入力スキーマ(必須フィールド2つ、オプション1つ)

すべては input オブジェクト内に置きます。スキーマは厳格で、認識できないフィールドはすべて拒否します。フィールドごとに本番 API と照合して検証済み:

フィールド 型 必須か 許可値 デフォルト
prompt string はい 画面をどう動かすか(カメラ、被写体の動き) —
image_url string はい 最初のフレームの公開 URL —
resolution string いいえ 540p、720p、1080p 540p

テキストから動画へのモデルから来た人がつまずきやすい3つのポイント:

  1. duration はありません。クリップの長さは固定(約5秒)です。duration を送信しても切り捨てられるのではなく、直接拒否されます。これは Ray 3.2 テキストから動画へとは正反対で、そこでは duration は選択可能な整数(5 または 10)です。
  2. aspect_ratio はありません。出力のアスペクト比は入力画像に従うので、設定するものはありません。テキストモデルではアスペクト比を選びますが、ここでは画像が決めます。(後述の実際の実行で確認:正方形の入力は正方形のクリップを生成しました。)
  3. スキーマは閉じています。seed、negative_prompt、prompt_optimizer その他あらゆるフィールドは 400 を返し、additional properties '<field>' not allowed が付きます。他のモデルからリクエストボディを移植して、余分なキーが無視されると仮定しないでください。

検証が実際に何を返すか

これらは本番エンドポイントからの実際の応答です——意図的に無効な値を送信し、タスクは作成されず、課金もされていません:

// POST with no image_url
{"code":400,"data":null,"error_code":"INVALID_REQUEST",
 "message":"invalid input: image_url: missing required field \"image_url\""}

// POST with "resolution": "2k"
{"code":400,"data":null,"error_code":"INVALID_REQUEST",
 "message":"invalid input: resolution: value must be one of '540p', '720p', '1080p'"}

// POST with a "duration" field
{"code":400,"data":null,"error_code":"INVALID_REQUEST",
 "message":"invalid input: <root>: additional properties 'duration' not allowed"}

// POST with an "aspect_ratio" field
{"code":400,"data":null,"error_code":"INVALID_REQUEST",
 "message":"invalid input: <root>: additional properties 'aspect_ratio' not allowed"}

message は具体的な問題フィールドを指し示すので、忘れたときは API 自体がスキーマのリファレンスになります。

最初のフレームを準備する

image_url は hiapi ワーカーが取得できる URL でなければなりません——公開リンクであり、ローカルパスでも data: base64 blob でもありません。画像はクリップのアスペクト比と冒頭の構図を決めるので、本当に動き始めてほしいフレームから始めてください。

事前に説明しておく価値のある落とし穴があります。見落としやすいからです:壊れた image_url はタスク作成時に捕捉されません。作成呼び出しはリクエストの形状(文字列か?)だけを検証し、URL が解決可能かは検証しません。到達不能または 404 の画像 URL は完全に正常な 200 と taskId を返します——そしてタスクは数秒後に fail に変わります。正確な失敗ペイロードはトラブルシューティングのセクションで見ますが、要点は常にタスクの終態 status で分岐し、作成が 200 を返したからといって成功と仮定しないことです。

最小限の動作例:curl

タスクを作成:

curl -s -X POST https://api.hiapi.ai/v1/tasks \
  -H "Authorization: Bearer sk-YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "ray-3.2/image-to-video",
    "input": {
      "prompt": "slow cinematic push-in, gentle parallax, soft drifting light, the subject breathing almost imperceptibly",
      "image_url": "https://your-cdn.example.com/first-frame.jpg",
      "resolution": "540p"
    }
  }'

作成応答にはタスク id が含まれます:

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

終態に到達するまでポーリングします:

curl -s https://api.hiapi.ai/v1/tasks/tk-hiapi-01M5... \
  -H "Authorization: Bearer sk-YOUR_KEY"

タスク実行中、data.status はいくつかの進行中状態を経ます(handling のような値が見え、次に archiving になります)。終態は success と fail の2つです。成功時:

{
  "code": 200,
  "data": {
    "taskId": "tk-hiapi-01M5...",
    "status": "success",
    "created": 1792435735,
    "completed": 1792435781,
    "storage": "temp",
    "output": [
      { "type": "video", "artifactId": "278504",
        "expireAt": 1793040580,
        "url": "https://temp.hiapi.ai/.../clip.mp4" }
    ]
  },
  "message": "success"
}

data.output[0].url をすぐにダウンロードしてください。それは temp.hiapi.ai 上の署名付き URL で、expireAt(Unix タイムスタンプ、約1週間後)が付いています——ホットリンクするのではなく、バイトを自分のストレージに永続化してください。Ray 3.2 は無音 MP4 を返します。待つべき音声トラックはありません。

完全な Python スクリプト(送信 → ポーリング → ダウンロード)

import time
import requests

API_BASE = "https://api.hiapi.ai/v1"
API_KEY = "sk-YOUR_KEY"
HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}


def create_task(image_url: str, prompt: str, resolution: str = "540p") -> str:
    resp = requests.post(
        f"{API_BASE}/tasks",
        headers=HEADERS,
        json={
            "model": "ray-3.2/image-to-video",
            "input": {
                "prompt": prompt,
                "image_url": image_url,     # public URL of the first frame (required)
                "resolution": resolution,   # "540p" | "720p" | "1080p"
                # no "duration" and no "aspect_ratio" — both are rejected
            },
        },
        timeout=30,
    )
    resp.raise_for_status()
    return resp.json()["data"]["taskId"]


def wait_task(task_id: str, timeout_s: int = 900, poll_interval: int = 8) -> dict:
    """Poll until terminal. Tolerates transient network blips by re-polling
    the SAME saved task_id instead of aborting the whole job."""
    deadline = time.time() + timeout_s
    while time.time() < deadline:
        try:
            resp = requests.get(f"{API_BASE}/tasks/{task_id}", headers=HEADERS, timeout=30)
            resp.raise_for_status()
        except requests.RequestException:
            time.sleep(poll_interval)      # a dropped poll is not a failed task
            continue
        task = resp.json()["data"]
        if task["status"] == "success":
            return task
        if task["status"] == "fail":
            err = task.get("error") or {}
            raise RuntimeError(f"task failed: {err.get('code')}: {err.get('message')}")
        time.sleep(poll_interval)           # any non-terminal status = still running
    raise TimeoutError(f"task {task_id} not finished after {timeout_s}s")


task_id = create_task(
    image_url="https://your-cdn.example.com/first-frame.jpg",
    prompt="slow cinematic push-in, gentle parallax, soft drifting light",
)
print("task created:", task_id)

task = wait_task(task_id)
video_url = task["output"][0]["url"]

# Output URLs expire — download right away and store the bytes yourself.
video = requests.get(video_url, timeout=120).content
with open("ray32-i2v.mp4", "wb") as f:
    f.write(video)
print(f"saved ray32-i2v.mp4 ({len(video) / 1e6:.1f} MB)")

pip install requests で実行し、あなたのキーと実際の image_url に置き換えれば、タスク完了後にディスク上に .mp4 ができます。

実際の1回の実行はどんなものか

数字を具体的にするために、2026-10-20 に本番 API に対して行った実際の 540p 実行をここに示します。最初のフレーム:1600×1600(正方形)の画像;プロンプト:ゆっくりとした映画的なプッシュイン。

  • タスクは handling → archiving → success を経て、サーバー側の処理は約 45 秒で完了しました(created 作成、completed 完了)——8 秒のポーリングループを含めると、実時間は約 60 秒です。動画タスクは数十秒から数分かかり、瞬間的ではありません。十分な締め切りを設けてください。
  • 出力は 720×720 MP4、約 1.3 MB、5.04 秒、無音でした。

この実行はパラメータ表が説明していない2つのことを教えてくれます:

  1. 出力アスペクト比は入力と一致します——正方形が入り、正方形が出ます。これがなぜ aspect_ratio フィールドがないのかの理由です;希望するクリップの構図に合わせて image_url をフレーミングしてください。
  2. resolution は品質ティアであり、文字通りのピクセル高さではありません。1600×1600 の入力に対して 540p を要求すると、720×720 が返りました——このティアは入力形状に合わせてスケールし、固定サイズを強制しません。正確な出力サイズが必要なら、納品するクリップのサイズを自分で調整してください。

本番環境の注意点:コールバック、冪等性、エラー

ポーリングよりもコールバックを優先する

スクリプト以外のあらゆるシナリオでは、ポーリングするのではなく作成時に webhook を登録してください:

{
  "model": "ray-3.2/image-to-video",
  "input": {
    "prompt": "...",
    "image_url": "https://your-cdn.example.com/first-frame.jpg",
    "resolution": "720p"
  },
  "callback": { "url": "https://your-app.example.com/hooks/hiapi", "when": "final" }
}

"when": "final" を使うと、hiapi はタスクが終態に到達したときにあなたのエンドポイントを一度だけ呼び出します——ポーリングのトラフィックもなく、間隔の間に完了を見逃すこともありません。その後、ハンドラで GET /v1/tasks/<id> を実行して output[0].url を取得します。HTTPS エンドポイントを公開できない CLI や notebook では、ポーリングで問題ありません。

リトライを冪等にする

作成呼び出しが返った瞬間に taskId を永続化してください。プロセスが生成中にクラッシュした場合は、保存した id をポーリングして復旧します——タスクを再作成しないでください。そうしないと2つ目のクリップに課金されます。元の作成リクエストがタスク id を返す前に失敗した場合にのみ再作成してください。上記の耐障害性のあるポーリングループはすでにこのルールに従っています:ネットワークリクエストが中断したら、最初からやり直すのではなく同じ id を再ポーリングします。

実際に見るエラー

下の表の各行は検証済みの本番応答形状です:

症状 応答 修正
キーが欠落/無効 401、ネストされた {"error":{"code":"permission_denied","type":"hiapi_error","request_id":"...","message":"This API key is invalid..."}} Authorization: Bearer sk-... ヘッダーと、キーにアクセス権があるかを確認してください。サポートに連絡する際は request_id を提供してください。
image_url の欠落 400 INVALID_REQUEST、message: "image_url: missing required field" このモデルは image_url が必須です——テキストのみのモードはありません。
列挙値の誤り / 余分なフィールド 400 INVALID_REQUEST、フラットな {"code":400,"error_code":"INVALID_REQUEST","message":"..."} message で指摘されたフィールドを読んでください。duration/aspect_ratio については、additional properties ... not allowed と表示されます——それらを削除してください。
ポーリング時のタスク id の誤り 404、フラットな {"code":404,"data":null,"message":"task not found"}(注意:404 には error_code がありません) id が間違っているか、別のアカウントのキーでポーリングしています。
image_url が到達不能 作成が 200 + taskId を返す;タスクはその後 data.status:"fail" で終了し、data.error:{"code":"INVALID_REQUEST","message":"Invalid request parameters for this model..."} が付く これが罠です。作成呼び出しはデッドリンクとライブリンクを区別できません。画像が公開アクセス可能であることを確認し、常に終態 data.status を読んでください。

最後の行は覚えておく価値があります:タスクが失敗しても、ポーリングの HTTP 外層は 200 のままです——失敗は HTTP ステータスコードではなく data.status と data.error の中に存在します。失敗したタスクは返金されますが、コードはそれに気づかなければなりません。data.status == "fail" で分岐し、200 を信じないでください。

関連リソース

よくある質問

モデル id は何で、どのエンドポイントを呼ぶべきですか? ray-3.2/image-to-video、POST https://api.hiapi.ai/v1/tasks に送信し、パラメータは input の下に置きます。非同期です——taskId を受け取り、GET /v1/tasks/<id> をポーリングします(またはコールバックを登録します)。

入力には何が必要ですか? ちょうど2つの必須フィールド——prompt(動き)と image_url(最初のフレームの公開 URL)——に加えてオプションの resolution(540p/720p/1080p)。それ以外はすべて拒否されます。

なぜサイズやアスペクト比を設定できないのですか? クリップの長さは固定(約5秒)なので duration フィールドはありません;出力アスペクト比は入力画像から継承されるので aspect_ratio フィールドはありません。送信すると 400 が返ります。選択可能な長さが必要なら、Ray 3.2 テキストから動画へを使ってください。

作成呼び出しが 200 を返したのに、動画が一向に来ません。なぜですか? 最も多いのは image_url が到達不能なケースです。作成呼び出しはリクエスト形状だけを検証するので、デッドリンクでも taskId を返します——そしてタスクは fail で終わります。終態までポーリングし、data.error を読んでください。画像が公開取得可能であることを確認してください(認証不要、ローカルパスでない、base64 でない)。

クリップに音声はありますか? ありません——Ray 3.2 画像から動画へは無音 MP4 を返します。後処理で音を追加するか、別の音声モデルを使ってください。

1クリップいくらですか? クリップ単位で課金され、長さが固定なので resolution が唯一のレバーです——1080p クリップのコストは 540p の数倍です。価格は変動するので、数字をハードコードするのではなく価格ページを確認し、まず 540p でプロトタイプを作ってください。

どれくらい待ってから諦めるべきですか? 数十秒から数分が正常です。ポーリングループで非終態の data.status を許容し、十分な締め切り(10〜15分)を設けるか、"when": "final" 付きの callback を登録して、間隔を推測するのではなく hiapi から通知を受け取ってください。

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

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

APIドキュメントを読む