HiAPI
博客

教程

通过 hiapi API 使用 FLUX.1 [schnell] 生成电商产品图

一份可直接运行的 curl + Python 指南,涵盖目录白底图、场景图与变体图,每张图 $0.005。

2026年11月3日
通过 hiapi API 使用 FLUX.1 [schnell] 生成电商产品图

目录摄影是在线发布一个 SKU 时最慢的环节。预约影棚、请造型师、搭柔光箱、请修图师——对于一个售价 $24 的马克杯来说,这完全说不通。hiapi 上的 flux-schnell/text-to-image 能帮你得到可用的白底白背景产品图、场景主图,以及颜色/材质变体图,每张图 $0.005,端到端大约 30 秒——在这个量级下,你不再纠结单张拍摄成本,而是开始思考提示词和提示词批次。

本指南会走一遍确切的工作流:产品详情页实际需要的四种图片类型;能让模型保持在干净无缝背景上、而不是凭空编造一张木桌的提示词结构;如何在不把终端搞崩的情况下通过 /v1/tasks 批量生成 100 个变体;以及 schnell 做不到的少数几件事,你需要提前规划。

为什么目录工作特别适合用 flux-schnell

hiapi 上还有保真度更高的模型(flux-2、flux-1.1-pro、nano-banana-pro),如果是品牌 campaign 的那一张主图,你应该用它们。但对于目录来说,账算得不一样:

  • 一次 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),在复杂品牌 logo 方面也明显较弱。对于“把产品展示给我,放在干净背景上,三种颜色,俯拍和四分之三视角”——也就是它的实际本职工作——它非常出色。

你实际要生成的图片清单

在写任何提示词之前,先确定你的产品详情页模板需要什么。一个有用的起始集合:

图片类型 宽高比 用于
白底产品图 1:1 产品详情页主图、网格缩略图
四分之三视角棚拍图 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。其他任何比例在创建任务时都会被拒绝。

一次性运行:用 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 特别需要注意的一个怪癖:该模型的输入 schema 接受 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 看起来像这样:

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 秒内完成。触发超时意味着任务卡住了——四种原因见 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

这三个提示词对同一个 SKU 运行,你就能以 $0.015 的成本得到一个可用的产品详情页图库。

无需重新拍摄的颜色/材质变体

schnell 在电商领域大放异彩的原因就是变体生成。一旦某个提示词对一种颜色有效,你就可以通过字符串替换颜色描述符来批量生产其余颜色:

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]

五个变体按 $0.005 算 = 整个颜色选择器 $0.025。问题在于:schnell 在不同渲染之间无法完美保持外形。杯柄粗细、杯口曲线、比例会在不同颜色之间轻微漂移。对于风格化的产品页来说这没问题。对于像素级一致的变体,你需要参考图像条件化,这意味着要用 flux-1.1-pro 配参考图或 nano-banana-pro,而不是 schnell。

接入你的店铺前台

输出是一个下载好的 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 密钥永远不会到达浏览器。

用于规划的成本计算

按每张图 $0.005 算,预算很少是约束——但按批次来思考会有帮助:

批次 图片数 成本
一个 SKU,4 张图 4 $0.02
50 个 SKU 上新,每个 4 张图 200 $1.00
200 个 SKU 目录刷新 800 $4.00
每日变体测试,每天 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 里,成本足够低,你可以迭代提示词而无需开预算会议。

常见问题

问:我可以向 flux-schnell 发送参考图像吗? 答:不可以。flux-schnell/text-to-image 仅支持文生图——input 字段只接受 prompt 和 aspect_ratio,仅此而已。对于参考条件化的变体,请使用 flux-1.1-pro 或 nano-banana-pro,它们在 hiapi 上都接受图像输入。

问:支持哪些宽高比? 答: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)在创建任务时会被拒绝,并返回指向支持集合的错误。

问:为什么我包含 resolution 时任务创建会返回 400? 答:flux-schnell 的 input 只接受 aspect_ratio。hiapi 上的其他图像模型(例如 flux-2、nano-banana-pro)接受 resolution(1K/2K/4K)。对于 schnell,去掉它。

问:输出 URL 的有效期是多久? 答:每个 output[].url 都带有 expireAt 时间戳(通常几十分钟)。一旦 status 变为 success,就立即下载图片字节并存储到你自己的存储桶中——不要把临时 URL 热链接到你的产品数据库。

问:我可以运行超过 8 个并发任务吗? 答:可以,但实际上不会更快完成——schnell 端到端已经很快,超过约 8 个并行只会堆积队列。对于真正的大批量(数千个),应在多台工作机器上并行,而不是在一个进程中使用线程。

问:如何获得同一产品的像素级一致变体? 答:仅靠 schnell 做不到。先生成一次主图,然后使用支持参考图像的模型(flux-1.1-pro 或 nano-banana-pro)以主图作为参考进行变体运行。成本会上升,但外形一致性会显著改善。

立即开始构建持久 AI 产物

获取 API 密钥,通过一个基于任务(task)的 API 调用图像、视频和音频模型。

阅读 API 文档