Alternar tema

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

Easton editorial illustration: large dark node-graph workspace with orange connected nodes

"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:

RotaFunçãoParâmetros/resposta
/promptValidar workflow e colocar na filaPOST {"prompt": workflow_dict, "client_id": "..."}; retorna prompt_id ou node_errors
/history/{prompt_id}Consultar histórico e metadataGET; outputs inclui filename, subfolder e type
/view?filename=...&subfolder=...&type=...Baixar arquivo de saídaGET; retorna dados binários
/wsReceber status por WebSocketws://127.0.0.1:8188/ws?clientId=...
/queueConsultar a filaGET; retorna tarefas pendentes
/interruptInterromper a execução atualPOST; útil para timeout
/upload/imageEnviar imagem de entradaPOST multipart/form-data
/object_infoConsultar tipos de nós e parâmetrosGET; 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, incluindo queue_remaining
  • execution_start: início com prompt_id
  • execution_cached: nós reutilizados do cache
  • executing: nó atual; node is None com o prompt_id correto indica conclusão
  • progress: etapas atual e total
  • executed: 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:

  1. Carregue no ComfyUI um workflow que já gere uma imagem válida
  2. Escolha File -> Export Workflow (API); algumas versões exibem Save (API Format)
  3. Salve um .json, como workflow_api.json
  4. Confirme IDs numéricos como "3" e "6", além de class_type e inputs em 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:

FormatoConteúdoUso
Save formatPosições, cores, grupos, dimensões e conexões visuaisReabrir e editar o layout na interface
API formatIDs numéricos, class_type, inputs e _meta opcionalEnviar 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 EmptyLatentImage ou outro nó específico
  • Nó de saída: SaveImage ou SaveImageWebsocket

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égiaCaracterísticasCasos adequados
Uma imagem por envioVRAM mais fácil de controlar por tarefaPouca folga, modelos grandes ou workflows complexos
Batch sizeVárias imagens em um prompt e pico de VRAM geralmente maiorFolga medida, modelos menores e workflows simples
Requisições concorrentesVários prompts com quantidade ativa limitadaCapacidade 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 ComfyUI
  • seed: seed aleatório
  • prompt_text: prompt usado
  • width/height: dimensões
  • timestamp: horário da geração
  • business_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:

ErroCausa provávelTratamento
node_errorsModelo/nó ausente, parâmetro inválido ou entrada faltandoCorrigir workflow e ambiente; não repetir
OOMVRAM insuficienteReduzir batch/carga e alterar opção verificada; não repetir igual
Desconexão WebSocketProblema de redeReconectar e consultar /history/{prompt_id}
Timeout da tarefaCarregamento lento ou workflow complexoAumentar timeout ou simplificar; repetir uma vez
Queda do serviçoMemória esgotada ou falha de GPUVerificar 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çãoAPI localAPI 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. 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. 2

    Step 2: Exportar API format

    Use Export Workflow (API) e confira class_type e inputs em cada nó.
  3. 3

    Step 3: Enviar uma tarefa

    Faça POST do workflow para /prompt, guarde prompt_id e confira node_errors se falhar.
  4. 4

    Step 4: Aguardar e baixar

    Aguarde por /ws ou consulte /history/{prompt_id}; baixe cada saída por /view.
  5. 5

    Step 5: Parametrizar entradas

    Copie o modelo, altere prompt, seed, width, height e filename_prefix e valide node ID e class_type.
  6. 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?
Use API format. O Save format contém o layout da interface; carregue no ComfyUI e exporte com Export Workflow (API).
Qual é o fluxo mínimo da API local?
Envie o workflow para /prompt, guarde prompt_id, aguarde por WebSocket ou /history/{prompt_id} e chame /view com filename, subfolder e type.
O que significa node_errors?
Contém erros por nó: modelo ou nó ausente, tipo incorreto ou entrada faltando. Corrija workflow ou ambiente antes de repetir.
Posso recuperar o resultado se o WebSocket cair?
Sim. Guarde prompt_id, reconecte ou consulte /history/{prompt_id} e /queue. WebSocket não deve ser o único registro.
É melhor aumentar batch size ou enviar vários prompts?
Em uma GPU local, batch 1 e envios sequenciais são o início mais seguro. A fila facilita falhas, repetição e arquivo.
Posso enviar vários prompts em paralelo?
Sim, mas comece com concorrência 1, copie o workflow por tarefa e aumente após medir VRAM, fila e falhas.
Comfy Cloud API e API local são iguais?
Não. Cloud exige X-API-Key e assinatura; endpoints de status, WebSocket e resultados diferem e a API segue experimental.

12 min de leitura · Publicado em: 24 jul 2026 · Atualizado em: 24 jul 2026

Comentários

Entre com GitHub para comentar

Easton BlogEaston Blog