Automatizzare la generazione di immagini con l’API ComfyUI

"La documentazione ufficiale descrive validazione e coda tramite /prompt, oltre a /ws, /history, /view, /queue e /interrupt."
Hai già un workflow ComfyUI stabile e devi creare quattro immagini principali per ciascuno di 200 prodotti. Modificare prompt e seed a mano non è sostenibile. Il primo POST a /prompt restituisce node_errors perché il file non è in API format. Al termine, /history mostra solo filename e subfolder; il file reale va scaricato tramite /view.
Il passaggio dalla GUI alla produzione via script richiede il corretto API format, l’attesa WebSocket senza polling cieco e il controllo di concorrenza e VRAM per evitare OOM. Seguono uno script minimo, parametri, strategie di coda e una checklist backend.
Accesso all’API locale di ComfyUI Server
Avvio da CLI e porta predefinita
ComfyUI ascolta su 127.0.0.1:8188 per impostazione predefinita. L’avvio normale basta ai processi locali. Specifica un indirizzo di ascolto solo per un dispositivo nella LAN:
# Local access only
python main.py --port 8188
# Allow LAN access; also configure a firewall, authentication, or a reverse proxy
python main.py --listen 0.0.0.0 --port 8188
Quando il terminale mostra Starting server, apri http://127.0.0.1:8188. Se compare l’interfaccia ComfyUI, il servizio è pronto.
Se la VRAM è poca, prova --lowvram, --novram o, come ultima soluzione lenta, --cpu, se supportati. Con margine ampio valuta --highvram. Non associare queste opzioni a una capacità fissa: modello, precisione, VAE e workflow cambiano il picco. Controlla python main.py --help e la documentazione ufficiale corrente.
Route principali dell’API
Le route locali sono definite in server.py. Uno script usa soprattutto questi endpoint:
| Route | Scopo | Parametri/risposta |
|---|---|---|
/prompt | Validare il workflow e inserirlo in coda | POST {"prompt": workflow_dict, "client_id": "..."}; restituisce prompt_id o node_errors |
/history/{prompt_id} | Ottenere cronologia e metadata | GET; outputs include filename, subfolder e type |
/view?filename=...&subfolder=...&type=... | Scaricare un file | GET; restituisce dati binari |
/ws | Ricevere lo stato via WebSocket | ws://127.0.0.1:8188/ws?clientId=... |
/queue | Consultare la coda | GET; restituisce i job in attesa |
/interrupt | Interrompere l’esecuzione corrente | POST; utile per timeout |
/upload/image | Caricare un’immagine di input | POST multipart/form-data |
/object_info | Consultare tipi di nodi e parametri | GET; verifica la presenza di un nodo |
Le route possono cambiare tra versioni. Consulta la documentazione corrente o server.py se il comportamento differisce.
Tipi di messaggi WebSocket
Durante l’attesa controlla:
status: stato della coda, inclusoqueue_remainingexecution_start: avvio conprompt_idexecution_cached: nodi riutilizzati dalla cacheexecuting: nodo corrente;node is Nonecon ilprompt_idcorretto indica la fineprogress: passaggio corrente e totaleexecuted: nodo completato con metadata di output
Per rilevare la fine, ricevi type == "executing", verifica che data.node sia None e che data.prompt_id coincida con il job inviato.
Esportare un workflow in API format
Passaggi di Export Workflow (API)
Il JSON salvato dall’interfaccia non è il formato atteso dall’API. Un workflow normale può fallire con node_errors, quindi esporta prima l’API format.
Procedi così:
- Carica in ComfyUI un workflow che genera già un’immagine valida
- Scegli
File -> Export Workflow (API); alcune versioni mostranoSave (API Format) - Salva un file
.json, per esempioworkflow_api.json - Verifica ID numerici come
"3"e"6", piùclass_typeeinputsin ogni nodo
Il nome del menu può cambiare; usa la funzione equivalente per l’esportazione API della tua versione.
API format e Save format
Il Save format include il layout dell’interfaccia. L’API format mantiene soltanto i dati necessari all’esecuzione:
| Formato | Contenuto | Uso |
|---|---|---|
| Save format | Posizioni, colori, gruppi, dimensioni e collegamenti visivi | Riaprire e modificare il layout |
| API format | ID numerici, class_type, inputs e _meta opzionale | Inviare il workflow via script o API |
Inviare Save format a /prompt può restituire node_errors o error. Caricalo nell’interfaccia ed esportalo di nuovo.
Gestire gli ID dei nodi
La parametrizzazione richiede gli ID di prompt, seed, dimensioni e output. Individua:
- Nodo prompt:
CLIPTextEncode, spesso"6"negli esempi semplici - Nodo seed:
KSampler, spesso"3" - Nodo width/height: input di
EmptyLatentImageo di un nodo specifico - Nodo di output:
SaveImageoSaveImageWebsocket
Note e Group possono documentare il ruolo, ma lo script deve leggere il JSON esportato. Gli ID appartengono al grafo e possono cambiare dopo una nuova esportazione.
Script minimo: invio, attesa e download
Il flusso ha tre passaggi: inviare a /prompt, attendere, leggere i metadata in /history e scaricare tramite /view.
Invio HTTP senza attesa
Il client minimo esegue POST su /prompt e non attende. Un worker separato può interrogare i job.
import json
import requests
# Load an API-format workflow
with open("workflow_api.json", "r") as f:
workflow = json.load(f)
# Build the request payload
payload = {
"prompt": workflow,
"client_id": "my-script-client" # Optional; associates the job with WebSocket events
}
# Add the job to the queue
response = requests.post("http://127.0.0.1:8188/prompt", json=payload)
if response.status_code == 200:
result = response.json()
prompt_id = result["prompt_id"]
print(f"Submitted: {prompt_id}")
else:
error = response.json()
print(f"Submission failed: {error}")
# node_errors contains node-level validation details
Una risposta valida contiene {"prompt_id": "...", "number": ...}; un errore contiene {"error": {...}, "node_errors": {...}}. L’invio aggiunge soltanto il job alla coda.
Attendere via WebSocket
Gli eventi WebSocket evitano polling aggressivo. Connettiti, invia il job e attendi l’evento terminale executing.
import json
import uuid
import requests
import websocket
# Load the workflow
with open("workflow_api.json", "r") as f:
workflow = json.load(f)
# Generate a client ID
client_id = str(uuid.uuid4())
# Connect to WebSocket
ws = websocket.create_connection(f"ws://127.0.0.1:8188/ws?clientId={client_id}")
# Submit the job
payload = {"prompt": workflow, "client_id": client_id}
response = requests.post("http://127.0.0.1:8188/prompt", json=payload)
prompt_id = response.json()["prompt_id"]
# Wait for completion
while True:
message = ws.recv()
data = json.loads(message)
if data["type"] == "executing":
# node is None with the same prompt_id when the job is complete
if data["data"]["node"] is None and data["data"]["prompt_id"] == prompt_id:
print("Job complete")
break
ws.close()
Aggiungi un’attesa massima, per esempio 300 secondi. /interrupt ferma l’esecuzione corrente, quindi usalo con cautela.
Scaricare con History e View
Dopo il completamento, richiedi /history/{prompt_id} per i metadata e scarica ogni file tramite /view.
import requests
# Retrieve job history
history_url = f"http://127.0.0.1:8188/history/{prompt_id}"
history = requests.get(history_url).json()
# Traverse output nodes
outputs = history[prompt_id]["outputs"]
for node_id, node_output in outputs.items():
if "images" in node_output:
for image in node_output["images"]:
filename = image["filename"]
subfolder = image.get("subfolder", "")
type = image.get("type", "output")
# Build the download URL
view_url = f"http://127.0.0.1:8188/view?filename={filename}&subfolder={subfolder}&type={type}"
# Download the binary image
img_data = requests.get(view_url).content
with open(f"output_{filename}", "wb") as f:
f.write(img_data)
print(f"Saved: output_{filename}")
outputs segue {node_id: {"images": [{"filename": "...", "subfolder": "...", "type": "..."}]}}. History fornisce i metadata; /view il binario.
Parametrizzare i job in serie
Un workflow stabile diventa un modello per la serie. Ogni iterazione modifica solo prompt, seed, dimensioni o un altro input scelto.
Parametrizzare prompt, seed e dimensioni
Modifica i valori in inputs:
import json
import random
# Load the workflow
with open("workflow_api.json", "r") as f:
workflow = json.load(f)
# Change the prompt; use the IDs from your own workflow
workflow["6"]["inputs"]["text"] = "a beautiful landscape, sunset, mountains"
# Generate a random seed
workflow["3"]["inputs"]["seed"] = random.randint(0, 1000000)
# Change the dimensions
workflow["5"]["inputs"]["width"] = 1024
workflow["5"]["inputs"]["height"] = 768
# Submit the job...
Conferma gli ID "6", "3" e "5" nel file esportato. Un ciclo può quindi modificare prompt e seed prima di ogni invio:
prompts = [
"product photo, white background",
"product photo, outdoor scene",
"product photo, studio lighting"
]
for i, prompt_text in enumerate(prompts):
workflow["6"]["inputs"]["text"] = prompt_text
workflow["3"]["inputs"]["seed"] = random.randint(0, 1000000)
# Submit the job
payload = {"prompt": workflow, "client_id": client_id}
response = requests.post("http://127.0.0.1:8188/prompt", json=payload)
prompt_id = response.json()["prompt_id"]
# Wait over WebSocket...
# Download outputs...
Gli esempi ufficiali modificano anche KSampler.seed e CLIPTextEncode.text, utili per localizzare i parametri.
Strategie di coda per le serie
Per 200 immagini puoi aumentare batch size o inviare molti prompt. La scelta sicura dipende dal modello e dal margine VRAM misurato.
| Strategia | Caratteristiche | Casi adatti |
|---|---|---|
| Un’immagine per invio | VRAM più semplice da controllare per job | Poco margine, modelli grandi o workflow complessi |
| Batch size | Più immagini in un prompt e picco VRAM di solito maggiore | Margine misurato, modelli piccoli e workflow semplici |
| Richieste concorrenti | Più prompt con numero attivo limitato | Capacità misurata e worker controllati |
Inviare 20 prompt insieme può riempire la coda e causare OOM o arresto del servizio.
L’inizio prudente usa invio sequenziale, completamento WebSocket e monitoraggio VRAM. Invia il job successivo dopo il precedente. Se manca memoria, riduci il carico o prova le opzioni correnti.
Esempio di controllo della concorrenza
Su più GPU o worker isolati, un semaforo limita i job attivi:
import copy
import threading
# Allow at most two active jobs
semaphore = threading.Semaphore(2)
def submit_and_wait(prompt_text, seed):
with semaphore:
# Give each task its own copy instead of mutating shared state
job_workflow = copy.deepcopy(workflow)
job_workflow["6"]["inputs"]["text"] = prompt_text
job_workflow["3"]["inputs"]["seed"] = seed
# Submit and wait...
# WebSocket loop...
# The permit is returned for the next task
# Submit the batch
threads = []
for i in range(50):
t = threading.Thread(target=submit_and_wait, args=(prompts[i], seeds[i]))
threads.append(t)
t.start()
for t in threads:
t.join()
Inizia con concorrenza 1. Aumenta dopo aver misurato picco, latenza ed errori nel workflow reale. Precisione, VAE, post-processing e isolamento cambiano il limite.
Monitorare la VRAM
Durante la serie, interroga le statistiche e sospendi i nuovi invii oltre una soglia:
import time
def check_vram(threshold=0.8):
stats = requests.get("http://127.0.0.1:8188/system_stats").json()
vram_used = stats["system_stats"]["devices"][0]["vram_used"]
vram_total = stats["system_stats"]["devices"][0]["vram_total"]
return (vram_used / vram_total) > threshold
# Check memory before each submission
for prompt_text in prompts:
while check_vram(0.85):
print("VRAM pressure is high; waiting 30 seconds...")
time.sleep(30)
# Submit the next job...
# Wait over WebSocket...
Il picco si verifica spesso durante KSampler e può scendere al termine. Un controllo ogni 10–30 secondi evita di sovraccaricare il servizio.
Archiviare gli output
Gli output richiedono una struttura prevedibile. {prompt_id}_{seed}_{timestamp}.png conserva ID, seed e orario.
Registra almeno:
prompt_id: ID del job ComfyUIseed: seed casualeprompt_text: prompt usatowidth/height: dimensionitimestamp: ora di generazionebusiness_id: identificatore applicativo come SKU o ordine
Organizza per data o serie, per esempio outputs/20260624/batch_001/. SQLite o PostgreSQL può collegare metadata e percorsi.
Checklist di ingegneria backend
Un backend richiede request ID, timeout, limiti della coda, monitoraggio memoria e gestione esplicita degli errori. Uno script funzionante non basta in produzione.
Request ID e idempotenza
Genera un request_id univoco, come UUID o numero d’ordine, e associalo al prompt_id. Se ritorna un ID già completato, restituisci il risultato esistente.
import uuid
# Business request ID
request_id = str(uuid.uuid4())
# Store the mapping in a database or cache
request_prompt_map[request_id] = prompt_id
# Return an existing result for duplicate requests
if request_id in completed_requests:
return get_cached_result(request_id)
ComfyUI crea il prompt_id, diverso dal request_id dell’applicazione. Salva l’associazione.
Timeout e annullamento
Imposta una durata massima, per esempio 300 secondi. Al superamento, /interrupt ferma l’esecuzione corrente.
import time
timeout = 300 # Five minutes
start_time = time.time()
# Wait for WebSocket messages...
while True:
elapsed = time.time() - start_time
if elapsed > timeout:
# Interrupt the current execution
requests.post("http://127.0.0.1:8188/interrupt")
print("Job timed out and was interrupted")
break
# Process normal messages...
Se WebSocket si disconnette o resta senza risposta, riconnettiti o consulta history. Dopo l’interruzione, controlla /queue e decidi sui job in attesa.
Limiti della coda e monitoraggio VRAM
Imposta un massimo esplicito, come cinque job in attesa, e rifiuta o rinvia l’eccesso. Limita anche i worker attivi.
Interroga /system_stats per controllare la VRAM:
stats = requests.get("http://127.0.0.1:8188/system_stats").json()
vram_used = stats["system_stats"]["devices"][0]["vram_used"]
vram_total = stats["system_stats"]["devices"][0]["vram_total"]
vram_percent = vram_used / vram_total
if vram_percent > 0.8:
print("VRAM pressure is high; rejecting a new request")
Con pressione elevata, rifiuta nuovo lavoro o attendi che la coda scenda. Riduci batch size e complessità prima di riprovare.
Classificazione degli errori e nuovi tentativi
Ogni errore richiede una risposta diversa:
| Errore | Causa probabile | Gestione |
|---|---|---|
node_errors | Modello/nodo assente, parametro non valido o input mancante | Correggere workflow e ambiente; non riprovare |
| OOM | VRAM insufficiente | Ridurre batch/carico e cambiare un’opzione verificata; non ripetere uguale |
| Disconnessione WebSocket | Problema di rete | Riconnettere e consultare /history/{prompt_id} |
| Timeout del job | Caricamento lento o workflow complesso | Aumentare timeout o semplificare; riprovare una volta |
| Arresto del servizio | Memoria esaurita o errore GPU | Controllare log, riavviare e riprovare con cautela |
node_errors identifica classi assenti e tipi incompatibili. Correggi workflow, custom nodes o modelli. Anche OOM richiede una modifica del carico.
Confrontare API Cloud e locale
Comfy Cloud esegue workflow ospitati, ma autenticazione, stato, WebSocket e concorrenza differiscono. L’API format è riutilizzabile; gli endpoint devono seguire il riferimento corrente.
Differenze tra le route
Le differenze principali sono:
| Funzione | API locale | API Cloud |
|---|---|---|
| Inviare job | /prompt | /api/prompt |
| Stato/risultati | /history/{prompt_id} | /api/job/{prompt_id}/status e /api/jobs/{job_id} |
| Scaricare output | /view | /api/view |
| WebSocket | /ws?clientId=... | /ws?clientId=...&token=... |
Cloud richiede X-API-Key e un abbonamento. L’API locale ascolta solo 127.0.0.1 per impostazione predefinita; con --listen, proxy o port forwarding devi aggiungere autenticazione e controllo accessi. Cloud resta experimental. /api/history_v2/{prompt_id} è deprecated a favore di /api/jobs/{job_id}.
Concorrenza e limiti dell’abbonamento
La concorrenza Cloud dipende dal piano; l’eccesso attende in coda. Gli output sono nel cloud storage e /api/view restituisce un URL firmato temporaneo.
Piani, limiti, durata e prezzi cambiano, quindi non fissiamo numeri. Cloud evita la gestione della GPU locale; in locale gestisci coda, VRAM, sicurezza e stabilità.
Passaggi successivi
Nella serie
Questa pagina porta da un workflow funzionante a una produzione in serie programmabile:
- Riutilizzare workflow ComfyUI: importazione, nodi mancanti e percorsi dei modelli
- Ottimizzare ComfyUI con poca VRAM: memoria, batch, OOM e Tiled VAE
- Video con ComfyUI: confine tra serie di immagini e workflow video
- Manutenzione ComfyUI: nodi mancanti, avvio e conflitti di versione
Argomenti correlati
Per schemi di automazione più ampi:
- Creare workflow IA con n8n: collegare ComfyUI a più strumenti
- API Ollama: chiamate programmatiche, code e output strutturati
- Output LLM strutturato: estrazione affidabile e integrazione API
Conclusione
Passare alla produzione via script significa esportare il workflow API, attendere via WebSocket, scaricare con /history e /view, parametrizzare gli input e aggiungere request ID, timeout, limiti e monitoraggio VRAM.
Inizia con un workflow e un’immagine. Genera poi dieci immagini parametrizzate e osserva memoria e coda. Aggiungi idempotenza, annullamento, monitoraggio e registri quando la serie è stabile.
Eseguire la prima serie con l’API ComfyUI
Valida il percorso locale dall’esportazione all’archiviazione dei risultati.
- 1
Step 1: Validare il workflow GUI
Genera un’immagine affidabile e verifica modelli, custom nodes, input e nodi di output. - 2
Step 2: Esportare API format
Usa Export Workflow (API) e controlla class_type e inputs in ogni nodo. - 3
Step 3: Inviare un job
Esegui POST del workflow su /prompt, conserva prompt_id e controlla node_errors in caso di errore. - 4
Step 4: Attendere e scaricare
Attendi su /ws o interroga /history/{prompt_id}, poi scarica ogni output con /view. - 5
Step 5: Parametrizzare gli input
Copia il modello, cambia prompt, seed, width, height e filename_prefix e verifica node ID e class_type. - 6
Step 6: Aggiungere protezioni
Inizia in sequenza e aggiungi job_id, idempotenza, limite, timeout, VRAM, errori e archivio.
FAQ
Quale workflow JSON usa l’API ComfyUI?
Qual è il flusso minimo dell’API locale?
Che cosa indica node_errors?
Posso recuperare il risultato dopo una disconnessione WebSocket?
Conviene aumentare batch size o inviare più prompt?
Posso inviare più prompt in parallelo?
Comfy Cloud API e API locale sono uguali?
12 min di lettura · Pubblicato il: 24 lug 2026 · Aggiornato il: 24 lug 2026
Guida pratica a ComfyUI e Stable Diffusion
Se arrivi dalla ricerca, il modo più veloce per orientarti è passare all’articolo precedente o successivo della stessa serie.
Precedente
Creare video in ComfyUI con Wan e AnimateDiff
Configura Wan o AnimateDiff in ComfyUI, organizza modelli e custom nodes e risolvi frames, FPS, limiti VRAM, esportazione video ed errori comuni del workflow.
Parte 9 di 10
Successivo
Questo è l’articolo più recente della serie per ora.



Commenti
Accedi con GitHub per lasciare un commento