Tutorial
Genera imágenes de producto para e-commerce con FLUX.1 [schnell] a través de la API de hiapi
Una guía ejecutable de curl + Python que cubre imágenes de catálogo sobre fondo blanco, imágenes de escena e imágenes de variantes, a $0.005 por imagen.
La fotografía de catálogo es el paso más lento al publicar un SKU en línea. Reservar un estudio, contratar a un estilista, montar un softbox, contratar a un retocador: para una taza que se vende a $24, eso no tiene ningún sentido. flux-schnell/text-to-image en hiapi te ayuda a obtener imágenes de producto utilizables sobre fondo blanco, imágenes principales de escena e imágenes de variantes de color/material, a $0.005 por imagen, de extremo a extremo en unos 30 segundos; a esta escala, ya no te preocupas por el costo de una sesión individual, sino que empiezas a pensar en los prompts y los lotes de prompts.
Esta guía recorre el flujo de trabajo exacto: los cuatro tipos de imagen que realmente necesita una página de detalles de producto; la estructura de prompt que mantiene al modelo sobre un fondo limpio y sin costuras en lugar de inventar una mesa de madera de la nada; cómo generar 100 variantes por lotes a través de /v1/tasks sin colapsar la terminal; y las pocas cosas que schnell no puede hacer, que necesitas planificar con antelación.
Por qué el trabajo de catálogo encaja especialmente bien con flux-schnell
En hiapi también hay modelos de mayor fidelidad (flux-2, flux-1.1-pro, nano-banana-pro), y si se trata de la imagen principal de una campaña de marca, deberías usarlos. Pero para un catálogo, las cuentas salen distintas:
- Una nueva temporada de 200 SKU, con unas 4 imágenes por SKU (fondo blanco, escena, referencia de escala, cenital) = 800 imágenes. A $0.005 son $4. A $0.05 son $40. A $0.17 son $136.
schnellcompleta la generación en 1–4 pasos de muestreo, por lo que de extremo a extremo (cola + generación + descarga) en nuestras ejecuciones tarda unos 30 segundos.flux-2ynano-banana-proson varias veces más lentos.- Las imágenes de catálogo se muestran a 600px de ancho en la página de detalles del producto y a 200px en la cuadrícula. A esos tamaños, el margen de fidelidad de los modelos avanzados no se nota en absoluto; importan más el cumplimiento del prompt y la sensatez de la relación de aspecto.
El compromiso es real: schnell es débil con texto pequeño y legible en etiquetas, es débil con personas sosteniendo el producto (para eso usa nano-banana-pro) y también es notablemente débil con logotipos de marca complejos. Para «muéstrame el producto sobre un fondo limpio, en tres colores, en cenital y en vista de tres cuartos» —es decir, su trabajo real— es excelente.
La lista de imágenes que realmente vas a generar
Antes de escribir cualquier prompt, determina qué necesita la plantilla de tu página de detalles de producto. Un conjunto inicial útil:
| Tipo de imagen | Relación de aspecto | Uso |
|---|---|---|
| Imagen de producto sobre fondo blanco | 1:1 |
Imagen principal de la página de detalles, miniatura de la cuadrícula |
| Imagen de estudio en vista de tres cuartos | 4:3 |
Segunda imagen de la galería de la página de detalles |
| Imagen de escena / contextual | 3:2 |
Galería de la página de detalles, anuncios sociales |
| Imagen cenital plana | 1:1 |
Galería de la página de detalles, referencia de escala |
| Cuadrícula de variantes de color/material | 1:1 por variante |
Miniaturas del selector de color |
flux-schnell en hiapi admite 1:1, 3:2, 2:3, 4:3, 3:4, 5:4, 4:5, 16:9, 9:16, 21:9, auto. Cualquier otra relación se rechaza al crear la tarea.
Ejecución única: generar una sola imagen de producto con curl
Confirma el requisito mínimo para que el token funcione. Mi última ejecución de extremo a extremo tardó 31 segundos:
TOKEN="$HIAPI_API_KEY"
# 1. Crear la tarea
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. Consultar hasta completar (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. Descargar (la URL de salida caduca; descárgala de inmediato)
URL=$(echo "$STATE" | jq -r '.data.output[0].url')
curl -s -o packshot.jpg "$URL"
echo "saved packshot.jpg"
Una peculiaridad que debes tener en cuenta con flux-schnell: el esquema de entrada de este modelo acepta aspect_ratio, pero rechaza resolution. Otros modelos de imagen en hiapi (por ejemplo, flux-2, nano-banana-pro) aceptan ambos. Si envías {"aspect_ratio": "1:1", "resolution": "1K"} a flux-schnell, obtendrás:
{
"code": 400,
"message": "additional properties not allowed: resolution"
}
Quita resolution. No lo necesitas: schnell genera con el tamaño predeterminado del modelo según la relación de aspecto que elijas.
Producción: generación por lotes con Python
El trabajo real de catálogo implica enviar muchas tareas, descargar las salidas a medida que se completan y reintentar los casos fallidos. Este es el script que usamos al prototipar para una serie de SKU:
"""Generador por lotes de packshots para flux-schnell en hiapi.
Lee un CSV de (sku, prompt, aspect_ratio), envía todos los trabajos, consulta en paralelo,
descarga las salidas terminadas en ./out/{sku}.jpg. Coste total = $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]:
"""Enviar → consultar → descargar. Devuelve (sku, status). status='ok' o '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 línea de skus.csv se ve así:
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
Notas sobre el código de producción:
ThreadPoolExecutor(max_workers=8)es un paralelismo seguro paraschnell. Subirlo solo hace que la cola sea más profunda, no que termine más rápido.- El tiempo de espera de 180 segundos por tarea es una configuración conservadora; en la práctica, cada tarea exitosa se completa en 30–60 segundos. Que se active el tiempo de espera significa que la tarea se quedó atascada; para las cuatro causas, consulta la guía de diagnóstico de tareas colgadas/timeouts de hiapi.
- Descargamos inmediatamente después de
success.output[].urles un enlace temporal con una marca de tiempoexpireAt; no lo guardes en tu CMS.
Plantillas de prompt que producen imágenes de producto aburridas pero útiles
Un prompt de catálogo no es un prompt creativo. Quieres la misma iluminación, el mismo fondo y la misma cámara cada vez, cambiando solo el producto. Una plantilla:
Product packshot: {MATERIAL} {PRODUCT}, {COLOR} {FINISH},
{COMPOSITION},
{BACKGROUND},
{LIGHTING},
{LENS_NOTE},
ecommerce hero, no props, clean negative space {AREA}
Rellena así:
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
El orden importa menos que la estructura. Lo siguiente mejora la salida de forma sostenida:
- «seamless white cyclorama» es mejor que «white background»: lo primero sugiere un fondo de estudio infinito con iluminación de ciclorama, mientras que lo segundo a veces te da un mantel.
- «subtle contact shadow underneath» evita que el producto parezca flotar.
- «filling about 60% of frame» ancla la composición; sin ello,
schnella veces aleja demasiado la cámara o empuja el producto hacia una esquina. - «no props» evita que aparezcan platos, hojas o papelería sobrantes junto a la taza.
Para la variante de escena, reemplaza el bloque de fondo + iluminación:
... 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
Para la imagen cenital plana:
... 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
Ejecuta estos tres prompts para el mismo SKU y obtendrás una galería de página de detalles de producto utilizable por $0.015.
Variantes de color/material sin volver a disparar
Donde schnell brilla en el ámbito del e-commerce es en la generación de variantes. Una vez que un prompt funciona para un color, puedes producir por lotes el resto sustituyendo el descriptor de color en la cadena:
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]
Cinco variantes a $0.005 = $0.025 por todo el selector de color. El problema: schnell no mantiene la forma perfectamente entre renderizaciones. El grosor del asa, la curva del borde y las proporciones derivan ligeramente entre colores. Para una página de producto estilizada no hay problema. Para variantes idénticas a nivel de píxel, necesitas condicionamiento de imagen de referencia, lo que significa usar flux-1.1-pro con una imagen de referencia o nano-banana-pro, no schnell.
Conectar con el frontend de tu tienda
La salida es un archivo JPG descargado. Desde ahí, el camino hacia la página de detalles de producto publicada depende de tu plataforma:
- Shopify:
POST /admin/api/2025-01/products/{id}/images.json, con cuerpo de solicitud{"image": {"attachment": "<base64>"}}. Devuelve una URL CDN alojada por Shopify. - WooCommerce: sube a través de la biblioteca de medios de WP (
POST /wp-json/wp/v2/media) y luego PATCH del producto con el ID de medio devuelto comoimages: [{id: ...}]. - Headless / CMS propio: sube a tu propio almacenamiento de objetos (S3, R2, Cloudflare Images) y referencia la URL generada en el registro del producto.
En cualquier caso, sube desde el servidor, no desde el cliente, para que tu clave de hiapi nunca llegue al navegador.
Cálculo de costos para planificar
Con $0.005 por imagen, el presupuesto rara vez es la restricción, pero pensar por lotes ayuda:
| Lote | Número de imágenes | Costo |
|---|---|---|
| Un SKU, 4 imágenes | 4 | $0.02 |
| Lanzamiento de 50 SKU, 4 imágenes cada uno | 200 | $1.00 |
| Renovación de catálogo de 200 SKU | 800 | $4.00 |
| Pruebas diarias de variantes, 100 imágenes al día, durante 30 días | 3,000 | $15.00 |
En hiapi, el costo del lado de los tokens (prompts) para los endpoints de imagen es gratuito; solo pagas por las imágenes de salida.
Cosas que flux-schnell hace mal
Cosas que necesitas planificar antes de comprometerte:
- Texto legible en etiquetas. Si tu imagen de producto necesita un wordmark de marca claramente legible, genera la imagen base con
schnelly luego compón el PNG de la etiqueta real en un editor de imágenes. Pedirle aschnellque renderice «'OAK ROAST' en la taza» produce texto ilegible aproximadamente el 80 % de las veces. - Productos sostenidos con la mano. La anatomía humana es un punto débil de
schnell. Para imágenes de escena con personas, usanano-banana-prooflux-1.1-pro. - Producto idéntico a nivel de píxel entre variantes. En este caso, usa un modelo que admita imágenes de referencia.
- Detalle submilimétrico (joyería, relojes con agujas visibles, metal grabado). Eso es territorio de modelos avanzados.
Para todo lo demás en una página de detalles de producto —fondos, iluminación, variantes de color, imágenes contextuales, ambiente—, flux-schnell es la herramienta correcta, en el rango de precio correcto y con el tiempo de respuesta correcto. Todo el ciclo cabe en un script while true: poll; download y un CSV de SKU, y el costo es lo bastante bajo como para iterar prompts sin necesidad de una reunión de presupuesto.
Preguntas frecuentes
P: ¿Puedo enviar imágenes de referencia a flux-schnell?
R: No. flux-schnell/text-to-image solo admite texto a imagen: el campo input solo acepta prompt y aspect_ratio, nada más. Para variantes condicionadas por referencia, usa flux-1.1-pro o nano-banana-pro, que en hiapi aceptan entrada de imagen.
P: ¿Qué relaciones de aspecto se admiten?
R: 1:1, 3:2, 2:3, 4:3, 3:4, 5:4, 4:5, 16:9, 9:16, 21:9 y auto. Cualquier otra relación (por ejemplo, 5:7, 2:1) se rechaza al crear la tarea, con un error que indica el conjunto admitido.
P: ¿Por qué la creación de la tarea devuelve 400 cuando incluyo resolution?
R: El input de flux-schnell solo acepta aspect_ratio. Otros modelos de imagen en hiapi (por ejemplo, flux-2, nano-banana-pro) aceptan resolution (1K/2K/4K). Para schnell, quítalo.
P: ¿Cuánto dura la URL de salida?
R: Cada output[].url lleva una marca de tiempo expireAt (normalmente unas pocas decenas de minutos). En cuanto status pasa a success, descarga inmediatamente los bytes de la imagen y guárdalos en tu propio bucket: no enlaces en caliente la URL temporal en la base de datos de tu producto.
P: ¿Puedo ejecutar más de 8 tareas concurrentes?
R: Sí, pero en la práctica no terminarás más rápido: schnell ya es rápido de extremo a extremo, y superar unas 8 en paralelo solo acumula cola. Para volúmenes realmente grandes (miles), paraleliza en varias máquinas de trabajo en lugar de usar hilos en un solo proceso.
P: ¿Cómo obtengo variantes idénticas a nivel de píxel del mismo producto?
R: Solo con schnell no se puede. Genera una vez la imagen principal y luego usa un modelo que admita imágenes de referencia (flux-1.1-pro o nano-banana-pro) para ejecutar las variantes usando la imagen principal como referencia. El costo sube, pero la consistencia de la forma mejora notablemente.
Artículos relacionados
Ver todos los artículos →-
Tutorial
Cómo llamar a hailuo-2.3-fast/image-to-video a través de la API de HiAPI: práctica con curl y Python
La primera solicitud ejecutable del nivel rápido y de bajo coste de imagen a vídeo: del ID del modelo a la descarga del MP4
-
Tutorial
Cómo llamar a hailuo-2.3/text-to-video mediante la API de HiAPI: curl, Python y ejemplos de solicitud ejecutables
-
Tutorial
Uso de Kandinsky 6.0 Pro texto a video en HiAPI: tutorial con curl y Python
Demostración práctica centrada en la solicitud: construir la llamada de prompt a clip de video, enviar la solicitud, sondear el estado de finalización y descargar el resultado