Generar imágenes automáticamente: plantillas, jobs y webhooks
Casi todas las respuestas a esta pregunta son la captura de un cuadro de texto. Escriba un prompt, pulse un botón, espere, descargue, repita. Eso no es automatización. Eso es usted, trabajando a mano, más rápido.
Automático significa sin persona en medio. Llega una fila de datos, sale una imagen, nadie miró cómo pasaba. Eso requiere cuatro piezas: una plantilla de prompt con huecos, datos para rellenar los huecos, una cola que acepte trabajo sin bloquear, y algún sitio duradero donde poner el resultado. Nosotros operamos la API que va en medio de eso, así que vemos los pipelines que monta la gente y los que se rompen en silencio tres semanas después del lanzamiento.

La parte que nadie le cuenta primero: ahora es barato
Empiece por la cifra, porque cambia lo que está dispuesto a construir.
Mil imágenes por Nano Banana 2 Lite cuestan $23.80. Eso es 1.000 × $0.0238, a nuestro precio del 26 de agosto de 2026. Cien mil fotos de producto —un catálogo real, cada SKU, cada combinación de color— son $2,380.
Los mismos volúmenes en los niveles entre los que elegiría:
| Imágenes | Nano Banana 2 Lite | Nano Banana 2 | Nano Banana Pro |
|---|---|---|---|
| 100 | $2.38 | $4.00 | $7.50 |
| 1.000 | $23.80 | $40.00 | $75.00 |
| 10.000 | $238.00 | $400.00 | $750.00 |
| 100.000 | $2,380.00 | $4,000.00 | $7,500.00 |
Los precios y las condiciones de los productos pueden cambiar con el tiempo; consulte los precios vigentes de cada proveedor antes de tomar una decisión de compra. Los precios de Google Batch o Flex no son directamente equivalentes a un request estándar de API bajo demanda, porque la programación, la disponibilidad y las condiciones de procesamiento son distintas; por eso quedan excluidos de esta comparación. Se trata de una comparación acotada, no de una afirmación de que E2X sea la opción más barata del mundo en cualquier configuración.
Casi todo el que pregunta cómo automatizar la generación de imágenes ya la ha tasado en silencio como «demasiado cara para probar». Es una pregunta de $23.80. Monte la cosa.
Paso 1: deje de escribir prompts, empiece a escribir una plantilla
Un prompt que escribe una vez es una frase. Un prompt que una máquina corre diez mil veces es una plantilla con huecos, y esa diferencia es todo el trabajo.
La regla: todo lo que varía va en un hueco, todo lo que tiene que quedarse idéntico va en el texto fijo. Equivóquese en esa frontera y el conjunto deriva: la mitad de las imágenes sobre blanco y la mitad sobre gris, porque «fondo» se coló en la parte variable.
TEMPLATE = (
"Studio product photograph of a {product_name} in {colour}, "
"centred on a plain warm-grey backdrop, soft directional key light "
"from the upper left, shallow depth of field, editorial catalog styling, "
"no text, no logos, no people"
)
Dos huecos. La iluminación, el fondo, el encuadre y las exclusiones están congelados. Esa mitad congelada es lo que hace que 400 imágenes parezcan una sola sesión de fotos en vez de 400 accidentes.
Deje cuatro cosas fuera de la mitad variable: el medio, la dirección de la luz, el fondo y las instrucciones negativas. Esas son su identidad visual. Si se mueven por fila, no tiene ninguna.

