チュートリアル
HiAPI API で gpt-6.1-terra を使う方法:curl、Python、そして実際に動作するリクエスト
gpt-6.1-terra へのリクエストに本当に必要なもの
HiAPI 上での gpt-6.1-terra への呼び出しは、すべて 4 つの要素に帰着します:URL、認証ヘッダー、モデル識別子、そしてリクエストボディです。これらを正しく押さえれば、この記事の残りは単なる配管の接続方法にすぎません。
エンドポイント
すべてのモデル呼び出しは HiAPI 上の同一のベース URL を通じて行われます。モデルごとにホストを切り替える必要はありません——リクエストボディ内のモデル名がリクエストのルーティング先を決めます。クライアントを HiAPI のベース URL に向け、それを変数に保存しておけば、変更は一箇所で済みます。
認証ヘッダー
認証は Authorization ヘッダーで bearer トークンを渡すことによって行われます。このトークンはあなたの HiAPI アカウントで発行された API キーです。ソースファイルにハードコードしないでください。環境変数(例えば HIAPI_API_KEY)から読み込むことで、同じコードがローカル、CI、本番環境でそのまま通用します。
モデル識別子
リクエストボディではモデルを明示的に指定する必要があります:"model": "gpt-6.1-terra"。この文字列は大文字と小文字を区別し、完全に一致しなければなりません。モデル名の誤りや途中での切り詰めはリクエストが拒否される最も一般的な原因の一つです。そのため、これはその場限りの文字列ではなく、コード内の定数として扱うべきです。
リクエストボディのフィールド
1 回のリクエストには少なくともモデルと入力内容——つまりモデルに処理してほしいプロンプトやメッセージ——が含まれます。この核となる部分に、モデルがサポートする任意のパラメータ(出力形式のヒント、長さ制御など)を追加できますが、まずは有効なレスポンスを生み出す最小限のリクエストボディから始めてください。最小限のリクエストボディはフル装備のものよりデバッグが容易で、失敗がリクエストボディ側にあるのか配管側にあるのかを判断するのに役立ちます。
早い段階で内面化しておくべき構造上の要点が一つあります:レスポンスにはインラインで返されるものもあれば、ポーリングが必要なタスクとして返されるものもあります。両者の区別については後述しますが、今のうちにリクエスト層を設計し、レスポンス処理が両方式を書き直しなしに切り替えられるようにしておくべきです。
実際に動作する curl リクエストを 1 行ずつ解析
以下は完全な呼び出し例です。キーを export した後、ターミナルに貼り付けてそのまま実行できます。HiAPI のベース URL と最小限の JSON ボディを使用しています。
curl -sS -X POST "$HIAPI_BASE_URL/v1/chat/completions" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-6.1-terra",
"messages": [
{"role": "user", "content": "水の循環を 3 文で要約してください。"}
]
}'
各フラグの役割
-X POST は HTTP メソッドを設定します。モデル呼び出しはリクエストボディを伴うため POST です。
-sS は curl に進捗情報を隠させつつ、実際のエラーは表示させます。-S がないと、失敗したリクエストはただの沈黙に見えるかもしれません。
-H "Authorization: Bearer $HIAPI_API_KEY" は認証情報を付加します。Bearer プレフィックスとそれに続く 1 つのスペースは認証スキームの一部です。どちらかを省略すると認証失敗につながり、しかもヘッダーとは無関係に見えます。
-H "Content-Type: application/json" はサーバーにボディが JSON であることを伝えます。Content-Type の欠落を許容するゲートウェイもありますが、それに頼るべきではありません。
-d '...' はリクエストボディです。シングルクォートはシェルによる JSON の展開を防ぎます。ファイルを使いたい場合は、-d '...' を -d @payload.json に置き換え、JSON を lint や diff ができるファイルに入れてください。
時間を節約する 2 つの習慣
第一に、失敗したときは -i を付けてレスポンスのステータス行とヘッダーを表示させましょう。200 を返しているのにボディが異常なレスポンスと、401 や 422 とでは読み方がまったく異なり、この違いはすぐに確認したいはずです。第二に、出力を JSON フォーマッタに通しましょう(curl ... | python -m json.tool)。これでネストしたレスポンスフィールドが読みやすくなります。どちらのフラグも副作用がなく、呼び出しを調整する際に頻繁に使うことになるでしょう。
呼び出しを Python に移植する
Python 版は curl 呼び出しの意味を正確に保つべきです:同じ URL、同じヘッダー、同じボディ、同じパース方法。標準ライブラリを使えば依存関係をゼロに保て、移植の過程も一目瞭然になります。
import json
import os
import urllib.request
HIAPI_BASE_URL = os.environ["HIAPI_BASE_URL"].rstrip("/")
HIAPI_API_KEY = os.environ["HIAPI_API_KEY"]
def call_terra(prompt: str, model: str = "gpt-6.1-terra") -> dict:
body = {
"model": model,
"messages": [{"role": "user", "content": prompt}],
}
req = urllib.request.Request(
f"{HIAPI_BASE_URL}/v1/chat/completions",
data=json.dumps(body).encode("utf-8"),
headers={
"Authorization": f"Bearer {HIAPI_API_KEY}",
"Content-Type": "application/json",
},
method="POST",
)
with urllib.request.urlopen(req, timeout=120) as resp:
return json.loads(resp.read().decode("utf-8"))
result = call_terra("水の循環を 3 文で要約してください。")
print(json.dumps(result, indent=2))
移植に関する注意
ヘッダーはリクエストオブジェクト内で一度に構築され、2 つの -H フラグに対応しています。ボディは Python の dict で、json.dumps でシリアライズされます。これはプログラム上、シングルクォートで囲んだ curl のボディと等価であり、さらに Windows シェルでのクォート問題も解消します。
この関数はモデルをデフォルト値付きの引数として受け取るため、リテラルをあちこちで繰り返す必要がありません。これによりモデル名の信頼できる唯一の情報源が確保されつつ、意図的に上書きすることも可能になります。
ここでは正常系を読みやすく保つため、エラー処理を意図的に省略しています。実際のコードでは、urlopen 呼び出しをラップして HTTP エラー例外を捕捉し、エラーオブジェクトから .read() を読んでサーバーメッセージを確認してください。タイムアウトは明示的に設定されています。無限にブロックすると、最終的にワーカーがハングしてしまいます。
httpx や requests を好む場合、対応は 1 対 1 です:requests.post(url, headers=..., json=body, timeout=120)。json= パラメータが dict をシリアライズしてくれます。意味は何も変わりません——それこそが要点です。curl 呼び出しが契約であり、Python はその第二の表現形態にすぎないのです。
単発呼び出しと非同期タスクのどちらを選ぶか
すべてのリクエストが待つに適しているわけではありません。判断は作業にかかる時間の長さと、呼び出し元がブロックに耐えられるかどうかに依存します。
単発呼び出しが適している場合
短いインタラクティブなリクエスト——要約、分類、小さな書き換え——は通常の同期呼び出しにすべきです。レスポンスはインラインで返り、それをパースして返すだけです。これが最も単純なコードパスであり、テストでも最も推論しやすいものです。ユーザーが UI で待っているなら、同期が期待される形です。
タスクとして扱うべき場合
作業が長時間にわたって実行される場合や、大量の成果物を生成し、接続の中断が高コストなやり直しを意味する場合は、呼び出しを非同期として扱うべきです。こうしたケースでは、リクエストを送信し、タスク識別子を受け取り、終端状態に達するまでポーリングします。送信自体はすぐに返るため、ワーカーや HTTP ハンドラーがプロセス全体を通じて占有されることはありません。
実用的なルール:呼び出しを「成功させる」ために異常に長いクライアントタイムアウトを設定していることに気づいたら、それがタスクに切り替えるシグナルです。タイムアウトはスケジューリングの仕組みではありません。
切り替えを低コストに保つ
コードベースを無関係な 2 つのクライアントに分裂させないでください。2 つのパスを、即時結果またはタスクハンドルを返す 1 つの関数の背後に包み、呼び出し元に必要なモードを要求させましょう。これにより、リクエスト構築——モデル、認証、ボディ——は共有されたまま、待機戦略だけが異なる形になります。これはデバッグの一貫性も意味します:ボディが小さいときに、非同期の失敗を同期で再現できるのです。
プラットフォームの挙動を推測するのではなく、自分のワークロードに正直になりましょう。リクエストが通常数秒で完了するなら、同期のままにして複雑さを避けてください。非同期パスの価値は本当に遅いリクエストに現れるのであって、すべてのリクエストにあるわけではありません。
API を叩きまくらずに完了状態をポーリングする
ポーリングループには 3 つの責務があります:礼儀正しく十分に待つこと、最終的に停止すること、終端状態を明確に提示すること。以下はコンパクトなバージョンです。
import time
def poll_until_done(fetch_status, job_id, interval=3.0, timeout=600.0):
deadline = time.monotonic() + timeout
while True:
status = fetch_status(job_id)
state = status.get("status")
if state in ("succeeded", "failed", "cancelled"):
return status
if time.monotonic() > deadline:
raise TimeoutError(f"job {job_id} not finished after {timeout}s")
time.sleep(interval)
なぜこの構造なのか
fetch_status は注入されるため、HTTP 経由でポーリングするのであれ内部キューに対してポーリングするのであれ、同じループが機能します。これにより、最初に 2 回 running を返してから succeeded を返す偽オブジェクトを使ったテストも容易になります。
状態チェックは単一の "done" 文字列を調べるのではなく、終端状態の明示的な許可リストです。終端状態はさまざま——成功、失敗、キャンセル——であり、1 つの状態だけを探してタイムアウトまで空回りするのではなく、いずれかが現れた時点で停止したいはずです。
期限には time.monotonic() を使うため、壁時計の調整によって待機時間が意図せず延びたり縮んだりすることはありません。
ポーリング間隔の選び方
数秒から始め、タイトループでポーリングしたくなる衝動を抑えましょう。サブ秒間隔はタスクを早く完了させることなく、リクエスト予算を消費し負荷を増やします。タスクが長くなると予想される場合は、穏やかなバックオフを検討してください——例えば、チェックごとに間隔を上限まで増やしていくなどです。上限は適度に保ちましょう。目標はリクエストを減らすことであって、数分間の盲点を作ることではありません。
タイムアウトは任意ではない
常にタイムアウトを設定し、作業に対して寛容でありながら有限にしましょう。発動したとき、空の結果を静かに返してはいけません。例外を投げ、タスク識別子を記録し、再試行するかどうかは呼び出し元に決めさせてください——ポーリングがタイムアウトしたタスクはまだ実行中かもしれず、盲目的な再送信は作業の重複を招く可能性があります。
出力をダウンロードして保存する
レスポンスに成果物への参照——URL、ファイル識別子、ストレージキー——が含まれているなら、それらは一時的なものとして扱ってください。期限切れになったり、アカウントの範囲に限定されたり、リトライをまたいで生き残れなかったりする可能性があります。永続的なコピーは自分で保存したものです。
取得して永続化するパターン
タスクが完了したら、終端状態から成果物参照を読み取り、一度取得し、自分が管理する保存先に書き込みます:スクリプトならローカルパス、本番に近い用途ならオブジェクトストレージバケットです。タスク識別子を保存したファイルと一緒に記録しておけば、成果物を生成したリクエストまで追跡できます。
def save_output(fetch_bytes, artifact_url, dest_path):
data = fetch_bytes(artifact_url)
with open(dest_path, "wb") as f:
f.write(data)
return dest_path
サイレントなデータ損失を防ぐ実践
一時パスに書き込み、成功後にリネームすることで、失敗したダウンロードが有効に見える途中までしか書かれていないファイルを残すことはありません。レスポンスがバイト数やチェックサムを公開していれば、検証してください。大きなファイルは、ペイロード全体をメモリに読み込むのではなく、ディスクにストリーミングで書き込みましょう。
最終状態呼び出しの生レスポンスも保持しておきましょう。JSON の保存は低コストで、誰かがファイルがおかしいように見える理由を尋ねてきたとき、モデル名、タイムスタンプ、タスク id を含む状態ドキュメントは、出来事を再構築する最速の方法です。
最後に、取得と処理を分離してください。まずダウンロードして保存し、それから変換します。ダウンロードしながらパースすると、一時的なネットワークエラーがフォーマットのバグに見えてしまい、間違った問題の解決に午後を費やすことになります。
よくある故障をデバッグする
ほとんどの故障は 4 つのカテゴリに収まります。それぞれに素早い切り分けテストがあります。
認証エラー
症状:リクエストボディのどの内容も問題になる前に 401 や 403 が返る。キーが実際に読み込まれているか確認してください——値ではなく、環境変数が設定されているかどうかを出力しましょう。Bearer プレフィックスとそれに続くスペースを検証してください。ヘッダー名の綴りが正しく、プロキシに剥がされていないことを確認してください。同じキーが curl では動くのにアプリで動かないなら、アプリのヘッダー構築方法が異なります。リテラルのヘッダー dict を curl のフラグと比較してください。
リクエストボディの形式エラー
症状:ボディに関するメッセージを伴う 400 や 422。送信前に JSON を検証してください——json.dumps は意図せず構築した dict を含め、ほとんど何でもシリアライズしてしまいます。設定ファイルから貼り付けた末尾のカンマ、文字列であるべきなのにリストで渡しているもの、必須フィールドの欠落をチェックしましょう。第 1 節の最小ボディまで縮小し、失敗するまでフィールドを 1 つずつ戻していってください。
モデル名の誤り
症状:不正なリクエストやモデル未検出のメッセージに見える拒否。モデル文字列は正確に gpt-6.1-terra でなければなりません。よくある原因:ヘルパー関数内の古いデフォルト値、異なる値を保持している環境変数、あるいはコピー&ペーストではなく再入力によるタイプミス。モジュール定数として保ち、重複を grep で探しましょう。
ポーリングのタイムアウト
症状:ループがタイムアウトで終了するのに、タスクはまだ生きている。まず、ポーリングしているタスク識別子が送信したものと同一であることを確認してください——送信時に両方を出力するのが最も簡単なチェックです。次に終端状態のリストを確認してください:予期していない状態があると、ループが永遠に回り続けます。最後に、間隔と期限が実際のタスク時間と一致しているか確認してください。短いリクエスト向けに調整された期限は、長いリクエストでは必ず発動します。タイムアウト時には最後の状態ペイロードを記録し、諦めた時点でタスクがどの状態にあったかを把握できるようにしましょう。
これらは順番に——認証、ボディ、モデル、ポーリング——対処してください。各層がその下の層を覆い隠すからです。壊れたヘッダーは、あらゆるボディを間違っているように見せてしまいます。
関連記事
すべての記事を見る →-
チュートリアル
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 チュートリアル
リクエスト優先の実践デモ:プロンプトから動画クリップへの呼び出しを構築し、リクエストを送信し、完了状態をポーリングし、結果をダウンロードする