画像生成を自動化する方法:テンプレート、job、webhook
この問いへの答えの大半は、テキストボックスのスクリーンショットです。prompt を打ち、ボタンを押し、待ち、ダウンロードし、繰り返す。それは自動化ではありません。あなたが手作業を、速くやっているだけです。
自動とは、輪の中に人がいないことです。データの行が届き、画像が出てきて、その様子は誰も見ていません。そのためには 4 つの部品が要ります。穴の空いた prompt テンプレート、穴を埋めるデータ、処理をブロックせずに受け付けるキュー、そして結果を置く恒久的な場所。当社はその真ん中で API を運用しているので、みなさんが組むパイプラインと、公開 3 週間後に静かに壊れるパイプラインの両方を目にします。

誰も最初に言わない点:いまはもう安い
数字から始めます。それによって、作る気になるものが変わるからです。
Nano Banana 2 Lite で 1,000 枚生成すると $23.80 です。2026年8月26日 時点の当社価格で、1,000 × $0.0238。商品写真 10 万枚 — 実在のカタログ、全 SKU、全カラーバリエーション — で $2,380 です。
選択肢になるティアで同じ数量を並べると、こうなります。
| 枚数 | Nano Banana 2 Lite | Nano Banana 2 | Nano Banana Pro |
|---|---|---|---|
| 100 | $2.38 | $4.00 | $7.50 |
| 1,000 | $23.80 | $40.00 | $75.00 |
| 10,000 | $238.00 | $400.00 | $750.00 |
| 100,000 | $2,380.00 | $4,000.00 | $7,500.00 |
価格および製品条件は変更される場合があります。購入を判断する前に、各プロバイダーの最新価格をご確認ください。Google の Batch または Flex 価格は、スケジューリング・空き状況・処理条件が異なるため、標準的なオンデマンド API request と直接同等ではなく、この比較には含めていません。これは特定の条件に絞った比較であり、E2X があらゆる構成において世界最安であると主張するものではありません。
画像生成の自動化について尋ねてこられる方の多くは、それを「試すには高すぎる」と静かに値付けしています。これは $23.80 の問いです。作ってください。
ステップ 1:prompt を書くのをやめ、テンプレートを書く
一度打つ prompt は文です。機械が 1 万回走らせる prompt はスロット付きのテンプレートで、その違いが仕事のすべてです。
規則はこうです。変わるものはすべてスロットへ、同一でなければならないものはすべて固定テキストへ。この境界を取り違えると集合がぶれます。「背景」が可変側へ漏れたせいで、半分が白背景、半分がグレー背景になる、というふうに。
TEMPLATE = (
"Studio product photograph of a {product_name} in {colour}, "
"centred on a plain warm-grey backdrop, soft directional key light "
"from the upper left, shallow depth of field, editorial catalog styling, "
"no text, no logos, no people"
)
スロットは 2 つ。ライティング、背景、フレーミング、除外指定は凍結されています。400 枚を 400 回の事故ではなく 1 回の撮影に見せるのは、この凍結された半分です。
可変側から外しておくべきものが 4 つ。媒体、光の向き、背景、そして否定の指示です。これらがあなたの視覚的な同一性です。行ごとに動くなら、同一性は存在しません。

