チュートリアル
HiAPI API で nano-banana を使う方法:curl、Python、実行可能なリクエスト
初回リクエストから出力のダウンロードまでの実践的な全フロー
nano-banana リクエストの全体像
コードを書く前に、nano-banana 呼び出しが HiAPI 上でどのようなライフサイクルをたどるかを知っておくと役立ちます。すべてのリクエストは同じパターンに従います。必要な内容を記述した JSON リクエストボディを送信すると、HiAPI がそのタスクを受け付け、完了済みの結果を直接返すか、または完了状態をポーリングするためのタスクハンドルを返します。
その形態を定義する特徴はいくつかあります:
- 1 つのエンドポイント、1 つの API キー。 すべての nano-banana トラフィックは HiAPI の統一インターフェースを経由します。HiAPI キーで認証し、上流に直接触れることは決してありません。
- JSON イン、JSON アウト。 リクエストボディは JSON です。レスポンスも JSON で、インラインの結果か、タスク id とステータスフィールドを含みます。
- テキストは必須、画像は任意。 最低限、プロンプトを送信する必要があります。参照画像を追加して、被写体、スタイル、キャラクターを誘導できます。
- 非同期フレンドリー。 画像生成は一般的な HTTP タイムアウトより時間がかかる場合があるため、API は非同期タスクをサポートしており、完了するまでポーリングできます。
リクエストボディの概略構造:
{
"model": "nano-banana",
"prompt": "a description of the image you want",
"image_input": ["https://.../reference.jpg"],
"consistency": "character",
"async": true
}
非同期時のレスポンス:
{
"id": "job_abc123",
"status": "queued",
"created_at": "..."
}
タスクが完了して取得すると、status は succeeded(または failed)になり、結果ペイロードに出力 URL が含まれます。この 2 段階モデルを覚えておいてください。このチュートリアルの後半はすべてこの上に成り立っています。
実行可能な curl リクエスト
これは画像を生成できる最小のリクエストです。HIAPI_API_KEY がシェルにエクスポート済みであることを前提としています:
curl -X POST https://api.hiapi.ai/v1/images/generations \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "nano-banana",
"prompt": "a cozy reading nook by a rain-streaked window, warm lamp light, watercolor style",
"async": true
}'
フィールドごとの説明:
model—nano-bananaに設定します。HiAPI はこれに基づいてリクエストをルーティングします。prompt— 唯一厳密に必須の創作フィールドです。被写体、構図、スタイルを具体的に記述してください。曖昧なプロンプトは、期待外れの出力を生む最も一般的な原因です。async—trueの場合、API は即座にタスクハンドルを返します。完了までブロックしたい場合はfalseにできますが、より長い応答時間を覚悟してください。image_input— 任意の参照画像 URL の配列です。テキストのみの画像生成では省略します。consistency— 任意の画像間のキャラクターまたは被写体の制御です。詳細は後述します。
現在のバージョンがどのオプションパラメータをサポートしているか不明な場合は、フィールドを追加する前に HiAPI のそのモデルのドキュメントページを確認してください。未知のフィールドはパラメータによって無視されたり拒否されたりする可能性があります。
コマンドを実行して JSON を確認してください。async が true の場合、画像ではなくタスクオブジェクトが返ります。id を保存してください。それが次のステップのハンドルです。
リクエストを Python に移植する
curl 呼び出しはそのまま翻訳できます。ここでの目標は単発のスクリプトを書くことだけでなく、パイプラインに組み込める再利用可能なラッパーを作ることです。
import os
import time
import requests
BASE = "https://api.hiapi.ai"
HEADERS = {
"Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}",
"Content-Type": "application/json",
}
def create_image(prompt, image_input=None, consistency=None, async_job=True):
body = {"model": "nano-banana", "prompt": prompt, "async": async_job}
if image_input:
body["image_input"] = image_input
if consistency:
body["consistency"] = consistency
resp = requests.post(
f"{BASE}/v1/images/generations",
headers=HEADERS,
json=body,
timeout=60,
)
if resp.status_code >= 400:
raise RuntimeError(f"create failed: {resp.status_code} {resp.text}")
return resp.json()
単に動くだけでなく堅牢にするポイント:
- 明示的なタイムアウト。 常に指定してください。「永遠に待つ」というデフォルトは、一時的なネットワーク問題をプロセスのハングに変えてしまいます。
- パース前にステータスを確認。 エラーレスポンスに対して例外を送出すれば、不正なリクエストがその後インデックスする JSON ボディを静かに返すことはありません。
- 任意フィールドは null ではなく省略。
"image_input": nullを送るのではなく、値があるときだけキーを追加します。これによりログが読みやすくなり、nullが「なし」なのか「未設定」なのかの曖昧さを避けられます。
関数にラップすることでリトライも簡単になります。失敗時に例外を送出するので、呼び出し側はリクエストコードに触れることなくリトライループで包むことができます。単一画像からバッチ処理に移行すると、この分離が報われます。
テキストと画像の入力送信
テキストから画像を生成するのは単純なケースです。nano-banana の面白い部分は、1 つのリクエストでプロンプトと参照画像を組み合わせることです。image_input を URL 配列として渡し、プロンプトでそれらをどう扱うかを記述します。
result = create_image(
prompt="the person in the first image standing in a Tokyo alley at night, neon reflections, cinematic",
image_input=["https://example.com/portrait.jpg"],
)
マルチモーダル混合の実践的なアドバイス:
- 役割を明確に。 複数の画像を送る場合は、どれが被写体を提供し、どれがスタイルを提供し、どれが背景を提供するかをプロンプトで明示してください。「これらの画像を組み合わせて」は曖昧ですが、「画像 1 の顔と画像 2 の照明を使って」はそうではありません。
- 到達可能で安定した URL を使う。 API は画像を取得できなければなりません。数分で期限切れになる署名付き URL はリアルタイム呼び出しには問題ありませんが、後でキューに入れたタスクをリトライすると失敗します。
- クリーンな参照を優先。 明るくクリアなポートレートは、ごちゃごちゃした集合写真よりも忠実な人物類似性を生みます。ガベージイン・ガベージアウトは、テキストよりも画像条件付けで顕著です。
- まずプロンプトを反復し、参照画像ではない。 類似性がずれている場合は、元画像を交換する前に被写体の記述方法を調整してください。多くの場合、そちらの方が手間が少ない変更です。
参照画像に複数回の生成で再利用する予定の人物が含まれている場合、それはキャラクター一貫性の領域に入ります。それが次の節です。
キャラクター一貫性オプション
キャラクター一貫性は、具体的なニーズに答えるものです。同じキャラクターや被写体が登場するように見える複数の画像を生成することです。nano-banana はこれを別個のモデルではなく、任意の制御として公開しています。
高レベルでは、トレードオフは概ね次のとおりです:
- 一貫性制御なし。 単発の画像に最適です。速くて簡単ですが、再生成するとキャラクターがドリフトする可能性があります。背景、物体、独立したイラストに使用します。
- 参照ベースの一貫性。 1 枚以上のキャラクター画像を提供し、一貫性オプションを設定してモデルにそれらをアンカーさせます。これはストーリーボード、コミック、製品シーケンスに正しい選択です。フレーム間で同一性を保つ必要があるためです。代償として、まず良い参照画像が必要です。
- テキスト記述による一貫性。 各プロンプトでキャラクターを詳細に記述します。髪色、服装、体型、目立つ特徴などで、参照を使いません。元の写真がない場合に有用ですが、規律が求められます。記述を再利用可能な定数に置き、呼び出し間でドリフトしないようにしてください。
シリーズの実用的なワークフロー:
- 一貫性なしで最初のフレームを生成し、キャラクターが正しく見えるまでプロンプトを反復します。
- その出力を参照画像として使用します。
- 一貫性制御を有効にして後続のフレームを生成し、参照を固定したままにします。
- それでもキャラクターがドリフトする場合は、プロンプトを短くして参照に多くの仕事をさせます。参照と矛盾する長いプロンプトはモデルを混乱させます。
一貫性はノブであり、保証ではありません。十分に調整されたパイプラインでも、出力をレビューし、少数のフレームを再生成することを想定してください。
非同期タスクのポーリング
async: true で送信すると、タスク id が返ります。ポーリングは単純ですが、細部は見た目以上に重要です。
最小かつ正しいポーラー:
def wait_for_job(job_id, timeout=300, initial_delay=2, max_delay=15):
deadline = time.time() + timeout
delay = initial_delay
while time.time() < deadline:
resp = requests.get(
f"{BASE}/v1/images/generations/{job_id}",
headers=HEADERS,
timeout=30,
)
resp.raise_for_status()
data = resp.json()
status = data.get("status")
if status == "succeeded":
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, max_delay)
raise TimeoutError(f"job {job_id} did not finish within {timeout}s")
各選択の理由:
- 上限付きのほぼ指数バックオフ。 数秒から始め、1.5 倍を掛けて上限まで上げます。30 秒のタスクに対して 1 秒固定のポーリングは数十のリクエストを無駄にします。30 秒固定のポーリングは速いタスクに不要な遅延を追加します。
- ハードな締め切り。 これがないと、スタックしたタスクがワーカープロセスを無期限にブロックします。むしろ大きな音で失敗させましょう。
- 終端状態の処理。
failedとcancelledはポーリングを続ける状態ではなく、例外として扱います。失敗状態のタスクが次のポーリングで成功することは決してありません。 - 毎回の反復で新しい GET を発行。 最初のレスポンスをキャッシュして更新されると仮定しないでください。
バッチ作業では、まずすべてのタスクを送信し、id を収集し、ループでポーリングします。これにより生成時間をタスク間で重ね合わせ、直列化せずに済みます。
出力のダウンロードと検証
タスクが成功すると、結果ペイロードに出力 URL が含まれます。ダウンロードは通常の GET ですが、確認する前にそのバイト列が画像であると仮定すべきではありません。
def download_image(url, path):
with requests.get(url, stream=True, timeout=60) as r:
r.raise_for_status()
content_type = r.headers.get("Content-Type", "")
if not content_type.startswith("image/"):
raise RuntimeError(f"unexpected content type: {content_type}")
with open(path, "wb") as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
return path
すべてのダウンロードで行う価値のある健全性チェック:
- Content-Type。 画像を要求したのに
application/jsonやtext/htmlが返ってきた場合、実際にはエラーページを受け取っています。ヘッダーを確認すれば、壊れたファイルを書き込む前に捕捉できます。 - ストリーミング。 レスポンスボディ全体をメモリに読み込むのではなく、ストリーミングでディスクに書き込みます。並列ダウンロード時にメモリを安定させられます。
- サイズがゼロでないこと。 0 バイトのファイルは、HTTP ステータスが正常でも問題が起きたことを意味します。
- ログにチェックサムまたはバイト数を記録。 タスク id と一緒に記録すれば、壊れたファイルを特定の生成に追跡できます。
出力 URL は一時的な場合があります。結果を保持する予定なら、リンクを後で使うために保存するのではなく、同じ実行中に自分のストレージへダウンロードしてください。
よくある失敗ポイント
ほとんどの問題はいくつかのカテゴリに分類されます。モデルの動作がおかしいと仮定する前に、これらを確認してください。
認証とルーティング
- 欠落または不正な形式の
Authorizationヘッダーは、画像ではなく認証エラーを返します。ヘッダーがBearer <key>と、間にスペースを 1 つ入れて読めることを確認してください。 - 誤った
model値を送ると、誤った場所にルーティングされるか、単に失敗します。正確にnano-bananaである必要があります。
リクエストボディの問題
- 曖昧なプロンプト。 「出力が悪い」の最も一般的な単一原因です。他の何かを変更する前に、構図、照明、スタイルの詳細を追加してください。
- 到達不能な画像 URL。 404 を返す、認証が必要、またはローカルパスを指す参照 URL は失敗するか、静かに無視されます。自分で URL を一度取得して動作を確認してください。
async: falseで長いプロンプトを送信 し、クライアントのタイムアウトに達する。タイムアウトを延ばすのではなく、非同期に切り替えてポーリングしてください。
タスク処理
- 存在しないタスクをポーリング。 タスク id のタイプミスは失敗のように見える not found エラーを生みます。送信した id を記録してください。
failedをまだ実行中として扱う。 常に終端状態でポーリングループを終了してください。- 締め切りがない。 バッチタスクで最もコストの高いバグ:1 枚のスタックした画像が実行全体を引きずります。
出力処理
- Content-Type を確認しない。 前述のとおり、
.png拡張子で保存されたエラーページは、後で壊れた画像のように見えます。 - 生成から数時間後に期限切れの結果 URL に依存する。 速やかにダウンロードしてください。
認証、リクエストボディ、タスクライフサイクル、ダウンロードの順にトリアージすれば、ほとんどの障害は数分で解決できます。
関連記事
すべての記事を見る →-
チュートリアル
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 チュートリアル
リクエスト優先の実践デモ:プロンプトから動画クリップへの呼び出しを構築し、リクエストを送信し、完了状態をポーリングし、結果をダウンロードする