Generare immagini in automatico: template, job e webhook
Quasi tutte le risposte a questa domanda sono lo screenshot di una casella di testo. Scrivi un prompt, premi un bottone, aspetti, scarichi, ricominci. Quella non è automazione. Quello è Lei che lavora a mano, più in fretta.
Automatico significa nessun umano nel mezzo. Arriva una riga di dati, esce un'immagine, nessuno ha guardato mentre succedeva. Servono quattro pezzi: un template di prompt con dei buchi dentro, dei dati per riempire i buchi, una coda che accetta lavoro senza bloccarsi, e un posto durevole in cui mettere il risultato. Noi facciamo girare l'API che sta in mezzo, quindi vediamo le pipeline che la gente costruisce e quelle che si rompono in silenzio tre settimane dopo il lancio.

La cosa che nessuno Le dice per prima: ormai costa poco
Parta dal numero, perché cambia quello che è disposto a costruire.
Mille immagini attraverso Nano Banana 2 Lite costano $23.80. Cioè 1.000 × $0.0238, al nostro prezzo del 26 agosto 2026. Centomila scatti di prodotto — un catalogo vero, ogni SKU, ogni variante di colore — fanno $2,380.
Gli stessi volumi sui tier fra cui sceglierebbe:
| Immagini | 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 |
I prezzi e le condizioni dei prodotti possono cambiare nel tempo. Verifichi i prezzi aggiornati di ciascun provider prima di prendere una decisione d'acquisto. Il prezzo Google Batch o Flex non è direttamente equivalente a una request API on-demand standard, perché le condizioni di pianificazione, disponibilità ed elaborazione sono diverse; è quindi escluso da questo confronto. Si tratta di un confronto circoscritto a uno scenario d'uso specifico, non dell'affermazione che E2X sia l'opzione più economica al mondo in ogni configurazione.
Quasi tutti quelli che ci chiedono come automatizzare la generazione di immagini l'hanno già silenziosamente etichettata come "troppo cara per provarci". È una domanda da $23.80. Lo costruisca.
Passo 1: smetta di scrivere prompt, cominci a scrivere un template
Un prompt che scrive una volta è una frase. Un prompt che una macchina esegue diecimila volte è un template con degli slot, e quella differenza è tutto il lavoro.
La regola: tutto ciò che varia va in uno slot, tutto ciò che deve restare identico va nel testo fisso. Sbagli quel confine e il set va alla deriva — metà delle immagini su bianco, metà su grigio, perché "sfondo" è finito nella parte variabile.
TEMPLATE = (
"Fotografia di prodotto in studio di {product_name} in {colour}, "
"al centro su un fondale grigio caldo uniforme, luce chiave morbida e direzionale "
"dall'alto a sinistra, profondità di campo ridotta, styling da catalogo editoriale, "
"niente testo, niente loghi, niente persone"
)
Due slot. Luce, fondale, inquadratura ed esclusioni sono congelati. È quella metà congelata a far sembrare 400 immagini un unico servizio fotografico invece di 400 incidenti.
Tenga quattro cose fuori dalla metà variabile: il medium, la direzione della luce, lo sfondo e le istruzioni negative. Quelle sono la Sua identità visiva. Se si muovono riga per riga, non ne ha nessuna.

