HiAPI
ブログ

チュートリアル

HiAPI API で seedream-4.5/image-to-image を呼び出す方法:curl と Python

実行可能なリクエストを一通り実演し、複数の参照画像入力、非同期ポーリング、出力処理を解説

Oct 31, 2026
深墨绿底上,两组宽扁丝带以平纹编织方式交错成开放式网格,穿插克制的黄铜色与深松绿色条带,为教程文章封面图。

このエンドポイントの役割と選択すべき場面

seedream-4.5/image-to-image は、1 枚または複数の参照画像とテキストプロンプトから新しい画像を生成します。これは HiAPI の image-to-image シリーズに属します。視覚的なコンテキストを提供し、モデルが指示に従ってそれを再解釈します。これが text-to-image との決定的な違いです。text-to-image ではプロンプトが唯一の入力であり、モデルがあらゆるものをゼロから創造します。

視覚的な結果を既存の素材に基づかせたい場合には image-to-image を選択してください。既存アセットのスタイルを刷新する、製品の形状やレイアウトを保ちながらライティングや背景を変える、被写体を失わずに構図を反復する、あるいは同じ参照画像を再利用して一連の結果の見た目を一貫させる、といったケースです。

参照素材がなく、テキストでシーンを説明したいだけなら text-to-image を選択してください。

このエンドポイントは非同期です。タスクを送信して識別子を受け取り、タスクが終了状態に達するまでポーリングし、その後で出力を取得します。最初からこのライフサイクルを前提に計画してください。本ガイドの残りでは、実行可能なリクエストを組み立て、その後で堅牢化していきます。

実行可能な curl リクエストを構築する

このエンドポイントへのすべてのリクエストは同じ基本構造を持ちます。フィールドを一つずつ見ていきましょう。

エンドポイントとメソッド

リクエストは HiAPI ベース URL 配下の image-to-image ルートに POST で送信し、モデル識別子 seedream-4.5/image-to-image を使用します。モデル名はパスの一部ではなく、リクエストペイロード内の値です。

認証

HiAPI API キーを bearer トークンとして Authorization ヘッダーで渡します。シェル履歴やソース管理に入らないよう、環境変数に保存してください:

export HIAPI_API_KEY="your-key-here"

最小限のリクエストペイロード

curl -sS -X POST "$HIAPI_BASE_URL/v1/images/generations" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "seedream-4.5/image-to-image",
    "prompt": "restyle this photo as a warm studio portrait",
    "image": "https://example.com/reference.jpg"
  }'

フィールドごとの説明:

  • model — リテラル識別子 seedream-4.5/image-to-image。
  • prompt — 参照画像に対して何を行うか。被写体だけでなく変換内容を説明してください。
  • image — 参照入力。単一画像の場合は 1 つの値です。複数の参照画像については次の節で説明します。

最初のレスポンスを読む

この呼び出しが返すのは完成した画像ではなく、受け付けられたタスクです。レスポンスボディからタスク識別子を取得して保持してください。ポーリング時に必要になります。200 系ステータスを「画像の準備完了」と解釈しないでください。

クライアントコードを書く前に、まずこのリクエストをそのまま一度実行してください。curl で失敗するリクエストペイロードは Python でも同じ理由で失敗しますが、シェルでのデバッグの方が速いです。

リクエストを Python に移植する

Python 版ではリクエストペイロードをバイト単位で同一に保ち、トランスポート層と認証処理だけを置き換えます。この分離が要点です。curl が動作すれば、Python も初回で動作するはずです。

import os
import requests

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

payload = {
    "model": "seedream-4.5/image-to-image",
    "prompt": "restyle this photo as a warm studio portrait",
    "image": "https://example.com/reference.jpg",
}

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

resp = requests.post(
    f"{BASE_URL}/v1/images/generations",
    json=payload,
    headers=headers,
    timeout=30,
)
resp.raise_for_status()
job = resp.json()
print(job["id"])

以下の説明は後々の時間を節約します:

  • リクエストペイロードを Python の dict として構築し、シリアライズは json= に任せてください。手書きの JSON 文字列は引用符エラーの温床です。
  • 認証構築はヘルパー関数にまとめてください。ヘッダー形式が変わった場合、修正は一箇所で済みます。
  • 送信呼び出しにも常に明示的な timeout を渡してください。送信ステップでのソケットハングは、モデルの遅さと区別できません。
  • 返されたタスク識別子は、ログから再導出するのではなく、すぐに保存してください。

以降の節のポーリングコードは同じ BASE_URL、API_KEY、headers を使用するので、モジュールスコープか小さなクライアントクラスに置いてください。

複数の参照画像を渡す

複数参照画像入力は image-to-image リクエストで最もよくある失敗箇所です。多くの場合、モデルが 3 枚の画像を受け取ったのに、プロンプトがそのうち 1 枚しか説明していないことが原因です。

入力を明示的に構成する

参照画像を同じ image フィールドの下にリストとして並べ、期待する順序で配置し、プロンプトで各位置の役割を指定します:

payload = {
    "model": "seedream-4.5/image-to-image",
    "prompt": (
        "Use the first reference for the subject's pose, "
        "the second for the background setting, "
        "and the third for color grading. Keep the subject sharp."
    ),
    "image": [
        "https://example.com/pose.jpg",
        "https://example.com/background.jpg",
        "https://example.com/grading.jpg",
    ],
}

位置より注釈が優れている理由

リスト順序は約束事ですが、その順序をどう解釈するかをモデルに伝えるのはプロンプトです。曖昧なプロンプトと空疎な配列では、参照画像が衝突したときにモデルはどれを優先すべきか推測せざるを得ません。プロンプトで各画像の役割を指定すれば、暗黙の位置前提を明示的な指示に変えられます。

