HiAPI
Blog

Tutoriale

Generare immagini di prodotto e-commerce con FLUX.1 [schnell] tramite l'API hiapi

Una guida curl + Python pronta all'uso per immagini catalogo su sfondo bianco, scene e varianti, a $0.005 per immagine.

Jun 28, 2026
Generare immagini di prodotto e-commerce con FLUX.1 [schnell] tramite l'API hiapi

La fotografia per catalogo è la fase più lenta quando si pubblica uno SKU online. Prenotare uno studio, ingaggiare uno stylist, allestire softbox, assumere un ritoccatore: per una tazza da $24 non ha alcun senso. flux-schnell/text-to-image su hiapi ti permette di ottenere immagini prodotto utilizzabili su sfondo bianco pulito, immagini principali in contesto e varianti colore/materiale, a $0.005 per immagine, in circa 30 secondi end-to-end — a questo livello non ragioni più sul costo del singolo scatto, ma inizi a pensare ai prompt e ai batch di prompt.

Questa guida percorre il flusso di lavoro esatto: i quattro tipi di immagine che una pagina di dettaglio prodotto richiede davvero; la struttura del prompt che mantiene il modello su uno sfondo pulito e senza giunture, invece di inventare un tavolo di legno dal nulla; come generare in batch 100 varianti tramite /v1/tasks senza mandare in crash il terminale; e le poche cose che schnell non sa fare, che devi pianificare in anticipo.

Perché il lavoro di catalogo è particolarmente adatto a flux-schnell