L'arte del prompt in sé è un articolo a parte. Per l'automazione quello che conta è la forma: cornice fissa, buchi con un nome, nessuna sorpresa.
Passo 2: il prodotto vero sono i dati
Il template è banale. Il lavoro sta nei dati, e di solito sono qualcosa che ha già.
- Una tabella di catalogo prodotti. Una riga per SKU, colonne per nome, colore, materiale, categoria.
- Un export dal CMS dei post del blog, dove titolo e argomento diventano gli slot per un'immagine di testata per articolo.
- Righe nel database della Sua applicazione — annunci generati dagli utenti, pagine evento, ricette.
- Un foglio di calcolo di varianti pubblicitarie: sei headline per quattro sfondi fanno ventiquattro immagini, e nessuno le briefa a mano.
Normalizzi qualunque sia la fonte in una lista di dizionari prima che tocchi l'API, e tenga un ID stabile su ogni riga. Le serve per riagganciare i risultati ai record, e saltarlo fa male esattamente nel momento in cui parte un webhook.
Passo 3: invii, non aspetti
La nostra API è asincrona di proposito. Fa una POST di un job, riceve subito indietro un job ID, e la generazione avviene dalla nostra parte.
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": "Fotografia di prodotto in studio di una lampada da scrivania in noce nero opaco...",
"aspect_ratio": "1:1"
},
"webhookUrl": "https://your-app.example.com/hooks/e2x"
}'
Il walker che trasforma una tabella in job sta in una trentina di righe. Legge un CSV, formatta il template riga per riga, invia, e registra il job ID accanto all'ID di riga così che niente resti orfano.
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 = (
"Fotografia di prodotto in studio di {product_name} in {colour}, "
"al centro su un fondale grigio caldo uniforme, luce chiave morbida e direzionale "
"dall'alto a sinistra, profondità di campo ridotta, styling da catalogo editoriale, "
"niente testo, niente loghi, niente persone"
)
MAX_IMAGES = 500 # tetto invalicabile, veda la sezione sui costi
UNIT_COST = 0.0238 # Nano Banana 2 Lite, verificato il 26 agosto 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")
Noti cosa quel ciclo non fa: fermarsi quando una riga fallisce. Un batch da 500 in cui la riga 212 ha il colore a null deve darLe 499 immagini e uno skip a log, non uno stack trace alle tre di notte e zero immagini.
Lo slug conta più di quanto sembri. google/nano-banana-2-lite/text-to-image porta il suffisso di capability; lo slug testuale di Nano Banana 2 è il nudo google/nano-banana-2, senza. Tirare a indovinare Le costa un 404 su una pipeline che per il resto sembra finita. Ogni modello pubblica la propria lista di parametri in un file di specifica leggibile dalla macchina.
Passo 4: webhook al posto del polling, ed ecco perché
Può fare polling. GET /v1/jobs/{JOB_ID} restituisce pending, processing, completed, failed o cancelled, e per uno script una tantum va benissimo.
Per una pipeline è la scelta sbagliata, e la ragione non è l'eleganza. Il polling lega la vita del Suo processo al job più lento del batch. Una generazione Lite gira intorno ai 60 secondi dalla nostra parte. Ne invii 500 e il poller deve restare vivo, con lo stato in mano, per tutto il tempo che ci mette la coda — attraverso un deploy, il restart di un container, il timeout di 15 minuti della piattaforma serverless su cui si trova. E quando muore, muore tenendosi l'unico registro di quali job erano in volo.
Un webhook ribalta la cosa. Lei invia ed esce. Più tardi arriva una POST con un job completato e Lei gestisce esattamente quel job, senza stato.
// Handler Express. Idempotente: lo stesso jobId può arrivare due volte.
app.post("/hooks/e2x", express.json(), async (req, res) => {
res.status(200).end(); // prima si conferma, poi si lavora
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);
});
Faccia polling quando sta eseguendo uno script a mano e lo sta guardando. Prenda il webhook quando non guarda nessuno, che è poi la definizione di automazione.
Passo 5: l'URL di output è temporaneo, ed è così che si rompono le pipeline
È questa. Se salta tutto il resto, legga questa sezione due volte.
data.outputs[0].url è un URL di consegna, non uno storage. Funziona adesso. Non funzionerà per sempre. Qualsiasi cosa lo tratti come permanente — una colonna product_image_url, un campo del CMS, un link mandato via mail a un cliente — si vede benissimo in staging e mostra icone di immagini rotte quindici giorni dopo.
Il guasto è cattivo perché è ritardato. Niente va in errore al momento della scrittura. I test passano. Il batch riporta 500 successi. Poi le immagini cominciano a sparire dai record più vecchi, e quando qualcuno se ne accorge non può rigenerarle senza rieseguire tutto il job, perché nemmeno i prompt erano stati salvati.
Scarichi i byte. Li metta nel Suo bucket. Salvi il Suo 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}"
Già che ci è dentro, salvi il prompt insieme all'immagine. Una colonna di testo, ed è la differenza fra "rigenera quella variante" e "ricomincia da capo".
Altre due abitudini che vale la pena avere dal primo giorno. Controlli che il download abbia restituito byte di immagine e non una pagina di errore, dato che un 200 con dentro dell'HTML si scriverà allegramente nel Suo bucket. E indicizzi lo storage sul Suo ID di riga, non sul nostro job ID, così una rigenerazione sovrascrive l'oggetto giusto invece di lasciarlo orfano.
Passo 6: cosa va storto davvero, quando i volumi salgono
I batch piccoli nascondono tutto. Ecco cosa salta fuori una volta superate qualche centinaio di immagini.
I batch parziali sono normali. Una frazione di qualsiasi run grande fallisce — un prompt che fa scattare un filtro sui contenuti, una riga malformata, un errore transitorio a monte. Tratti 497 su 500 come il caso di successo, e si dia un retry a un comando solo su quelle tre righe.
I retry hanno bisogno di un tetto di spesa. Un ciclo di retry senza cap è un bug che fattura. $0.0238 a colpo sembrano innocui finché un ciclo bloccato non li esegue quarantamila volte. Metta un conteggio secco nel codice — la riga MAX_IMAGES qui sopra è l'assicurazione più economica che esista.
L'idempotenza non è opzionale. I webhook possono essere consegnati più di una volta. Un handler che non è sicuro da eseguire due volte sullo stesso job ID Le regala asset duplicati e righe duplicate, e se ne accorgerà nella settimana più piena.
I costi hanno bisogno di un contatore live, non di una fattura mensile. I valori monetari nella nostra API tornano in micro-centesimi — 1.000.000 equivale a $1. Li sommi per batch e logghi il totale quando si chiude. Una pipeline che ogni notte riporta "412 immagini, $9.81" è una pipeline che nessuno deve andare a controllare.
I default di aspect ratio La sorprenderanno. Lite parte da 1:1, mentre Nano Banana 2 e Pro partono da 9:16. Lo fissi nel template o metà del Suo catalogo diventa verticale in silenzio.
Il watermark viaggia col file. Google imprime SynthID in tutto ciò che i suoi modelli immagine producono. È invisibile, non è un flag, e sopravvive a qualunque cosa la Sua pipeline faccia ai byte dopo. Chi si occupa di brand o di legale dalla Sua parte dovrebbe approvare gli asset generati dall'IA prima che la pipeline esista, non dopo che ha scritto 20.000 oggetti dentro un bucket.