実用的なルール:

  • 参照画像の数は少なく保ち、それぞれに明確な目的を持たせてください。余分な参照画像は結果を豊かにするのではなく薄めます。
  • 衝突解決を明示してください。ポーズ参照と背景参照のフレーミングが矛盾する場合、どちらを優先するかを述べてください。
  • 安定した公開アクセス可能な URL を使うか、同等のアップロード機構を使ってください。タスクの途中で 404 を返す参照画像はリクエスト全体を失敗させます。

出力が特定の参照画像を無視した場合、通常の解決策はモデルを再呼び出しすることではなく、その画像の役割をより明確なプロンプト文で指定することです。

非同期タスクのライフサイクルを扱う

画像生成には時間がかかるため、このエンドポイントはタスクベースです。ライフサイクルを 4 つの独立したステップとして捉え、それぞれコードを書きましょう。

1. 送信

先ほど示した POST を送信します。他の何よりも前に、レスポンスのタスク識別子を永続化してください。

2. ポーリング

同じ認証ヘッダーを使ってタスクルートに GET リクエストを送り、ステータスが終了状態に達するまで続けます:

import time

def poll_job(job_id, interval=2.0, max_interval=30.0, deadline=300.0):
    start = time.monotonic()
    while True:
        r = requests.get(
            f"{BASE_URL}/v1/images/generations/{job_id}",
            headers=headers,
            timeout=30,
        )
        r.raise_for_status()
        data = r.json()
        status = data["status"]
        if status in ("succeeded", "failed"):
            return data
        if time.monotonic() - start > deadline:
            raise TimeoutError(f"job {job_id} not finished within {deadline}s")
        time.sleep(interval)
        interval = min(interval * 2, max_interval)

3. 終了状態を検出する

ループを終わらせる結果は 2 種類だけです:成功または失敗です。それ以外はすべて待機を続けることを意味します。出力が即時であると仮定してはならず、実際の時間的な締め切りなしに固定回数のポーリングを行ってもいけません。

4. 結果を取得する

次の節で扱います。

バックオフは重要です。詰め込んだ sleep(0.1) のループはレート予算をステータス確認に費やします。約 2 秒から始めて上限まで倍々にすれば、API を圧迫せずに低レイテンシを保てます。

出力を取得して保存する

ポーリングが成功ステータスを返したとき、レスポンスは出力参照を運びます。返されるのは生成画像へのポインタであり、必ずしもバイト列そのものではありません。

意図的にダウンロードする

data = poll_job(job_id)
url = data["output"][0]["url"] if isinstance(data["output"], list) else data["output"]

with requests.get(url, stream=True, timeout=60) as r:
    r.raise_for_status()
    with open(f"{job_id}.png", "wb") as f:
        for chunk in r.iter_content(chunk_size=8192):
            f.write(chunk)

固定パターンを仮定するのではなく、最初の実タスクで観察した実際のレスポンス構造に合わせてパスを調整してください。

手戻りを防ぐ保存ルール

  • ファイルはタスク識別子で命名してください。一意で追跡可能であり、成果物をそれを生成したリクエストに紐づけます。
  • 速やかにダウンロードしてください。出力リンクには期限がある場合があるため、成功ポーリング後すぐに永続化し、後で使うために URL をキャッシュしないでください。
  • 大きなファイルは丸ごとメモリに読み込まず、ストリーミング処理してください。
  • プロンプトと参照画像リストをファイルと一緒に記録してください。出力が想定外だったとき、入力がデバッグ記録になります。

確実な再試行が必要な場合、タスク識別子を保存しておけば、2 回目の生成に課金されることなく、再ポーリングや再ダウンロードができます。

エラー、再試行、コスト管理

ほとんどの失敗は 3 つのカテゴリのいずれかに属し、それぞれ異なる扱いが必要です。

クライアントエラー

不正な形式のリクエストペイロード、model の欠落、アクセス不能な参照画像 URL、誤ったキー。これらは即座に返り、再試行しても再び返ります。リクエストを修正してください。ループで再試行しないでください。送信前に参照画像 URL が解決可能か検証してください。

一時的なサーバーおよびネットワークエラー

タイムアウト、接続リセット、レート制限レスポンス。これらは指数バックオフとジッターを用いて再試行する価値があり、最大試行回数を小さく上限設定します。パイプライン全体ではなく、特定のステップ(送信、ポーリング、ダウンロード)を再試行してください。送信成功後に再送信することこそが、意図しない重複課金の原因です。

タスクレベルの失敗

タスクが失敗の終了状態に達します。同じタスク識別子で再試行しても役に立ちません。修正したプロンプトや参照画像セットで再送信するかどうかを明確に判断し、失敗理由を記録して、入力エラーと一時的障害を区別できるようにしてください。

def with_retries(fn, attempts=4, base=1.0):
    for i in range(attempts):
        try:
            return fn()
        except requests.RequestException:
            if i == attempts - 1:
                raise
            time.sleep(base * (2 ** i))

コスト管理

課金は生成に結びついているため、高くつくエラーは重複送信です。ポーリングには実際の時間的締め切りを設け、ポーリング前にタスク識別子を保存して送信を冪等に保ち、同時実行数を制限して、再試行の嵐が数十の課金タスクに膨れ上がらないようにしてください。すべての送信とそのタスク識別子を記録してください。そのログが支出の監査証跡になります。

これら 3 つの関心事を一度クライアントに組み込めば、同じコードで単一参照画像と複数参照画像のリクエストを変更なしに扱えます。

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

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

APIドキュメントを読む