HiAPI
Blog

Tutoriel

Générer des visuels produits e-commerce avec FLUX.1 [schnell] via l'API hiapi

Un guide curl + Python prêt à l'emploi, couvrant les packshots fond blanc, les visuels contextuels et les variantes, à 0,005 $ par image.

Jun 28, 2026
Générer des visuels produits e-commerce avec FLUX.1 [schnell] via l'API hiapi

La photographie de catalogue est l'étape la plus lente lorsqu'on met en ligne un SKU. Réserver un studio, engager un styliste, monter des boîtes à lumière, faire appel à un retoucheur — pour une tasse vendue à 24 $, c'est tout simplement absurde. flux-schnell/text-to-image sur hiapi vous permet d'obtenir des packshots produits utilisables sur fond blanc, des visuels contextuels principaux, ainsi que des variantes de couleur/matière, à 0,005 $ par image, en environ 30 secondes de bout en bout — à ce niveau de coût, vous ne vous souciez plus du prix d'une prise de vue unitaire, vous commencez à réfléchir aux prompts et aux lots de prompts.

Ce guide parcourt le workflow exact : les quatre types d'images dont une fiche produit a réellement besoin ; la structure de prompt qui maintient le modèle sur un fond blanc et sans couture plutôt que d'inventer une table en bois ; comment générer 100 variantes par lot via /v1/tasks sans faire planter votre terminal ; et les quelques choses que schnell ne sait pas faire, que vous devez anticiper.

Pourquoi le travail de catalogue convient particulièrement à flux-schnell

Il existe sur hiapi des modèles à plus haute fidélité (flux-2, flux-1.1-pro, nano-banana-pro), que vous devriez utiliser pour l'image principale d'une campagne de marque. Mais pour un catalogue, le calcul est différent :

  • Un lancement de 200 SKU nécessite environ 4 images par SKU (fond blanc, scène, référence d'échelle, vue de dessus) = 800 images. À 0,005 $, cela fait 4 $. À 0,05 $, cela fait 40 $. À 0,17 $, cela fait 136 $.
  • schnell termine la génération en 1 à 4 étapes d'échantillonnage, donc de bout en bout (file d'attente + génération + téléchargement) prend environ 30 secondes dans nos exécutions. flux-2 et nano-banana-pro sont plusieurs fois plus lents.
  • Les images de catalogue s'affichent à 600 px de large sur la fiche produit et à 200 px dans la grille. À ces dimensions, la marge de fidélité des modèles avancés est invisible ; le respect du prompt et la pertinence du ratio d'aspect comptent davantage.

Le compromis est réel : schnell est faible sur les petits textes lisibles sur les étiquettes, faible sur les personnes tenant un produit (utilisez nano-banana-pro pour cela), et nettement plus faible sur les logos de marque complexes. Pour « montrez-moi le produit sur un fond propre, en trois couleurs, vue de dessus et vue de trois quarts » — c'est-à-dire son vrai métier — il est excellent.

La liste d'images que vous devez réellement générer

Avant d'écrire le moindre prompt, déterminez ce dont votre template de fiche produit a besoin. Un ensemble de départ utile :

Type d'image Ratio d'aspect Utilisée pour
Packshot fond blanc 1:1 Image principale de la fiche produit, vignette de grille
Vue de trois quarts en studio 4:3 2e image de la galerie de la fiche produit
Visuel contextuel / en situation 3:2 Galerie de la fiche produit, publicités sociales
Vue de dessus à plat 1:1 Galerie de la fiche produit, référence d'échelle
Grille de variantes couleur/matière 1:1 par variante Vignettes du sélecteur de couleur

flux-schnell sur hiapi prend en charge 1:1, 3:2, 2:3, 4:3, 3:4, 5:4, 4:5, 16:9, 9:16, 21:9, auto. Tout autre ratio est rejeté à la création de la tâche.

Exécution unique : générer une seule image produit avec curl

Le strict minimum pour vérifier que le token fonctionne. Ma dernière exécution a pris 31 secondes de bout en bout :

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"

Une particularité à surveiller avec flux-schnell : le schéma d'entrée de ce modèle accepte aspect_ratio, mais rejette resolution. Les autres modèles d'image sur hiapi (par exemple flux-2, nano-banana-pro) acceptent les deux. Si vous soumettez {"aspect_ratio": "1:1", "resolution": "1K"} à flux-schnell, vous obtenez :