Su hiapi ci sono anche modelli a fedeltà più alta (flux-2, flux-1.1-pro, nano-banana-pro), e se si tratta dell'immagine principale di una campagna di brand dovresti usarli. Ma per il catalogo i conti sono diversi:

  • Un lancio di 200 SKU, con circa 4 immagini per SKU (sfondo bianco, scena, riferimento proporzioni, dall'alto) = 800 immagini. A $0.005 sono $4. A $0.05 sono $40. A $0.17 sono $136.
  • schnell completa la generazione in 1–4 step di campionamento, quindi end-to-end (coda + generazione + download) nei nostri test richiede circa 30 secondi. flux-2 e nano-banana-pro sono diverse volte più lenti.
  • Le immagini di catalogo vengono mostrate a 600px di larghezza nella pagina di dettaglio prodotto e a 200px nella griglia. A queste dimensioni il margine di fedeltà dei modelli avanzati non si vede affatto; contano di più l'aderenza al prompt e la plausibilità del rapporto d'aspetto.

Il compromesso è reale: schnell è debole sul testo piccolo e leggibile sulle etichette, è debole sulle persone che tengono in mano il prodotto (per questi casi usa nano-banana-pro), ed è chiaramente debole sui loghi di brand complessi. Per «mostrami il prodotto, su sfondo pulito, in tre colori, vista dall'alto e a tre quarti» — cioè il suo vero lavoro — è eccellente.

La lista delle immagini che devi effettivamente generare

Prima di scrivere qualsiasi prompt, stabilisci di cosa ha bisogno il tuo template di pagina di dettaglio prodotto. Un insieme di partenza utile:

Tipo di immagine Rapporto d'aspetto Uso
Immagine prodotto su sfondo bianco 1:1 Immagine principale della pagina di dettaglio, miniatura della griglia
Scatto in studio a tre quarti 4:3 Seconda immagine della galleria della pagina di dettaglio
Immagine di scena / contesto 3:2 Galleria della pagina di dettaglio, annunci social
Flat lay dall'alto 1:1 Galleria della pagina di dettaglio, riferimento proporzioni
Griglia varianti colore/materiale 1:1 per variante Miniature del selettore colore

flux-schnell su hiapi supporta 1:1, 3:2, 2:3, 4:3, 3:4, 5:4, 4:5, 16:9, 9:16, 21:9, auto. Qualsiasi altro rapporto viene rifiutato alla creazione del task.

Esecuzione singola: generare una singola immagine prodotto con curl

Il minimo indispensabile per verificare che il token funzioni. La mia ultima esecuzione ha impiegato 31 secondi end-to-end:

TOKEN="$HIAPI_API_KEY"

# 1. Crea il task
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. Polling finché non è completo (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. Scarica (l'URL di output scade; recuperalo subito)
URL=$(echo "$STATE" | jq -r '.data.output[0].url')
curl -s -o packshot.jpg "$URL"
echo "saved packshot.jpg"

Una particolarità da tenere presente specificamente per flux-schnell: lo schema di input di questo modello accetta aspect_ratio ma rifiuta resolution. Gli altri modelli di immagine su hiapi (ad esempio flux-2, nano-banana-pro) accettano entrambi. Se invii a flux-schnell {"aspect_ratio": "1:1", "resolution": "1K"}, ottieni:

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

Rimuovi resolution. Non ti serve: schnell restituisce in output alle dimensioni predefinite del modello, secondo il rapporto d'aspetto che hai scelto.

In produzione: generazione in batch con Python

Il vero lavoro di catalogo invia molti task, scarica gli output quando sono pronti e riprova i casi falliti. Questo è lo script che abbiamo usato quando facevamo prototipazione per una serie di SKU:

"""Generatore batch di packshot per flux-schnell su hiapi.

Legge un CSV di (sku, prompt, aspect_ratio), invia tutti i job, fa polling in parallelo,
scarica gli output completati in ./out/{sku}.jpg. Costo totale = $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. Restituisce (sku, status). status='ok' oppure '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()

Una riga di skus.csv è così:

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

Note sul codice di produzione:

  • ThreadPoolExecutor(max_workers=8) è un livello di parallelismo sicuro per schnell. Andare oltre allunga solo la coda, senza completare più in fretta.
  • Il timeout di 180 secondi per task è impostato per essere prudente; in realtà ogni task riuscito si completa entro 30–60 secondi. Se scatta il timeout significa che il task è bloccato: le quattro cause sono nella guida hiapi ai task bloccati/timeout.
  • Scarichiamo immediatamente dopo success. output[].url è un link temporaneo con un timestamp expireAt; non salvarlo nel tuo CMS.

Il template di prompt che ti dà immagini prodotto noiose ma utili

I prompt di catalogo non sono prompt creativi. Vuoi la stessa luce, lo stesso sfondo, la stessa fotocamera ogni volta, cambiando solo il prodotto. Un template:

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

Compilato:

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'ordine conta meno della struttura. Questi elementi migliorano costantemente l'output:

  • «seamless white cyclorama» è meglio di «white background»: il primo implica una fondale da studio infinito con illuminazione uniforme, il secondo a volte ti dà una tovaglia.
  • «subtle contact shadow underneath» impedisce che il prodotto sembri sospeso.
  • «filling about 60% of frame» ancora la composizione; senza, schnell a volte si allontana molto o spinge il prodotto in un angolo.
  • «no props» impedisce che accanto alla tazza compaiano piatti, foglie o cancelleria superflui.

Per le varianti di scena, sostituisci il blocco sfondo + illuminazione:

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

Per il flat lay dall'alto:

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

Esegui questi tre prompt sullo stesso SKU e ottieni una galleria di pagina di dettaglio prodotto utilizzabile al costo di $0.015.

Varianti colore/materiale senza rifare lo shooting

schnell eccelle nell'e-commerce proprio per la generazione di varianti. Una volta che un prompt funziona per un colore, puoi produrre in batch gli altri sostituendo le stringhe dei descrittori di colore:

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]

Cinque varianti a $0.005 = l'intero selettore colore a $0.025. Il problema: schnell non mantiene perfettamente la forma tra render diversi. Lo spessore del manico, la curva del bordo, le proporzioni variano leggermente tra i colori. Per una pagina prodotto stilizzata va bene. Per varianti identiche al pixel ti serve il condizionamento con immagine di riferimento, cioè flux-1.1-pro con immagine di riferimento o nano-banana-pro, non schnell.

Integrazione nel frontend del tuo negozio

L'output è un file JPG scaricato. Da lì alla pubblicazione nella pagina di dettaglio prodotto il percorso dipende dalla tua piattaforma:

  • Shopify: POST /admin/api/2025-01/products/{id}/images.json, con body {"image": {"attachment": "<base64>"}}. Restituisce un URL CDN ospitato da Shopify.
  • WooCommerce: carica tramite la libreria media di WP (POST /wp-json/wp/v2/media), poi aggiorna il prodotto con l'ID media restituito tramite images: [{id: ...}] con una PATCH.
  • Headless / CMS proprio: carica sul tuo object storage (S3, R2, Cloudflare Images) e referenzia l'URL generato nel record del prodotto.

In ogni caso, carica lato server, non lato client, così la tua chiave hiapi non arriva mai al browser.

Calcolo dei costi per la pianificazione

A $0.005 per immagine il budget raramente è il vincolo, ma ragionare per batch aiuta:

Batch Numero di immagini Costo
Uno SKU, 4 immagini 4 $0.02
Lancio di 50 SKU, 4 immagini ciascuno 200 $1.00
Aggiornamento catalogo di 200 SKU 800 $4.00
Test giornaliero di varianti, 100 al giorno per 30 giorni 3,000 $15.00

Su hiapi il costo lato token dell'endpoint immagini (il prompt) è gratuito: paghi solo per l'immagine prodotta in output.

Cose che flux-schnell non fa bene

Cose da pianificare prima di investire:

  • Testo leggibile sulle etichette. Se la tua immagine prodotto richiede un wordmark di brand chiaramente leggibile, genera prima l'immagine base con schnell, poi sovrapponi il PNG dell'etichetta reale in un editor di immagini. Chiedere a schnell di renderizzare «una tazza con la scritta 'OAK ROAST' sopra» produce testo illeggibile in circa l'80% dei casi.
  • Prodotto tenuto in mano. L'anatomia umana è un punto debole di schnell. Per le immagini di scena con persone, passa a nano-banana-pro o flux-1.1-pro.
  • Prodotto identico al pixel tra varianti. In questo caso usa un modello che supporta le immagini di riferimento.
  • Dettagli submillimetrici (gioielli, orologi con lancette visibili, metallo inciso). Questo è il territorio dei modelli avanzati.

Per tutto il resto della pagina di dettaglio prodotto — sfondi, illuminazione, varianti colore, immagini di contesto, atmosfera — flux-schnell è lo strumento giusto, nella fascia di prezzo giusta, con il tempo di turnaround giusto. L'intero ciclo sta in uno script while true: poll; download e in un CSV di SKU, e costa abbastanza poco da poter iterare sui prompt senza riunioni di budget.

Domande frequenti

D: Posso inviare immagini di riferimento a flux-schnell? R: No. flux-schnell/text-to-image supporta solo text-to-image — il campo input accetta solo prompt e aspect_ratio, nient'altro. Per varianti condizionate da riferimento usa flux-1.1-pro o nano-banana-pro, che su hiapi accettano input immagine.

D: Quali rapporti d'aspetto sono supportati? R: 1:1, 3:2, 2:3, 4:3, 3:4, 5:4, 4:5, 16:9, 9:16, 21:9 e auto. Qualsiasi altro rapporto (ad esempio 5:7, 2:1) viene rifiutato alla creazione del task, con un errore che indica l'insieme supportato.

D: Perché la creazione del task restituisce 400 quando includo resolution? R: L'input di flux-schnell accetta solo aspect_ratio. Gli altri modelli di immagine su hiapi (ad esempio flux-2, nano-banana-pro) accettano resolution (1K/2K/4K). Per schnell, rimuovilo.

D: Per quanto tempo è valido l'URL di output? R: Ogni output[].url porta un timestamp expireAt (tipicamente alcune decine di minuti). Appena status diventa success, scarica subito i byte dell'immagine e salvali nel tuo bucket — non fare hotlink dell'URL temporaneo nel tuo database prodotti.

D: Posso eseguire più di 8 task concorrenti? R: Sì, ma di fatto non finiscono più in fretta — schnell è già veloce end-to-end, e oltre circa 8 in parallelo si accumula solo coda. Per volumi davvero grandi (migliaia), parallelizza su più macchine worker, non con thread in un unico processo.

D: Come ottengo varianti identiche al pixel dello stesso prodotto? R: Con il solo schnell non è possibile. Genera una volta l'immagine principale, poi esegui le varianti con un modello che supporta le immagini di riferimento (flux-1.1-pro o nano-banana-pro) usando l'immagine principale come riferimento. Il costo sale, ma la coerenza della forma migliora sensibilmente.

Inizia a creare con artefatti IA persistenti

Ottieni la tua chiave API e chiama modelli di immagini, video e audio tramite un'unica API basata sui task.

Leggi la documentazione API