チュートリアル
hiapi API で FLUX.1 [schnell] を使った EC 商品画像の生成
すぐに実行できる curl + Python ガイド。カタログ用の白背景画像、シーン画像、バリエーション画像をカバーし、1 枚あたり $0.005。
カタログ撮影は、SKU をオンラインで公開する際の最も遅い工程です。スタジオを予約し、スタイリストを手配し、ソフトボックスを組み、レタッチャーを雇う——$24 のマグカップ 1 つに対してこれでは全く割に合いません。hiapi の flux-schnell/text-to-image なら、そのまま使える白背景の商品画像、シーンヒーロー画像、カラー/素材バリエーション画像を、1 枚 $0.005、エンドツーエンドで約 30 秒 で得られます——この価格帯では、1 枚の撮影コストに悩むことはなくなり、プロンプトとプロンプトのバッチについて考えるようになります。
このガイドでは、実際のワークフローを順に説明します。商品詳細ページに実際に必要な 4 種類の画像、モデルを架空の木製テーブルではなく清潔でシームレスな背景上に保つためのプロンプト構造、ターミナルを壊さずに /v1/tasks 経由で 100 個のバリエーションを一括生成する方法、そして schnell にできない少数のことを事前に計画する必要がある点です。
カタログ作業が flux-schnell に特に向いている理由
hiapi にはより高忠実度のモデル(flux-2、flux-1.1-pro、nano-banana-pro)もあり、ブランドキャンペーンのメインビジュアルならそれらを使うべきです。しかしカタログでは計算が異なります:
- 200 SKU の新商品展開で、各 SKU におよそ 4 枚の画像(白背景、シーン、スケール参考、俯瞰)が必要だと = 800 枚。$0.005 なら $4。$0.05 なら $40。$0.17 なら $136。
schnellは 1〜4 ステップのサンプリングで生成を完了するため、エンドツーエンド(キュー待ち + 生成 + ダウンロード)は私たちの実行では約 30 秒です。flux-2やnano-banana-proは数倍遅くなります。- カタログ画像は商品詳細ページで幅 600px、グリッドで 200px で表示されます。このサイズでは、高級モデルの忠実度の余裕はまったく見分けがつきません。プロンプト遵守度とアスペクト比の妥当性の方が重要です。
このトレードオフは現実のものです:schnell はラベル上の小さく読みやすい文字が苦手で、人が商品を持つ構図も苦手(その場合は nano-banana-pro を使ってください)、複雑なブランドロゴも明らかに苦手です。「商品を清潔な背景に置いて見せて、3 色、俯瞰と斜め 45 度視点」——つまり本来の仕事——では非常に優れています。
実際に生成すべき画像リスト
プロンプトを書く前に、商品詳細ページのテンプレートに何が必要かを決めましょう。有用な出発点のセット:
| 画像タイプ | アスペクト比 | 用途 |
|---|---|---|
| 白背景商品画像 | 1:1 |
商品詳細ページのメイン画像、グリッドのサムネイル |
| 斜め 45 度視点のスタジオ撮影 | 4:3 |
商品詳細ページのギャラリー 2 枚目 |
| シーン / 状況画像 | 3:2 |
商品詳細ページのギャラリー、ソーシャル広告 |
| 俯瞰のフラットレイ | 1:1 |
商品詳細ページのギャラリー、スケール参考 |
| カラー/素材バリエーションのグリッド | 各バリエーション 1:1 |
カラーセレクターのサムネイル |
hiapi の flux-schnell は 1:1, 3:2, 2:3, 4:3, 3:4, 5:4, 4:5, 16:9, 9:16, 21:9, auto をサポートします。それ以外の比率はタスク作成時に拒否されます。
1 回だけの実行:curl で単一の商品画像を生成
token が使えることの最低限の確認。私の前回の実行はエンドツーエンドで 31 秒かかりました:
TOKEN="$HIAPI_API_KEY"
# 1. タスクを作成
TASK=$(curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "flux-schnell/text-to-image",
"input": {
"prompt": "Product packshot: matte ceramic coffee mug, soft beige glaze, centered on seamless white cyclorama, soft diffused studio lighting, subtle contact shadow, 50mm photography, no props, ecommerce hero, clean negative space top and bottom",
"aspect_ratio": "1:1"
}
}' | jq -r '.data.taskId')
echo "task: $TASK"
# 2. 完了までポーリング(success | fail)
while true; do
STATE=$(curl -s "https://api.hiapi.ai/v1/tasks/$TASK" \
-H "Authorization: Bearer $TOKEN")
STATUS=$(echo "$STATE" | jq -r '.data.status')
echo " status=$STATUS"
[ "$STATUS" = "success" ] && break
[ "$STATUS" = "fail" ] && { echo "$STATE" | jq '.data.error'; exit 1; }
sleep 4
done
# 3. ダウンロード(出力 URL は期限切れになるので、すぐ取得する)
URL=$(echo "$STATE" | jq -r '.data.output[0].url')
curl -s -o packshot.jpg "$URL"
echo "saved packshot.jpg"
flux-schnell で特に注意すべき癖:このモデルの入力スキーマは aspect_ratio を受け付けますが、resolution を拒否します。hiapi 上の他の画像モデル(例:flux-2、nano-banana-pro)は両方を受け付けます。もし flux-schnell に {"aspect_ratio": "1:1", "resolution": "1K"} を送ると、次のようになります:
{
"code": 400,
"message": "additional properties not allowed: resolution"
}
resolution を削除してください。必要ありません——schnell は選択したアスペクト比でモデルのデフォルトサイズで出力します。
本番環境:Python で一括生成
実際のカタログ作業では、多数のタスクを投入し、出力が完了したらダウンロードし、失敗したケースをリトライします。これは私たちがある SKU シリーズのプロトタイプを作ったときに使ったスクリプトです:
"""Batch packshot generator for flux-schnell on hiapi.
Reads a CSV of (sku, prompt, aspect_ratio), submits all jobs, polls in parallel,
downloads finished outputs to ./out/{sku}.jpg. Total cost = $0.005 * len(csv).
"""
import csv
import json
import os
import subprocess
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
from pathlib import Path
API = "https://api.hiapi.ai/v1/tasks"
TOKEN = os.environ["HIAPI_API_KEY"]
OUT = Path("./out"); OUT.mkdir(exist_ok=True)
def _post(path, body):
r = subprocess.run(
["curl", "-s", "-X", "POST", path,
"-H", f"Authorization: Bearer {TOKEN}",
"-H", "Content-Type: application/json",
"-d", json.dumps(body)],
capture_output=True, text=True, timeout=60,
)
return json.loads(r.stdout)
def _get(path):
r = subprocess.run(
["curl", "-s", path, "-H", f"Authorization: Bearer {TOKEN}"],
capture_output=True, text=True, timeout=30,
)
return json.loads(r.stdout)
def render(sku: str, prompt: str, ratio: str) -> tuple[str, str]:
"""Submit → poll → download. Returns (sku, status). status='ok' or 'fail:<reason>'."""
body = {"model": "flux-schnell/text-to-image",
"input": {"prompt": prompt, "aspect_ratio": ratio}}
resp = _post(API, body)
task_id = (resp.get("data") or {}).get("taskId")
if not task_id:
return sku, f"fail:submit:{resp.get('message')}"
deadline = time.time() + 180
while time.time() < deadline:
state = _get(f"{API}/{task_id}")
data = state.get("data") or {}
status = data.get("status")
if status == "success":
url = (data.get("output") or [{}])[0].get("url")
if not url:
return sku, "fail:no-output-url"
subprocess.run(["curl", "-s", "-o", str(OUT / f"{sku}.jpg"), url],
timeout=60, check=True)
return sku, "ok"
if status == "fail":
return sku, f"fail:model:{data.get('error', {}).get('code')}"
time.sleep(4)
return sku, "fail:timeout"
def main():
rows = list(csv.DictReader(open("skus.csv")))
print(f"submitting {len(rows)} renders (cost ${0.005 * len(rows):.3f})")
with ThreadPoolExecutor(max_workers=8) as ex:
futures = {ex.submit(render, r["sku"], r["prompt"], r["aspect_ratio"]): r
for r in rows}
for f in as_completed(futures):
sku, status = f.result()
print(f" {sku}: {status}")
if __name__ == "__main__":
main()
skus.csv の 1 行はこのようになります:
sku,prompt,aspect_ratio
mug-beige-01,"Product packshot: matte ceramic mug, beige glaze, seamless white cyclorama, soft studio lighting, subtle contact shadow, 50mm photography, ecommerce hero, clean negative space",1:1
mug-charcoal-01,"Product packshot: matte ceramic mug, charcoal glaze, seamless white cyclorama, soft studio lighting, subtle contact shadow, 50mm photography, ecommerce hero, clean negative space",1:1
本番コードに関する注意:
ThreadPoolExecutor(max_workers=8)はschnellにとって安全な並列度です。それ以上にしてもキューが深くなるだけで、より速く完了することはありません。- 各タスク 180 秒のタイムアウトは保守的な設定です。実際には成功するタスクはすべて 30〜60 秒以内に完了します。タイムアウトが発生する場合はタスクがスタックしていることを意味します——4 つの原因については hiapi タスクのハング/タイムアウト診断ガイド を参照してください。
- 私たちは
successの直後にダウンロードします。output[].urlはexpireAtタイムスタンプ付きの一時リンクです。それを CMS に保存しないでください。
地味だが役立つ商品画像を生み出すプロンプトテンプレート
カタログ用プロンプトはクリエイティブなプロンプトではありません。毎回同じ光、同じ背景、同じカメラで、商品だけを変えたいのです。テンプレート:
Product packshot: {MATERIAL} {PRODUCT}, {COLOR} {FINISH},
{COMPOSITION},
{BACKGROUND},
{LIGHTING},
{LENS_NOTE},
ecommerce hero, no props, clean negative space {AREA}
埋めるとこうなります:
Product packshot: matte ceramic coffee mug, soft beige glaze,
centered single subject filling about 60% of frame,
seamless white cyclorama background,
soft diffused studio lighting from the upper left, subtle contact shadow underneath,
shot on 50mm with shallow depth of field,
ecommerce hero, no props, clean negative space top and bottom
順序は構造ほど重要ではありません。以下は出力を継続的に改善します:
- 「seamless white cyclorama」は「white background」より優れています——前者はスタジオの無限曲線背景の照明を示唆し、後者は時にテーブルクロスを返してしまうからです。
- 「subtle contact shadow underneath」は商品が浮いて見えるのを防ぎます。
- 「filling about 60% of frame」は構図を固定します。これがないと
schnellは時に引きすぎたり、商品を隅に追いやったりします。 - 「no props」はマグカップの隣に余計な皿、葉、文房具が現れるのを防ぎます。
シーンバリエーションでは、背景 + 照明ブロックを置き換えます:
... matte ceramic coffee mug, soft beige glaze, single subject filling about 40% of frame,
on a light oak countertop with morning sunlight from a kitchen window,
soft golden-hour rim lighting, gentle defocused background showing a blurred linen curtain,
shot on 35mm at f/2.8, lifestyle photography, no people, no other props
俯瞰のフラットレイでは:
... single subject centered, top-down 90-degree angle, ceramic coffee mug, soft beige glaze,
on a light grey paper surface, soft even overhead lighting, soft shadow directly under the rim,
shot on 50mm, flatlay product photography, no props, clean negative space all sides, 1:1
これら 3 つのプロンプトを同じ SKU に対して実行すれば、$0.015 のコストで使える商品詳細ページのギャラリーが得られます。
再撮影不要のカラー/素材バリエーション
schnell が EC 分野で輝く理由はバリエーション生成です。あるプロンプトが 1 つの色でうまく機能すれば、色記述子を文字列置換するだけで残りの色を大量生産できます:
COLORS = [
("beige", "soft beige glaze"),
("charcoal", "deep charcoal matte glaze"),
("sage", "muted sage green glaze"),
("terracotta", "warm terracotta clay glaze"),
("cream", "off-white cream glaze with subtle speckle"),
]
TEMPLATE = (
"Product packshot: matte ceramic coffee mug, {color_desc}, "
"centered single subject filling about 60% of frame, "
"seamless white cyclorama background, soft diffused studio lighting from the upper left, "
"subtle contact shadow underneath, shot on 50mm, ecommerce hero, no props, "
"clean negative space top and bottom"
)
rows = [{"sku": f"mug-{c}-01", "prompt": TEMPLATE.format(color_desc=desc),
"aspect_ratio": "1:1"} for c, desc in COLORS]
5 つのバリエーションで $0.005 計算なら = カラーセレクター全体で $0.025。問題は、schnell がレンダリング間で形状を完全に保てないことです。取っ手の太さ、口縁の曲線、プロポーションが色ごとにわずかにずれます。スタイライズされた商品ページなら問題ありません。ピクセル単位で一致するバリエーションが必要なら、参照画像による条件付けが必要で、それは schnell ではなく、参照画像を使える flux-1.1-pro や nano-banana-pro を使うことになります。
店舗のフロントエンドに接続する
出力はダウンロード済みの JPG ファイルです。そこから商品詳細ページを公開するまでの経路はプラットフォームによって異なります:
- Shopify:
POST /admin/api/2025-01/products/{id}/images.json、リクエストボディは{"image": {"attachment": "<base64>"}}。Shopify がホストする CDN URL が返ります。 - WooCommerce:WP メディアライブラリ経由でアップロード(
POST /wp-json/wp/v2/media)、その後、返されたメディア ID で商品をimages: [{id: ...}]で PATCH します。 - Headless / 自前 CMS:自分のオブジェクトストレージ(S3、R2、Cloudflare Images)にアップロードし、商品レコードに生成された URL を参照させます。
いずれの場合も、クライアント側ではなくサーバー側でアップロードしてください。そうすれば hiapi のキーがブラウザに届くことはありません。
計画のためのコスト計算
1 枚 $0.005 なら、予算が制約になることはほとんどありません——それでもバッチ単位で考えると役立ちます:
| バッチ | 画像数 | コスト |
|---|---|---|
| 1 SKU、4 枚 | 4 | $0.02 |
| 50 SKU の新商品展開、各 4 枚 | 200 | $1.00 |
| 200 SKU のカタログ刷新 | 800 | $4.00 |
| 毎日のバリエーションテスト、1 日 100 枚、30 日間 | 3,000 | $15.00 |
hiapi では、画像エンドポイントの token 側コスト(プロンプト)は無料です——出力された画像に対してのみ支払います。
flux-schnell が苦手なこと
投資する前に計画すべきこと:
- 読みやすいラベル文字。 商品画像でブランドのワードマークをはっきり読ませる必要があるなら、まず
schnellでベース画像を生成し、画像エディタで実際のラベル PNG を合成してください。schnellに「カップに『OAK ROAST』の文字」をレンダリングさせるよう頼むと、およそ 80% の確率で文字化けします。 - 人が商品を持つ撮影。 人体の解剖学は
schnellの弱点です。人物が入るシーン画像にはnano-banana-proまたはflux-1.1-proを使ってください。 - バリエーション間でピクセル単位に一致する商品。 この場合は参照画像をサポートするモデルを使ってください。
- サブミリ単位のディテール(ジュエリー、針が見える時計、エッチングされた金属)。これは高級モデルの領域です。
商品詳細ページ上の他のすべて——背景、照明、カラーバリエーション、状況画像、雰囲気——については、flux-schnell は正しいツールであり、正しい価格帯にあり、正しいターンアラウンドタイムを持っています。ループ全体は while true: poll; download スクリプトと SKU の CSV に収まり、コストは十分に低いので、予算会議を開かずにプロンプトを反復できます。
よくある質問
Q:flux-schnell に参照画像を送れますか?
A:いいえ。flux-schnell/text-to-image はテキストから画像への生成のみをサポートします——input フィールドは prompt と aspect_ratio のみを受け付け、それだけです。参照条件付けのバリエーションには、hiapi で画像入力を受け付ける flux-1.1-pro または nano-banana-pro を使ってください。
Q:どのアスペクト比がサポートされていますか?
A:1:1、3:2、2:3、4:3、3:4、5:4、4:5、16:9、9:16、21:9、auto です。それ以外の比率(例:5:7、2:1)はタスク作成時に拒否され、サポートされているセットを示すエラーが返ります。
Q:resolution を含めるとタスク作成が 400 を返すのはなぜですか?
A:flux-schnell の input は aspect_ratio のみを受け付けます。hiapi 上の他の画像モデル(例:flux-2、nano-banana-pro)は resolution(1K/2K/4K)を受け付けます。schnell の場合は削除してください。
Q:出力 URL の有効期間はどのくらいですか?
A:各 output[].url には expireAt タイムスタンプが付いています(通常数十分)。status が success になったらすぐに画像のバイトをダウンロードし、自分のストレージバケットに保存してください——一時 URL を商品データベースにホットリンクしないでください。
Q:8 個を超える並列タスクを実行できますか?
A:できますが、実際にはより速く完了しません——schnell はエンドツーエンドですでに速く、約 8 個を超える並列はキューを積み上げるだけです。本当に大量(数千個)の場合は、1 つのプロセスでスレッドを使うのではなく、複数のワーカーマシンで並列化してください。
Q:同じ商品のピクセル単位で一致するバリエーションはどうやって得ますか?
A:schnell だけではできません。まずメイン画像を 1 回生成し、参照画像をサポートするモデル(flux-1.1-pro または nano-banana-pro)でそのメイン画像を参照としてバリエーションを実行します。コストは上がりますが、形状の一貫性は大幅に改善されます。
関連記事
すべての記事を見る →-
チュートリアル
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 チュートリアル
リクエスト優先の実践デモ:プロンプトから動画クリップへの呼び出しを構築し、リクエストを送信し、完了状態をポーリングし、結果をダウンロードする