{
  "code": 400,
  "message": "additional properties not allowed: resolution"
}

Supprimez resolution. Vous n'en avez pas besoin — schnell produit à la taille par défaut du modèle selon le ratio d'aspect choisi.

En production : génération par lot en Python

Le vrai travail de catalogue soumet de nombreuses tâches, télécharge les sorties au fur et à mesure et réessaie les cas en échec. Voici le script que nous avons utilisé en prototypant une série de 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()

Une ligne de skus.csv ressemble à ceci :

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

Remarques sur le code de production :

  • ThreadPoolExecutor(max_workers=8) est un niveau de parallélisme sûr pour schnell. Au-delà, vous ne faites qu'approfondir la file d'attente, sans terminer plus vite.
  • Le délai d'expiration de 180 secondes par tâche est une valeur prudente ; dans la pratique, chaque tâche réussie se termine en 30 à 60 secondes. Un déclenchement du délai signifie que la tâche est bloquée — voir les quatre causes dans le guide hiapi sur les tâches bloquées/expirées.
  • Nous téléchargeons immédiatement après success. output[].url est un lien temporaire avec un horodatage expireAt ; ne le stockez pas dans votre CMS.

Un template de prompt qui produit des images produit ennuyeuses mais utiles

Un prompt de catalogue n'est pas un prompt créatif. Vous voulez la même lumière, le même fond, le même appareil à chaque fois, en ne changeant que le produit. Un template :

Product packshot: {MATERIAL} {PRODUCT}, {COLOR} {FINISH},
{COMPOSITION},
{BACKGROUND},
{LIGHTING},
{LENS_NOTE},
ecommerce hero, no props, clean negative space {AREA}

Une fois rempli :

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

L'ordre compte moins que la structure. Voici ce qui améliore systématiquement les sorties :

  • « seamless white cyclorama » est meilleur que « white background » — le premier suggère un fond studio incurvé sans couture, le second vous donne parfois une nappe.
  • « subtle contact shadow underneath » empêche le produit de sembler flotter.
  • « filling about 60% of frame » ancre la composition ; sans cela, schnell recule parfois beaucoup ou pousse le produit dans un coin.
  • « no props » empêche l'apparition d'assiettes, de feuilles ou de fournitures superflues à côté de la tasse.

Pour la variante contextuelle, remplacez le bloc fond + lumière :

... 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

Pour la vue de dessus à plat :

... 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

Lancez ces trois prompts sur le même SKU et vous obtenez une galerie de fiche produit utilisable pour 0,015 $.

Des variantes de couleur/matière sans nouvelle prise de vue

C'est dans la génération de variantes que schnell brille en e-commerce. Une fois qu'un prompt fonctionne pour une couleur, vous pouvez produire les autres par lot en remplaçant le descripteur de couleur dans la chaîne :

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]

Cinq variantes à 0,005 $ = 0,025 $ pour tout le sélecteur de couleurs. Le problème : schnell ne conserve pas parfaitement la forme d'un rendu à l'autre. L'épaisseur de l'anse, la courbe du bord, les proportions dérivent légèrement d'une couleur à l'autre. Pour une page produit stylisée, c'est acceptable. Pour des variantes identiques au pixel près, il vous faut un conditionnement par image de référence, ce qui signifie utiliser flux-1.1-pro avec une image de référence ou nano-banana-pro, et non schnell.

Intégrer à votre vitrine boutique

La sortie est un fichier JPG téléchargé. De là jusqu'à la mise en ligne sur la fiche produit, le chemin dépend de votre plateforme :

  • Shopify : POST /admin/api/2025-01/products/{id}/images.json, avec le corps {"image": {"attachment": "<base64>"}}. Renvoie une URL CDN hébergée par Shopify.
  • WooCommerce : téléversez via la médiathèque WP (POST /wp-json/wp/v2/media), puis PATCHez le produit avec l'ID média renvoyé sous la forme images: [{id: ...}].
  • Headless / CMS maison : téléversez vers votre propre stockage objet (S3, R2, Cloudflare Images) et référencez l'URL générée dans l'enregistrement produit.

Dans tous les cas, effectuez le téléversement côté serveur, et non côté client, afin que votre clé hiapi n'atteigne jamais le navigateur.