Su quale modello puntare la pipeline
Default su Nano Banana 2 Lite, e ci si sposti solo quando qualcosa di preciso La costringe.
È aritmetica, non un modo per non prendere posizione. A $0.0238 è la cosa più economica che vendiamo capace di fare un'immagine, è di una generazione intera più recente del Nano Banana originale che ha sostituito, e ai volumi di una pipeline il divario di prezzo si compone fino a diventare l'unico numero che il finance guarda. Porta anche quattro aspect ratio che nessun altro ha — 1:4, 4:1, 1:8 e 8:1 — esattamente quello che serve se l'output sono banner e rail verticali. Una stranezza: nessun parametro resolution, proprio, e inviarne uno è un errore, non un no-op silenzioso.
Se ne allontani in questi quattro casi, e non altrimenti:
- Output sopra il 1K. Lite fa solo 1K. Nano Banana 2 arriva al 4K con base $0.04, con il 2K a ×1,5 e il 4K a ×2.
- Testo leggibile dentro l'immagine. Schede prodotto con i prezzi, infografiche localizzate. Quella roba vive su Nano Banana Pro a $0.075, dove 1K e 2K costano uguale — prenda il 2K, è gratis.
- Sfondi trasparenti. Scontorni e fogli di sticker hanno bisogno di
background: transparent, e GPT Image 2 è l'unico modello che portiamo a farlo in una sola chiamata. Metta a budget $0.0525 a 1K, non il numero grezzo di catalogo —qualitymoltiplica per 7 a ogni tier. - Lo stesso volto o la stessa bottiglia su molti frame. È un problema di reference image, non di volume, e ha un articolo tutto suo sulla coerenza.
Per carichi misti, faccia girare il grosso su Lite e devii verso Pro il 2% che ha bisogno di testo o di fedeltà, in base a una colonna dei Suoi dati. Una pipeline, due modelli, prezzi Pro solo dove si guadagnano qualcosa.
Per la passeggiata parametro per parametro dentro il contratto di submit e polling, veda il tutorial sull'API di Nano Banana Pro, e per la scomposizione tier per tier, il confronto fra i modelli. Entrambi danno per scontato che abbia già scelto un modello. Se non l'ha fatto, i modelli text-to-image sono elencati con i prezzi allegati, e il catalogo aggiunge la parte video.
Tutto quanto, in ordine
Template con slot. Dati con ID stabili. Invio come job sotto un tetto secco. Webhook in ricezione. Byte scaricati nel Suo storage. Fallimenti e spesa a log. Sei passi, nessuno difficile, e quello che si salta è sempre il quinto.
Domande frequenti
Come si genera un'immagine in automatico?
Scriva il Suo prompt come template con slot nominati, riempia quegli slot da una fonte di dati come un catalogo prodotti o un CSV, e invii ogni prompt riempito come job asincrono a un'API immagine. Prenda il risultato su un webhook invece di fare polling, poi scarichi i byte dell'immagine nel Suo storage. Niente in quel ciclo ha bisogno di un umano, ed è questo a renderlo automatico invece che lavoro manuale veloce.
Quanto costa generare 1.000 immagini in automatico?
Ai prezzi E2X del 26 agosto 2026, 1.000 immagini attraverso Nano Banana 2 Lite fanno $23.80, dato che ogni request costa $0.0238. Lo stesso volume fa $40.00 su Nano Banana 2 e $75.00 su Nano Banana Pro. Le risoluzioni più alte moltiplicano sopra, ma per l'output 1K standard il numero per immagine è tutto il calcolo.
Meglio un webhook o il polling per i job di generazione immagini?
Usi un webhook per qualsiasi cosa giri non presidiata. Il polling lega la vita del Suo processo al job più lento del batch, quindi un deploy o un timeout serverless ammazza il poller mentre tiene l'unico registro di quali job stiano ancora girando. Un webhook Le permette di inviare e uscire, poi di gestire ogni job completato senza stato. Il polling va bene per uno script che sta guardando girare.
Perché le mie immagini generate smettono di caricarsi dopo un po'?
Perché ha salvato l'URL di output dell'API invece dell'immagine. Quell'URL è consegna temporanea, non storage, quindi una colonna che lo contiene all'inizio si vede correttamente e più avanti si rompe. Scarichi i byte appena il job si completa, li scriva nel Suo bucket, salvi il Suo URL. È il modo più comune in cui una pipeline di immagini va in produzione già rotta.
Su quale modello dovrebbe stare di default una pipeline automatica di immagini?
Nano Banana 2 Lite, a $0.0238 per immagine. È il modello immagine più economico del nostro catalogo ed è di una generazione più recente del Nano Banana originale, e ai volumi di batch il prezzo domina ogni altra considerazione. Passi a Nano Banana 2 solo per output sopra il 1K, a Nano Banana Pro solo per testo leggibile dentro l'immagine, e a GPT Image 2 solo per gli sfondi trasparenti.
Cosa succede quando alcune immagini di un batch falliscono?
Qualcuna fallisce sempre, e la pipeline dovrebbe trattarlo come normale, non come fatale. Continui a inviare dopo una riga rifiutata, logghi gli ID falliti con i loro errori, e costruisca un percorso di retry che esegua solo i fallimenti. Metta un cap ai tentativi di retry, dato che ognuno è un addebito vero.