Automatizar a geração de imagens com a API do ComfyUI

"A documentação oficial descreve validação e fila por /prompt, além de /ws, /history, /view, /queue e /interrupt."
Você já tem um workflow estável no ComfyUI e precisa de quatro imagens principais para cada um de 200 produtos. Alterar prompt e seed manualmente não é viável. O primeiro POST para /prompt retorna node_errors porque o arquivo não está em API format. Ao terminar, /history mostra apenas filename e subfolder; o arquivo real é baixado por /view.
Passar da GUI para produção por script exige exportar o workflow correto, aguardar pelo WebSocket sem polling cego e controlar concorrência e VRAM para evitar OOM. A seguir estão o script mínimo, parâmetros, estratégias de fila e uma lista de engenharia backend.
Acesso à API local do ComfyUI Server
Inicialização pela CLI e porta padrão
O ComfyUI escuta em 127.0.0.1:8188 por padrão. A inicialização normal basta para processos locais. Informe um endereço de escuta somente se outro equipamento da LAN precisar acessar:
# 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 o terminal mostrar Starting server, abra http://127.0.0.1:8188. A interface do ComfyUI confirma que o serviço está pronto.
Se faltar VRAM, teste --lowvram, --novram ou, como último recurso lento, --cpu, conforme a versão. Com folga, avalie --highvram. Não associe opções a uma capacidade fixa: modelo, precisão, VAE e workflow alteram o pico. Confira python main.py --help e a documentação oficial atual.
Rotas principais da API
As rotas locais ficam em server.py. Um script geralmente usa estes endpoints:
| Rota | Função | Parâmetros/resposta |
|---|---|---|
/prompt | Validar workflow e colocar na fila | POST {"prompt": workflow_dict, "client_id": "..."}; retorna prompt_id ou node_errors |
/history/{prompt_id} | Consultar histórico e metadata | GET; outputs inclui filename, subfolder e type |
/view?filename=...&subfolder=...&type=... | Baixar arquivo de saída | GET; retorna dados binários |
/ws | Receber status por WebSocket | ws://127.0.0.1:8188/ws?clientId=... |
/queue | Consultar a fila | GET; retorna tarefas pendentes |
/interrupt | Interromper a execução atual | POST; útil para timeout |
/upload/image | Enviar imagem de entrada | POST multipart/form-data |
/object_info | Consultar tipos de nós e parâmetros | GET; verifica a existência de um nó |
As rotas podem mudar entre versões. Consulte a documentação atual ou server.py se o comportamento for diferente.
Tipos de mensagem WebSocket
Ao aguardar uma tarefa, acompanhe:
status: estado da fila, incluindoqueue_remainingexecution_start: início comprompt_idexecution_cached: nós reutilizados do cacheexecuting: nó atual;node is Nonecom oprompt_idcorreto indica conclusãoprogress: etapas atual e totalexecuted: nó concluído com metadata de saída
Para detectar o fim, receba type == "executing", confirme data.node como None e compare data.prompt_id com a tarefa enviada.
Exportar um workflow em API format
Etapas do Export Workflow (API)
O JSON salvo pela interface não é a representação esperada pela API. Um workflow comum pode falhar com node_errors; exporte primeiro o API format.
Siga estas etapas:
- Carregue no ComfyUI um workflow que já gere uma imagem válida
- Escolha
File -> Export Workflow (API); algumas versões exibemSave (API Format) - Salve um
.json, comoworkflow_api.json - Confirme IDs numéricos como
"3"e"6", além declass_typeeinputsem cada nó
O nome do menu pode mudar; use a função equivalente de exportação para API da sua versão.
API format versus Save format
O Save format inclui o layout da interface. O API format mantém somente o necessário para executar:
| Formato | Conteúdo | Uso |
|---|---|---|
| Save format | Posições, cores, grupos, dimensões e conexões visuais | Reabrir e editar o layout na interface |
| API format | IDs numéricos, class_type, inputs e _meta opcional | Enviar o workflow por script ou API |
Enviar Save format para /prompt pode retornar node_errors ou error. Carregue na interface e exporte novamente.
Gerenciar IDs de nós
A parametrização precisa dos IDs de prompt, seed, dimensões e saída. Localize:
- Nó de prompt:
CLIPTextEncode, frequentemente"6"em exemplos simples - Nó de seed:
KSampler, frequentemente"3" - Nó de width/height: entrada de
EmptyLatentImageou outro nó específico - Nó de saída:
SaveImageouSaveImageWebsocket
Notes e Groups podem documentar a função, mas o script precisa ler o JSON exportado. Os IDs pertencem ao grafo e podem mudar após nova exportação.
Script mínimo: enviar, aguardar e baixar
O fluxo tem três etapas: enviar para /prompt, aguardar e ler metadata em /history para baixar por /view.
Envio HTTP sem espera
O cliente mínimo faz POST para /prompt e não aguarda. Um worker separado pode consultar as tarefas.
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
Uma resposta correta inclui {"prompt_id": "...", "number": ...}; uma falha inclui {"error": {...}, "node_errors": {...}}. O envio apenas coloca a tarefa na fila.
Aguardar pelo WebSocket
Os eventos WebSocket evitam polling agressivo. Conecte, envie a tarefa e aguarde o evento terminal 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()
Adicione uma espera máxima, como 300 segundos. /interrupt encerra a execução atual, então use com cuidado.
Baixar com History e View
Após a conclusão, solicite /history/{prompt_id} para obter metadata e baixe cada arquivo por /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 fornece metadata; /view retorna o binário.
Parametrizar tarefas em lote
Um workflow estável vira um modelo. Cada iteração altera apenas prompt, seed, dimensões ou outra entrada selecionada.
Parametrizar prompt, seed e dimensões
Altere os valores em 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...
Confirme os IDs "6", "3" e "5" no arquivo exportado. Depois, um loop pode mudar prompt e seed antes de cada envio:
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...
Os exemplos oficiais também alteram KSampler.seed e CLIPTextEncode.text, úteis para localizar parâmetros.
Estratégias de fila para lotes
Para 200 imagens, você pode aumentar batch size ou enviar muitos prompts. A escolha segura depende do modelo e da folga de VRAM medida.
| Estratégia | Características | Casos adequados |
|---|---|---|
| Uma imagem por envio | VRAM mais fácil de controlar por tarefa | Pouca folga, modelos grandes ou workflows complexos |
| Batch size | Várias imagens em um prompt e pico de VRAM geralmente maior | Folga medida, modelos menores e workflows simples |
| Requisições concorrentes | Vários prompts com quantidade ativa limitada | Capacidade medida e workers controlados |
Enviar 20 prompts de uma vez pode lotar a fila e causar OOM ou queda do serviço.
O início conservador usa envio sequencial, conclusão pelo WebSocket e monitoramento de VRAM. Envie a próxima tarefa após a anterior. Se faltar memória, reduza a carga ou teste as opções atuais.
Exemplo de controle de concorrência
Em servidor com várias GPUs ou workers isolados, um semáforo limita tarefas ativas:
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()
Comece com concorrência 1. Aumente após medir pico, latência e falhas com o workflow real. Precisão, VAE, pós-processamento e isolamento mudam o limite.
Monitorar a VRAM
Durante o lote, consulte estatísticas e pause novos envios acima de um limite:
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...
O pico costuma ocorrer durante KSampler e pode cair ao concluir. Um intervalo de 10 a 30 segundos evita sobrecarregar o serviço.
Arquivar as saídas
As saídas precisam de estrutura previsível. {prompt_id}_{seed}_{timestamp}.png mantém ID, seed e horário.
Registre no mínimo:
prompt_id: ID da tarefa no ComfyUIseed: seed aleatórioprompt_text: prompt usadowidth/height: dimensõestimestamp: horário da geraçãobusiness_id: identificador de negócio, como SKU ou pedido
Organize por data ou lote, como outputs/20260624/batch_001/. SQLite ou PostgreSQL pode relacionar metadata e caminhos.
Lista de engenharia backend
Um backend precisa de request ID, timeouts, limites de fila, monitoramento de memória e tratamento explícito de erros. Um script funcional não basta para produção.
Request ID e idempotência
Gere um request_id único, como UUID ou pedido, e relacione-o ao prompt_id. Se um ID concluído voltar, retorne o resultado existente.
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)
O ComfyUI cria o prompt_id, diferente do request_id da aplicação. Persista a relação.
Timeouts e cancelamento
Defina um tempo máximo, como 300 segundos. Ao exceder, /interrupt encerra a execução atual.
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 o WebSocket desconectar ou ficar sem resposta, reconecte ou consulte history. Após interromper, confira /queue e decida sobre tarefas pendentes.
Limites de fila e monitoramento de VRAM
Defina um máximo explícito, como cinco tarefas pendentes, e rejeite ou adie o excesso. Limite também os workers ativos.
Consulte /system_stats para verificar a 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")
Com pressão alta, rejeite novos trabalhos ou aguarde a fila baixar. Reduza batch size e complexidade antes de repetir.
Classificação de erros e novas tentativas
Cada falha exige uma resposta diferente:
| Erro | Causa provável | Tratamento |
|---|---|---|
node_errors | Modelo/nó ausente, parâmetro inválido ou entrada faltando | Corrigir workflow e ambiente; não repetir |
| OOM | VRAM insuficiente | Reduzir batch/carga e alterar opção verificada; não repetir igual |
| Desconexão WebSocket | Problema de rede | Reconectar e consultar /history/{prompt_id} |
| Timeout da tarefa | Carregamento lento ou workflow complexo | Aumentar timeout ou simplificar; repetir uma vez |
| Queda do serviço | Memória esgotada ou falha de GPU | Verificar logs, reiniciar e repetir com cautela |
node_errors identifica classes ausentes e tipos incompatíveis. Corrija workflow, custom nodes ou modelos. OOM também exige mudança de carga.
Comparar API Cloud e local
O Comfy Cloud executa workflows hospedados, mas autenticação, status, WebSocket e concorrência diferem. O API format é reutilizado; os endpoints devem seguir a referência atual.
Diferenças entre rotas
As principais diferenças são:
| Função | API local | API Cloud |
|---|---|---|
| Enviar tarefa | /prompt | /api/prompt |
| Status/resultados | /history/{prompt_id} | /api/job/{prompt_id}/status e /api/jobs/{job_id} |
| Baixar saída | /view | /api/view |
| WebSocket | /ws?clientId=... | /ws?clientId=...&token=... |
Cloud exige X-API-Key e assinatura. A API local escuta apenas 127.0.0.1 por padrão; com --listen, proxy ou redirecionamento, adicione autenticação e controle de acesso. Cloud segue experimental. /api/history_v2/{prompt_id} está deprecated em favor de /api/jobs/{job_id}.
Concorrência e limites da assinatura
A concorrência Cloud depende do plano; o excedente aguarda na fila. As saídas ficam em cloud storage e /api/view retorna uma URL assinada temporária.
Planos, limites, duração e preço mudam, portanto não fixamos números. Cloud evita gerenciar GPU local; no local você cuida de fila, VRAM, segurança e estabilidade.
Próximas etapas
Dentro da série
Esta página leva de um workflow funcional para produção em lote programável:
- Reutilizar workflows do ComfyUI: importação, nós ausentes e caminhos de modelos
- Otimizar ComfyUI com pouca VRAM: memória, batch, OOM e Tiled VAE
- Vídeo no ComfyUI: limite entre lotes de imagens e workflows de vídeo
- Manutenção do ComfyUI: nós ausentes, inicialização e conflitos de versão
Temas relacionados
Para padrões mais amplos de automação:
- Criar workflows de IA com n8n: conectar ComfyUI a várias ferramentas
- API do Ollama: chamadas programáticas, filas e saída estruturada
- Saída LLM estruturada: extração confiável e integração de API
Conclusão
Passar para produção por script significa exportar o workflow API, aguardar pelo WebSocket, baixar com /history e /view, parametrizar entradas e adicionar request ID, timeout, limites e monitoramento de VRAM.
Comece com um workflow e uma imagem. Gere depois dez imagens parametrizadas e observe memória e fila. Adicione idempotência, cancelamento, monitoramento e registros quando o lote estiver estável.
Executar o primeiro lote com a API do ComfyUI
Valide o caminho local desde a exportação até o arquivamento dos resultados.
- 1
Step 1: Validar o workflow da GUI
Gere uma imagem confiável e verifique modelos, custom nodes, entradas e nós de saída. - 2
Step 2: Exportar API format
Use Export Workflow (API) e confira class_type e inputs em cada nó. - 3
Step 3: Enviar uma tarefa
Faça POST do workflow para /prompt, guarde prompt_id e confira node_errors se falhar. - 4
Step 4: Aguardar e baixar
Aguarde por /ws ou consulte /history/{prompt_id}; baixe cada saída por /view. - 5
Step 5: Parametrizar entradas
Copie o modelo, altere prompt, seed, width, height e filename_prefix e valide node ID e class_type. - 6
Step 6: Adicionar proteções
Comece em série e adicione job_id, idempotência, limite, timeout, VRAM, erros e arquivo.
FAQ
Qual workflow JSON usar com a API do ComfyUI?
Qual é o fluxo mínimo da API local?
O que significa node_errors?
Posso recuperar o resultado se o WebSocket cair?
É melhor aumentar batch size ou enviar vários prompts?
Posso enviar vários prompts em paralelo?
Comfy Cloud API e API local são iguais?
12 min de leitura · Publicado em: 24 jul 2026 · Atualizado em: 24 jul 2026
Guia prático de ComfyUI e Stable Diffusion
Se você chegou pela busca, o caminho mais rápido é ir para o post anterior ou próximo desta série.



Comentários
Entre com GitHub para comentar