チュートリアル
hiapi 上で単一の統合APIを使ってマルチモデルAIアプリを構築する
- モデル
- seedance-2.5
構築するもの
ほとんどのチームは、1つのプロダクトのために最終的に3〜4社の異なるAIプロバイダーを呼び出すことになります。サムネイル用のテキスト→画像モデル、SNS向けショート動画用のテキスト→動画モデル、そして場合によってはナレーション用のTTSモデルです。各プロバイダーにはそれぞれのSDK、それぞれの認証方式、それぞれのポーリングロジックがあります。hiapi はこれをすべて1つのエンドポイントパターンに集約します。すなわち POST /v1/tasks でタスクを作成し、GET /v1/tasks/:id でタスクを照会する——プラットフォーム上のすべての生成モデルにこれが当てはまります。
本チュートリアルでは、現在オンラインで利用可能な3つの実在モデルを使ってこの統合パターンを示します——z-image(テキスト→画像)、qwen-image-2.0(テキスト→画像、スキーマが異なる)、そして seedance-2.5(テキスト→動画)です。これにより、APIが完全に同一な部分と、各モデルの input オブジェクトだけが異なる部分が明確になります。
前提条件
- コンソールから取得した hiapi の APIキー
curl、またはJSONを送信できる任意のHTTPクライアント- SDKは不要——以下の各例はすべて純粋なHTTP呼び出しです
すべてのリクエストに1つのヘッダーが必要です:
Authorization: Bearer <your-api-key>
キーが無効または欠落している場合、どのモデルにも触れる前に401系のエラーが返ります:
{
"error": {
"code": "permission_denied",
"message": "invalid api key",
"request_id": "req_...",
"type": "hiapi_error"
}
}
キーなし・匿名モードは存在しません——すべての /v1/tasks 呼び出しには有効なベアラートークンが必要です。キーのローテーションとスコープについては認証を参照してください。
最小の動作例
1. タスクの作成
作成呼び出しの構造は常に同じです。1つの model id と、モデル固有の input オブジェクトです。以下は z-image で画像タスクを作成する例です:
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "z-image",
"input": {
"prompt": "a minimalist workspace, soft morning light, isometric illustration",
"aspect_ratio": "16:9"
}
}'
レスポンスはすぐに返ります——生成は非同期で行われます:
{
"code": 200,
"message": "ok",
"data": {
"taskId": "tk-hiapi-xxxxxxxxxxxx",
"status": "queued"
}
}
次に qwen-image-2.0 でも同じパターンを使います。同じエンドポイント、同じ外側の構造——異なるのは input だけです(このモデルは aspect_ratio ではなく size 文字列を受け取ります):
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen-image-2.0",
"input": {
"prompt": "a minimalist workspace, soft morning light, isometric illustration",
"size": "2048*2048"
}
}'
さらに seedance-2.5/text-to-video で動画タスクを作成します——外側の構造はやはり同じで、今度は duration と、より広い値域の aspect_ratio 列挙が加わります:
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "seedance-2.5/text-to-video",
"input": {
"prompt": "a paper airplane gliding through a sunlit office, slow motion",
"duration": 5,
"aspect_ratio": "16:9"
}
}'
これが核心です:タスクの外側の構造は決して変わらず、変わるのは input だけです。 hiapi 上のすべてのモデルは、それぞれのモデルページで自身の input スキーマを記録しています——新しい統合を構築する前に必ずそのモデルのスキーマを確認してください。なぜなら aspect_ratio、size、duration といったフィールドはモデル間で互換性がないからです。
2. 結果のポーリング
curl -s https://api.hiapi.ai/v1/tasks/tk-hiapi-xxxxxxxxxxxx \
-H "Authorization: Bearer $HIAPI_API_KEY"
status は queued → handling → archiving と遷移し、最終的に success または fail の終端状態になります。成功時、data.output は配列です——ほとんどの画像/動画モデルは1つのエントリを返しますが、1つのタスクが複数種類の出力を返すこともあります(例えば動画モデルが video と thumbnail を同時に返す場合など):
{
"code": 200,
"message": "ok",
"data": {
"taskId": "tk-hiapi-xxxxxxxxxxxx",
"status": "success",
"output": [
{ "type": "image", "url": "https://...", "expireAt": "2026-10-09T00:00:00Z" }
]
}
}
expireAt は重要です——出力URLはデフォルトで一時的です。タスクが完了したらすぐにバイト内容をダウンロードしてください(または自身のストレージに再アップロードしてください)。ファイルをより長期間プラットフォームに保持させたい場合は、作成時に storage: "persistent" を設定するか、後からアップグレードしてください——ストレージを参照してください。
失敗時には、output の代わりに error オブジェクトが返ります:
{
"code": 200,
"message": "ok",
"data": {
"taskId": "tk-hiapi-xxxxxxxxxxxx",
"status": "fail",
"error": { "code": "...", "message": "..." }
}
}
以下の最小限のPythonポーリングループは、上記のすべてのモデルに同様に適用できます:
import time
import requests
def run_task(model: str, input_payload: dict, api_key: str) -> list[dict]:
headers = {"Authorization": f"Bearer {api_key}"}
create = requests.post(
"https://api.hiapi.ai/v1/tasks",
headers=headers,
json={"model": model, "input": input_payload},
).json()
task_id = create["data"]["taskId"]
time.sleep(3) # initial wait before first poll
while True:
poll = requests.get(
f"https://api.hiapi.ai/v1/tasks/{task_id}", headers=headers
).json()
status = poll["data"]["status"]
if status == "success":
return poll["data"]["output"]
if status == "fail":
raise RuntimeError(poll["data"]["error"])
time.sleep(4) # 3-5s between polls; use 5-10s for video models
本番環境でのパターン
本番環境ではポーリングの代わりにコールバックを使用する
ポーリングはスクリプトやCLIツールには問題ありませんが、実際のトラフィック規模ではコールバックが推奨されます。タスク作成時に callback オブジェクトを渡すと、hiapi はループ内で GET /v1/tasks/:id を繰り返しリクエストさせる代わりに、終端状態の結果をあなたのエンドポイントにPOSTします。
{
"model": "z-image",
"input": { "prompt": "...", "aspect_ratio": "1:1" },
"callback": { "url": "https://yourapp.com/webhooks/hiapi", "when": "final" }
}
when: "final" は、終端状態(success または fail)のときに1回だけPOSTを受け取ることを意味します——中間の handling/archiving 状態によるノイズはありません。コールバックを設定したのに何も受け取らない場合は、エンドポイントが公開アクセス可能で、素早く2xxを返しているか確認してください。コールバックが静かに失われる最も一般的な原因については、こちらの関連記事を参照してください。
冪等なリトライ
ネットワークの揺らぎは避けられません。タイムアウト後に保護策なしで POST /v1/tasks 呼び出しをリトライすると、重複したタスクを作成(および課金)してしまう可能性があります。Idempotency-Key ヘッダー(255バイト以内の任意の文字列、通常は論理リクエストごとに生成するUUID)を送信すると、同じキーでのリトライは新しいタスクを作成せず元のタスクを返します:
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Idempotency-Key: 8f14e45f-ceea-467e-bd5f-...-9a2c" \
-H "Content-Type: application/json" \
-d '{"model": "z-image", "input": {"prompt": "...", "aspect_ratio": "1:1"}}'
モデルごとのバリデーションエラーの処理
各モデルが独自の input スキーマを持っているため、最もよくある失敗パターンは特定のフィールドに対する400です——あるモデルには有効で別のモデルには無効なフィールド、例えば size を期待するモデルに aspect_ratio を送信する場合などです:
{
"code": 400,
"data": null,
"error_code": "INVALID_REQUEST",
"message": "invalid input: aspect_ratio: value must be one of '1:1', '4:3', '3:4', '16:9', '9:16'"
}
マルチモデルルーターを構築する場合(ユーザーまたは独自のロジックが実行時にモデルを選択する場合)、共有のペイロード構造ではなく、モデル別の入力ビルダーの小さなレジストリを維持してください:
MODEL_INPUT_BUILDERS = {
"z-image": lambda prompt, **kw: {
"prompt": prompt, "aspect_ratio": kw.get("aspect_ratio", "1:1")
},
"qwen-image-2.0": lambda prompt, **kw: {
"prompt": prompt, "size": kw.get("size", "2048*2048")
},
"seedance-2.5/text-to-video": lambda prompt, **kw: {
"prompt": prompt,
"duration": kw.get("duration", 5),
"aspect_ratio": kw.get("aspect_ratio", "16:9"),
},
}
def build_task_payload(model: str, prompt: str, **kw) -> dict:
return {"model": model, "input": MODEL_INPUT_BUILDERS[model](prompt, **kw)}
これにより、各モデルの特殊性が呼び出し箇所ごとに散らばるのではなく、1か所に集約されます。存在しない、または別アカウントの taskId は通常の404を返します:
{ "code": 404, "message": "task not found", "data": null }
また一時的な 503 はユーザーに直接見せるのではなく、指数バックオフでリトライすべきです——レート制限と容量制限についてはレート制限に記載されています。
関連記事
- 非同期タスクAPIリファレンス——完全な
create/detailの契約 - 認証——APIキーのスコープとローテーション
- ストレージ——出力をデフォルトの有効期限以降も保持する
- タスクコールバックが発火しない可能性がある理由
- すべてのモデルを閲覧——各モデル自身の
inputスキーマはそのページにあります - 料金——コストはリクエスト単位の一律価格ではなく、モデルと出力に基づいて計算されます
よくある質問
モデルごとに異なるSDKやAPIキーが必要ですか?
いいえ。1つのAPIキー、1つのエンドポイントパターン(/v1/tasks)が、プラットフォーム上のすべてのモデルに適用されます。変わるのはリクエストボディ内の input オブジェクトだけです。
タスクを作成した直後のレスポンスに output がないのはなぜですか?
生成は非同期です。作成呼び出しは taskId と status: "queued" のみを返します——status が success または fail に達するまで、GET /v1/tasks/:id をポーリングする(またはコールバックを使用する)必要があります。
素早くテストするために認証なしでAPIを呼び出せますか?
いいえ——すべての /v1/tasks 呼び出しには有効な Authorization: Bearer キーが必要です。本番呼び出しにキーなし・デモモードはありません。まずコンソールからキーを生成してください。
出力URLが期限切れになった後にポーリングするとどうなりますか?
タスクレコード自体は保持されますが、非永続化タスクの output[].url は expireAt 以降は解決できなくなります。成功時にすぐファイルをダウンロードするか、長期保持が必要な場合は作成時に storage: "persistent" を設定してください。
あるモデルがどのフィールドを受け入れるかはどうやって知ればよいですか?
モデル配下のそのモデルのページを確認してください——それぞれが正確な input スキーマを記録しています。aspect_ratio、size、duration がモデル間で共通だと仮定しないでください。上記のように、z-image と qwen-image-2.0 は既に画像の形状の表現方法が一致していません。
関連記事
すべての記事を見る →-
チュートリアル
HiAPI API で hailuo-2.3-fast/image-to-video を呼び出す方法:curl と Python の実践
高速・低コストな画像から動画へのティアにおける最初の実行可能なリクエスト — モデル ID から MP4 のダウンロードまで
-
チュートリアル
HiAPI APIでhailuo-2.3/text-to-videoを呼び出す方法:curl、Python、実行可能なリクエスト例
-
チュートリアル
HiAPI で Kandinsky 6.0 Pro のテキストto動画を使用する:curl と Python チュートリアル
リクエスト優先の実践デモ:プロンプトから動画クリップへの呼び出しを構築し、リクエストを送信し、完了状態をポーリングし、結果をダウンロードする