prompt を練ること自体は別の記事の話です。自動化にとって大事なのはかたちです。固定された枠、名前の付いた穴、驚きのないこと。
ステップ 2:本当の製品はデータのほう
テンプレートは些細です。仕事が宿るのはデータのほうで、たいていはすでにお持ちのものです。
- 商品カタログのテーブル。SKU ごとに 1 行、名称・色・素材・カテゴリの列。
- ブログ記事の CMS エクスポート。タイトルとトピックが、記事ごとのヘッダー画像のスロットになります。
- アプリケーションのデータベースにある行 — ユーザー投稿の出品、イベントページ、レシピ。
- 広告バリエーションの表計算。見出し 6 種 × 背景 4 種で 24 枚になり、それを人が手でブリーフィングすることはありません。
出どころが何であれ、API に触れる前に辞書のリストへ正規化し、すべての行に安定した ID を持たせてください。結果をレコードへ突き合わせるために必要で、省略した痛みは webhook が飛んできたまさにその瞬間に出ます。
ステップ 3:submit したら、待たない
当社の API は意図して非同期です。job を POST すると即座に job ID が返り、生成は当社側で進みます。
curl -X POST https://api.e2x.ai/v1/jobs/submit \
-H "Authorization: Bearer $E2X_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "google/nano-banana-2-lite/text-to-image",
"input": {
"prompt": "Studio product photograph of a walnut desk lamp in matte black...",
"aspect_ratio": "1:1"
},
"webhookUrl": "https://your-app.example.com/hooks/e2x"
}'
テーブルを job に変える walker はおよそ 30 行です。CSV を読み、行ごとにテンプレートを整形し、submit し、行 ID に対して job ID を記録することで、どれも迷子になりません。
import csv
import os
import time
import requests
API = "https://api.e2x.ai/v1"
HEADERS = {
"Authorization": f"Bearer {os.environ['E2X_API_KEY']}",
"Content-Type": "application/json",
}
TEMPLATE = (
"Studio product photograph of a {product_name} in {colour}, "
"centred on a plain warm-grey backdrop, soft directional key light "
"from the upper left, shallow depth of field, editorial catalog styling, "
"no text, no logos, no people"
)
MAX_IMAGES = 500 # 上限。コストの節を参照
UNIT_COST = 0.0238 # Nano Banana 2 Lite、2026年8月26日 に確認
def submit(row):
body = {
"model": "google/nano-banana-2-lite/text-to-image",
"input": {
"prompt": TEMPLATE.format(**row),
"aspect_ratio": "1:1",
},
"webhookUrl": "https://your-app.example.com/hooks/e2x",
}
for attempt in range(4):
r = requests.post(f"{API}/jobs/submit", json=body, headers=HEADERS, timeout=30)
if r.status_code < 300:
return r.json()["data"]["jobId"]
if r.status_code == 429 or r.status_code >= 500:
time.sleep(2 ** attempt)
continue
raise RuntimeError(f"row {row['id']} rejected: {r.status_code} {r.text}")
raise RuntimeError(f"row {row['id']} still failing after 4 attempts")
with open("catalog.csv") as fh:
rows = list(csv.DictReader(fh))[:MAX_IMAGES]
print(f"submitting {len(rows)} jobs, budget ${len(rows) * UNIT_COST:.2f}")
submitted = {}
for row in rows:
try:
submitted[row["id"]] = submit(row)
except RuntimeError as err:
print(f"skipped: {err}")
print(f"{len(submitted)}/{len(rows)} accepted")
このループがしないことにご注目ください。行が失敗しても止まらないのです。500 行のうち 212 行目の色が null だったなら、返ってくるべきは 499 枚の画像とスキップ 1 件のログであって、午前 3 時のスタックトレースと 0 枚ではありません。
slug は見た目以上に効きます。google/nano-banana-2-lite/text-to-image には capability の接尾辞が付きますが、Nano Banana 2 のテキスト側の slug は接尾辞なしの google/nano-banana-2 です。当てずっぽうは、他の部分は完成して見えるパイプラインに 404 をもたらします。各モデルはパラメーターの一覧を機械可読の仕様ファイルとして公開しています。
ステップ 4:polling より webhook、その理由
polling もできます。GET /v1/jobs/{JOB_ID} は pending、processing、completed、failed、cancelled のいずれかを返し、単発のスクリプトならそれで構いません。
パイプラインには誤った選択で、理由は美しさではありません。polling は、あなたのプロセスの寿命を batch のなかで最も遅い job に結びつけます。Lite の生成は当社側でおよそ 60 秒。500 件 submit すれば、poller は最後尾が終わるまで状態を抱えたまま生き続けなければなりません。デプロイをまたぎ、コンテナの再起動をまたぎ、お使いのサーバーレス基盤の 15 分の timeout をまたいで。そして死ぬときは、どの job が処理中だったかを記した唯一の記録を抱えたまま死にます。
webhook はそれを反転させます。submit して、抜ける。後になって完了した job の POST が届き、あなたはその 1 件だけを、状態を持たずに処理します。
// Express のハンドラー。冪等:同じ jobId が 2 回届きうる。
app.post("/hooks/e2x", express.json(), async (req, res) => {
res.status(200).end(); // 先に応答し、処理は後で
const job = req.body.data;
if (job.status !== "completed") {
await markFailed(job.jobId, job.error?.message ?? "unknown");
return;
}
const already = await findAsset(job.jobId);
if (already) return;
await storeImage(job.jobId, job.outputs[0].url);
});
自分でスクリプトを走らせて見ているときは polling を。誰も見ていないときは webhook を。後者こそが自動化の定義です。
ステップ 5:出力 URL は一時的、そしてここでパイプラインが壊れる
これです。他をすべて飛ばして読むとしても、この節は 2 回お読みください。
data.outputs[0].url は配信用の URL であって、ストレージではありません。いまは動きます。永久には動きません。これを恒久的なものとして扱うもの — product_image_url の列、CMS のフィールド、クライアントへメールしたリンク — は、ステージングではきれいに表示され、2 週間後に画像の壊れたアイコンを見せます。
この失敗が厄介なのは、遅れてやってくるからです。書き込み時には何もエラーになりません。テストは通ります。batch は 500 件の成功を報告します。それから、古いレコードから順に画像が消え始め、誰かが気づいた頃には job を丸ごと流し直さない限り再生成できません。prompt もまた、保存されていなかったからです。
バイト列をダウンロードしてください。自分のバケットに置いてください。自分の URL を保存してください。