Calcul de coût pour la planification

À 0,005 $ par image, le budget est rarement la contrainte — mais raisonner par lot aide :

Lot Nombre d'images Coût
Un SKU, 4 images 4 0,02 $
Lancement de 50 SKU, 4 images chacun 200 1,00 $
Rafraîchissement d'un catalogue de 200 SKU 800 4,00 $
Tests de variantes quotidiens, 100 images/jour, pendant 30 jours 3 000 15,00 $

Sur hiapi, le coût côté tokens (prompt) de l'endpoint image est gratuit — vous ne payez que les images en sortie.

Ce que flux-schnell ne fait pas bien

À anticiper avant de vous lancer :

  • Le texte lisible sur les étiquettes. Si votre image produit exige un logotype de marque parfaitement lisible, générez d'abord la base avec schnell, puis compositez le vrai PNG d'étiquette dans un éditeur d'image. Demander à schnell de rendre « une tasse portant l'inscription 'OAK ROAST' » produit un charabia dans environ 80 % des cas.
  • Le produit tenu en main. L'anatomie humaine est le point faible de schnell. Pour les visuels contextuels avec des personnes, utilisez plutôt nano-banana-pro ou flux-1.1-pro.
  • Un produit identique au pixel près entre variantes. Utilisez pour cela un modèle prenant en charge les images de référence.
  • Les détails submillimétriques (bijoux, montres à aiguilles visibles, métal gravé). C'est le domaine des modèles avancés.

Pour tout le reste sur une fiche produit — fonds, éclairage, variantes de couleur, visuels contextuels, ambiance — flux-schnell est le bon outil, au bon prix, avec le bon délai. La boucle entière tient dans un script while true: poll; download et un CSV de SKU, et le coût est assez bas pour itérer sur les prompts sans réunion budgétaire.

FAQ

Q : Puis-je envoyer une image de référence à flux-schnell ? R : Non. flux-schnell/text-to-image ne prend en charge que la génération texte-vers-image — le champ input accepte uniquement prompt et aspect_ratio, rien d'autre. Pour des variantes conditionnées par référence, utilisez flux-1.1-pro ou nano-banana-pro, qui acceptent tous deux des images en entrée sur hiapi.

Q : Quels ratios d'aspect sont pris en charge ? R : 1:1, 3:2, 2:3, 4:3, 3:4, 5:4, 4:5, 16:9, 9:16, 21:9 et auto. Tout autre ratio (par exemple 5:7, 2:1) est rejeté à la création de la tâche, avec une erreur indiquant l'ensemble pris en charge.

Q : Pourquoi la création de tâche renvoie-t-elle 400 lorsque j'inclus resolution ? R : L'input de flux-schnell n'accepte que aspect_ratio. Les autres modèles d'image sur hiapi (par exemple flux-2, nano-banana-pro) acceptent resolution (1K/2K/4K). Pour schnell, supprimez-la.

Q : Combien de temps l'URL de sortie est-elle valide ? R : Chaque output[].url porte un horodatage expireAt (généralement quelques dizaines de minutes). Dès que status passe à success, téléchargez immédiatement les octets de l'image et stockez-les dans votre propre bucket — ne liez pas en hotlink l'URL temporaire dans votre base de données produit.

Q : Puis-je exécuter plus de 8 tâches concurrentes ? R : Oui, mais vous ne terminerez pas plus vite en pratique — schnell est déjà rapide de bout en bout, et au-delà d'environ 8 en parallèle, vous ne faites qu'empiler la file. Pour de très gros volumes (milliers), parallélisez sur plusieurs machines de travail plutôt que dans un seul processus avec des threads.

Q : Comment obtenir des variantes identiques au pixel près d'un même produit ? R : Impossible avec schnell seul. Générez d'abord une image principale, puis utilisez un modèle prenant en charge les images de référence (flux-1.1-pro ou nano-banana-pro) pour générer les variantes avec cette image comme référence. Le coût augmente, mais la cohérence de forme s'améliore nettement.

Articles similaires

Voir tous les articles →

Commencez avec des artefacts IA persistants

Obtenez votre clé API et appelez des modèles d'image, de vidéo et d'audio via une API unique basée sur les tasks.

Lire la documentation de l'API