El oficio del prompt en sí es otra página aparte. Lo que importa para la automatización es la forma: marco fijo, huecos con nombre, sin sorpresas.
Paso 2: los datos son el producto de verdad
La plantilla es trivial. El trabajo vive en los datos, y normalmente son algo que ya tiene.
- Una tabla de catálogo de productos. Una fila por SKU, columnas para nombre, color, material, categoría.
- Una exportación del CMS con los posts del blog, donde el título y el tema se convierten en los huecos para una imagen de cabecera por artículo.
- Filas de la base de datos de su aplicación: anuncios creados por usuarios, páginas de evento, recetas.
- Una hoja de cálculo de variantes publicitarias: seis titulares por cuatro fondos son veinticuatro imágenes, y eso nadie lo brifea a mano.
Normalice la fuente, sea la que sea, a una lista de diccionarios antes de que toque la API, y mantenga un ID estable en cada fila. Lo necesita para casar resultados con registros, y saltárselo duele exactamente en el momento en que dispara un webhook.
Paso 3: envíe, no espere
Nuestra API es asíncrona a propósito. Usted hace POST de un job, recibe un job ID de vuelta al instante, y la generación ocurre en nuestro lado.
curl -X POST https://api.e2x.ai/v1/jobs/submit \
-H "Authorization: Bearer $E2X_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "google/nano-banana-2-lite/text-to-image",
"input": {
"prompt": "Studio product photograph of a walnut desk lamp in matte black...",
"aspect_ratio": "1:1"
},
"webhookUrl": "https://your-app.example.com/hooks/e2x"
}'
El recorredor que convierte una tabla en jobs son unas treinta líneas. Lee un CSV, formatea la plantilla por fila, envía, y anota el job ID contra el ID de fila para que nada quede huérfano.
import csv
import os
import time
import requests
API = "https://api.e2x.ai/v1"
HEADERS = {
"Authorization": f"Bearer {os.environ['E2X_API_KEY']}",
"Content-Type": "application/json",
}
TEMPLATE = (
"Studio product photograph of a {product_name} in {colour}, "
"centred on a plain warm-grey backdrop, soft directional key light "
"from the upper left, shallow depth of field, editorial catalog styling, "
"no text, no logos, no people"
)
MAX_IMAGES = 500 # techo duro, vea la sección de coste
UNIT_COST = 0.0238 # Nano Banana 2 Lite, comprobado el 26 de agosto de 2026
def submit(row):
body = {
"model": "google/nano-banana-2-lite/text-to-image",
"input": {
"prompt": TEMPLATE.format(**row),
"aspect_ratio": "1:1",
},
"webhookUrl": "https://your-app.example.com/hooks/e2x",
}
for attempt in range(4):
r = requests.post(f"{API}/jobs/submit", json=body, headers=HEADERS, timeout=30)
if r.status_code < 300:
return r.json()["data"]["jobId"]
if r.status_code == 429 or r.status_code >= 500:
time.sleep(2 ** attempt)
continue
raise RuntimeError(f"row {row['id']} rejected: {r.status_code} {r.text}")
raise RuntimeError(f"row {row['id']} still failing after 4 attempts")
with open("catalog.csv") as fh:
rows = list(csv.DictReader(fh))[:MAX_IMAGES]
print(f"submitting {len(rows)} jobs, budget ${len(rows) * UNIT_COST:.2f}")
submitted = {}
for row in rows:
try:
submitted[row["id"]] = submit(row)
except RuntimeError as err:
print(f"skipped: {err}")
print(f"{len(submitted)}/{len(rows)} accepted")
Fíjese en lo que ese bucle no hace: pararse cuando una fila falla. Un batch de 500 donde la fila 212 tiene el color a null debería darle 499 imágenes y un salto registrado, no un stack trace a las tres de la madrugada y cero imágenes.
El slug importa más de lo que parece. google/nano-banana-2-lite/text-to-image lleva el sufijo de capability; el slug de texto de Nano Banana 2 es google/nano-banana-2 a secas, sin ninguno. Adivinar le cuesta un 404 en un pipeline que por lo demás parece terminado. Cada modelo publica su lista de parámetros en un fichero de especificación legible por máquina.
Paso 4: webhook antes que polling, y esta es la razón
Puede hacer polling. GET /v1/jobs/{JOB_ID} devuelve pending, processing, completed, failed o cancelled, y para un script suelto eso está bien.
Para un pipeline es la elección equivocada, y la razón no es la elegancia. El polling acopla la vida de su proceso al job más lento del batch. Una generación en Lite tarda unos 60 segundos por nuestro lado. Envíe 500 y el poller tiene que seguir vivo, sosteniendo estado, todo lo que tarde la cola: a través de un deploy, del reinicio de un contenedor, del timeout de 15 minutos de la plataforma serverless en la que esté. Cuando se muera, se muere sosteniendo el único registro de qué jobs estaban en vuelo.
Un webhook invierte eso. Usted envía y sale. Más tarde llega un POST con un job completado y usted maneja exactamente ese job, sin estado.
// Handler de Express. Idempotente: el mismo jobId puede llegar dos veces.
app.post("/hooks/e2x", express.json(), async (req, res) => {
res.status(200).end(); // primero confirmar, después trabajar
const job = req.body.data;
if (job.status !== "completed") {
await markFailed(job.jobId, job.error?.message ?? "unknown");
return;
}
const already = await findAsset(job.jobId);
if (already) return;
await storeImage(job.jobId, job.outputs[0].url);
});
Haga polling cuando esté corriendo un script a mano y mirándolo. Quédese con el webhook cuando no mire nadie, que es la definición de automatización.
Paso 5: la URL de salida es temporal, y así es como se rompen los pipelines
Esta es la buena. Si se salta todo lo demás, lea esta sección dos veces.
data.outputs[0].url es una URL de entrega, no de almacenamiento. Funciona ahora. No va a funcionar para siempre. Cualquier cosa que la trate como permanente —una columna product_image_url, un campo de CMS, un enlace enviado por correo a un cliente— renderiza perfectamente en staging y muestra iconos de imagen rota quince días después.
El fallo es feo porque es diferido. Nada da error al escribir. Los tests pasan. El batch reporta 500 éxitos. Después las imágenes empiezan a desaparecer, primero de los registros más antiguos, y para cuando alguien se da cuenta no puede regenerarlas sin volver a correr el job entero, porque los prompts tampoco se guardaron nunca.
Descargue los bytes. Póngalos en su propio bucket. Guarde su URL.