import requests
import boto3
s3 = boto3.client("s3")
def store_image(job_id, row_id, source_url):
img = requests.get(source_url, timeout=60)
img.raise_for_status()
key = f"products/{row_id}.jpg"
s3.put_object(
Bucket="my-assets",
Key=key,
Body=img.content,
ContentType=img.headers.get("content-type", "image/jpeg"),
)
return f"https://cdn.example.com/{key}"
ついでに、prompt を画像と並べて保存してください。テキストの列ひとつで、「あのバリエーションを再生成する」と「最初からやり直す」の差になります。
初日から身につけておく価値のある習慣がもう 2 つ。ダウンロードがエラーページではなく画像のバイト列を返したかを確認すること。HTML の入った 200 は、何のためらいもなくバケットへ書き込まれます。そして保存のキーは当社の job ID ではなく、あなたの行 ID にすること。そうすれば再生成が正しいオブジェクトを上書きし、迷子を増やしません。
ステップ 6:量が増えたときに実際に起きること
小さな batch はすべてを隠します。数百件を超えたところで表に出てくるものは、こうです。
部分的な失敗は正常です。 大きな実行では必ず一定の割合が落ちます。コンテンツフィルターに引っかかる prompt、形式の崩れた行、上流の一時的なエラー。500 件のうち 497 件を成功とみなし、その 3 行だけを対象にコマンド 1 つで再試行できるようにしてください。
再試行には支出の上限が要ります。 上限のない再試行ループは、請求書を生むバグです。1 回 $0.0238 は無害に聞こえますが、詰まったループが 4 万回走れば話は別です。コードに固定の回数を置いてください。上の MAX_IMAGES の行が、ここで最も安い保険です。
冪等性は任意ではありません。 webhook は 2 回以上配信されうるものです。ひとつの job ID に対して 2 回走らせても安全でないハンドラーは、重複したアセットと重複した行を残し、しかも発覚するのは最も忙しい週です。
コストには月次の請求書ではなく、その場のカウンターが要ります。 当社の API が返す金額はマイクロセント単位で、1,000,000 が $1 です。batch ごとに合計し、終了時に総額をログへ出してください。毎晩「412 枚、$9.81」と報告してくるパイプラインは、誰も監査しなくてよいパイプラインです。
aspect ratio のデフォルトは驚きを連れてきます。 Lite のデフォルトは 1:1、Nano Banana 2 と Pro は 9:16 です。テンプレートで固定しないと、カタログの半分が黙って縦長になります。
watermark はファイルとともに移動します。 Google は画像モデルが生み出すすべてに SynthID を刻みます。目には見えず、フラグでもなく、その後パイプラインがバイト列に何をしようとも残ります。ブランドや法務を担当する方の承認は、バケットに 2 万個のオブジェクトを書き込んだ後ではなく、パイプラインが存在する前に取ってください。