import requests
import boto3
s3 = boto3.client("s3")
def store_image(job_id, row_id, source_url):
img = requests.get(source_url, timeout=60)
img.raise_for_status()
key = f"products/{row_id}.jpg"
s3.put_object(
Bucket="my-assets",
Key=key,
Body=img.content,
ContentType=img.headers.get("content-type", "image/jpeg"),
)
return f"https://cdn.example.com/{key}"
Ya que está dentro, guarde el prompt junto a la imagen. Una columna de texto, y es la diferencia entre «regenera esa variante» y «empieza de cero».
Dos hábitos más que conviene tener desde el primer día. Compruebe que la descarga devolvió bytes de imagen y no una página de error, porque un 200 con HTML dentro se escribe tan contento en su bucket. Y clave el almacenamiento por su ID de fila, no por nuestro job ID, para que una regeneración sobrescriba el objeto correcto en lugar de dejarlo huérfano.
Paso 6: qué se tuerce de verdad a volumen
Los batches pequeños lo esconden todo. Esto es lo que aparece en cuanto pasa de unos cientos.
Los batches parciales son normales. Alguna fracción de cualquier ejecución grande falla: un prompt que activa un filtro de contenido, una fila malformada, un error transitorio upstream. Trate 497 de 500 como el caso de éxito, y dese a sí mismo un reintento de un solo comando sobre esas tres filas.
Los reintentos necesitan techo de gasto. Un bucle de reintento sin límite es un bug que factura. $0.0238 el disparo suena inofensivo hasta que un bucle atascado lo corre cuarenta mil veces. Meta un contador duro en el código: la línea MAX_IMAGES de arriba es el seguro más barato que hay aquí.
La idempotencia no es opcional. Los webhooks se pueden entregar más de una vez. Un handler que no sea seguro de correr dos veces sobre un mismo job ID le da activos duplicados y filas duplicadas, y se entera en la semana más ocupada.
Los costes necesitan un contador en vivo, no una factura mensual. Los valores monetarios de nuestra API vuelven en micro-céntimos: 1.000.000 equivale a $1. Súmelos por batch y registre el total al cerrarlo. Un pipeline que reporta «412 imágenes, $9.81» cada noche es un pipeline que nadie tiene que auditar.
Los valores por defecto de aspect ratio le van a sorprender. Lite tiene por defecto 1:1, mientras que Nano Banana 2 y Pro tienen por defecto 9:16. Fíjelo en la plantilla o medio catálogo se le vuelve vertical sin avisar.
El watermark viaja con el fichero. Google estampa SynthID en todo lo que producen sus modelos de imagen. Es invisible, no es un flag, y sobrevive a lo que sea que su pipeline le haga después a los bytes. Quien lleve marca o legal por su parte debería dar el visto bueno a los activos generados con IA antes de que exista el pipeline, no después de que haya escrito 20.000 objetos en un bucket.

A qué modelo apuntar el pipeline
Por defecto, Nano Banana 2 Lite, y muévase de ahí solo cuando algo concreto le obligue.
Eso es aritmética, no una evasiva. A $0.0238 es lo más barato que vendemos que haga una imagen, toda una generación más nuevo que el Nano Banana original al que reemplazó, y a volúmenes de pipeline la diferencia de precio se compone hasta ser la única cifra que mira finanzas. Además lleva cuatro aspect ratios que no tiene nada más —1:4, 4:1, 1:8 y 8:1—, justo lo que quiere si la salida son banners y raíles verticales. Una peculiaridad: no tiene parámetro resolution en absoluto, y enviarlo es un error, no un no-op silencioso.
Desvíese de él en estos cuatro casos, y en ningún otro:
- Salida por encima de 1K. Lite es solo 1K. Nano Banana 2 llega a 4K desde $0.04 de base, con 2K a ×1,5 y 4K a ×2.
- Texto legible en la imagen. Fichas de producto con precios, infografías localizadas. Eso vive en Nano Banana Pro a $0.075, donde 1K y 2K cuestan lo mismo: llévese el 2K, es gratis.
- Fondos transparentes. Los recortes y las hojas de stickers necesitan
background: transparent, y GPT Image 2 es el único modelo que llevamos que lo hace en una llamada. Presupueste $0.0525 en 1K, no la cifra en crudo del catálogo:qualitymultiplica por 7 en todos los niveles. - El mismo rostro o la misma botella a lo largo de muchos fotogramas. Es un problema de imágenes de referencia, no de volumen, y tiene su propia página sobre consistencia.
Para cargas mixtas, corra el grueso en Lite y desvíe a Pro ese 2% que necesita texto o fidelidad, con una columna en sus datos. Un pipeline, dos modelos, precio de Pro solo donde se gana algo.
Para el recorrido parámetro a parámetro del contrato de submit y polling, vea el tutorial de la API de Nano Banana Pro, y para el desglose nivel a nivel, la comparación de modelos. Los dos dan por hecho que ya eligió modelo. Si no lo ha hecho, los modelos text-to-image están listados con sus precios al lado, y el catálogo añade el lado de vídeo.
Todo junto, en orden
Plantilla con huecos. Datos con IDs estables. Envío como jobs bajo un techo duro. Recepción por webhook. Descarga de los bytes a su propio almacenamiento. Registro de los fallos y del gasto. Seis pasos, ninguno difícil, y el que se salta la gente es siempre el quinto.
Preguntas frecuentes
¿Cómo se genera una imagen automáticamente?
Escriba su prompt como una plantilla con huecos con nombre, rellene esos huecos desde una fuente de datos como un catálogo de productos o un CSV, y envíe cada prompt relleno como un job asíncrono a una API de imagen. Reciba el resultado por webhook en lugar de hacer polling, y después descargue los bytes de la imagen a su propio almacenamiento. Nada de ese bucle necesita una persona, que es lo que lo hace automático en vez de trabajo manual rápido.
¿Cuánto cuesta generar 1.000 imágenes automáticamente?
A precios de E2X del 26 de agosto de 2026, 1.000 imágenes por Nano Banana 2 Lite son $23.80, ya que cada request está a $0.0238. El mismo volumen son $40.00 en Nano Banana 2 y $75.00 en Nano Banana Pro. Las resoluciones más altas multiplican encima, pero para salida estándar de 1K la cifra por imagen es todo el cálculo.
¿Debería usar webhook o polling para los jobs de generación de imágenes?
Use webhook para cualquier cosa que corra desatendida. El polling ata la vida de su proceso al job más lento del batch, así que un deploy o un timeout serverless le mata el poller mientras sostiene el único registro de qué jobs siguen corriendo. Un webhook le deja enviar y salir, y después manejar cada job completado sin estado. El polling está bien para un script que está viendo correr.
¿Por qué mis imágenes generadas dejan de cargar al cabo de un tiempo?
Porque guardó la URL de salida de la API en lugar de la imagen. Esa URL es entrega temporal, no almacenamiento, así que una columna que la contenga renderiza bien al principio y se rompe después. Descargue los bytes en cuanto el job se complete, escríbalos en su propio bucket y guarde su propia URL. Es la forma más común de que un pipeline de imagen salga roto.
¿A qué modelo debería apuntar por defecto un pipeline de imagen automatizado?
A Nano Banana 2 Lite, a $0.0238 por imagen. Es el modelo de imagen más barato de nuestro catálogo y una generación más nuevo que el Nano Banana original, y a volúmenes de batch el precio domina cualquier otra consideración. Pase a Nano Banana 2 solo para salida por encima de 1K, a Nano Banana Pro solo para texto legible dentro de la imagen, y a GPT Image 2 solo para fondos transparentes.
¿Qué pasa cuando fallan algunas imágenes de un batch?
Siempre fallarán algunas, y el pipeline debería tratarlo como normal y no como fatal. Siga enviando después de una fila rechazada, registre los IDs fallidos con sus errores, y construya una ruta de reintento que corra solo los fallos. Limite los intentos de reintento, porque cada uno es un cargo real.