パイプラインをどのモデルに向けるか
Nano Banana 2 Lite を既定にし、具体的な事情に迫られたときにだけ離れてください。
これは日和見ではなく算数です。$0.0238 は当社が売っている画像を作るもののなかで最安であり、置き換えた初代 Nano Banana より丸 1 世代新しく、パイプライン規模では価格差が積み上がって、財務が見る唯一の数字になります。さらに他にはない aspect ratio を 4 つ持っています — 1:4、4:1、1:8、8:1 — 出力がバナーや縦長の帯であれば、まさに欲しいものです。癖がひとつ。resolution パラメーターがそもそもなく、送ると黙って無視されるのではなくエラーになります。
そこから逸らすのは次の 4 つの場合だけで、それ以外ではありません。
- 1K を超える出力。 Lite は 1K のみです。Nano Banana 2 は基準 $0.04 で 4K に届き、2K が ×1.5、4K が ×2 です。
- 画像の中の判読できる文字。 価格の入った商品カード、ローカライズされたインフォグラフィック。これは $0.075 の Nano Banana Pro の領分で、そこでは 1K と 2K が同額です。2K を取ってください、無料ですから。
- 背景の透過。 切り抜きやステッカーシートには
background: transparentが必要で、それを 1 回の呼び出しで行えるのは当社が扱うなかで GPT Image 2 だけです。予算はカタログ上の素の数字ではなく、1K で $0.0525 を見てください。qualityがどのティアでも 7 倍になります。 - 多数のフレームにわたる同じ顔、同じボトル。 これは量の問題ではなく参照画像の問題で、一貫性についての記事が別にあります。
混在するワークロードなら、大半を Lite で回し、文字や忠実性を要する 2% だけをデータ側の列で Pro へ分岐させてください。パイプライン 1 本、モデル 2 つ、Pro の価格はそれが何かを稼ぐところにだけかかります。
submit と polling の契約をパラメーター単位で追った解説は Nano Banana Pro API チュートリアルに、ティアごとの内訳はモデル比較にあります。どちらもモデルを選び終えていることを前提にしています。まだであれば、text-to-image のモデルに価格付きで並んでおり、カタログには動画側も加わります。
全体を、順番に
スロット付きのテンプレート。安定した ID を持つデータ。上限を決めたうえで job として submit。webhook で受け取る。バイト列を自分のストレージへダウンロード。失敗と支出をログに残す。6 段階、どれも難しくなく、そして飛ばされるのはいつもステップ 5 です。
よくある質問
画像生成はどうやって自動化しますか?
prompt を名前付きスロットのあるテンプレートとして書き、商品カタログや CSV などのデータソースからそのスロットを埋め、埋まった prompt ごとに画像 API へ非同期の job として submit します。結果は polling ではなく webhook で受け取り、画像のバイト列を自分のストレージへダウンロードします。このループのどこにも人は必要なく、それが手作業を速くしたものではなく自動である理由です。
1,000 枚を自動生成するといくらかかりますか?
2026年8月26日 時点の E2X 価格では、Nano Banana 2 Lite で 1,000 枚が $23.80 です。1 request が $0.0238 だからです。同じ量が Nano Banana 2 で $40.00、Nano Banana Pro で $75.00 になります。解像度を上げれば倍率が積み上がりますが、標準的な 1K 出力であれば 1 枚あたりの数字が計算のすべてです。
画像生成の job には webhook と polling のどちらを使うべきですか?
無人で走るものには webhook を使ってください。polling はプロセスの寿命を batch のなかで最も遅い job に結びつけるため、デプロイやサーバーレスの timeout が、どの job がまだ動いているかの唯一の記録を抱えた poller を殺します。webhook なら submit して抜け、完了した job を 1 件ずつ状態なしで処理できます。自分で走らせて見ているスクリプトなら polling で構いません。
生成した画像が、しばらく経つと表示されなくなるのはなぜですか?
画像ではなく、API の出力 URL を保存してしまっているからです。あの URL は一時的な配信用であってストレージではないため、それを保持した列は最初こそ正しく表示され、後になって壊れます。job が完了したらすぐにバイト列をダウンロードし、自分のバケットへ書き、自分の URL を保存してください。画像パイプラインが壊れた状態で世に出る、最もよくある原因です。
自動化した画像パイプラインは、どのモデルを既定にすべきですか?
1 枚 $0.0238 の Nano Banana 2 Lite です。当社のカタログで最も安い画像モデルであり、初代 Nano Banana より 1 世代新しく、batch の規模では価格が他のあらゆる考慮を圧倒します。1K を超える出力のときにだけ Nano Banana 2 へ、画像内の判読できる文字のときにだけ Nano Banana Pro へ、背景の透過のときにだけ GPT Image 2 へ移ってください。
batch のなかの一部の画像が失敗したらどうなりますか?
必ずいくつかは失敗しますし、パイプラインはそれを致命的ではなく正常として扱うべきです。行が弾かれても submit を続け、失敗した ID をエラーとともにログへ残し、失敗ぶんだけを走らせる再試行の経路を用意してください。再試行の回数には上限を。1 回ごとが実際の課金だからです。