Baustelle: Work in Progress · noindex

Tutorial · Einsteiger

ComfyUI API Grundlagen: Workflows programmatisch steuern

Python ComfyUI KI-Bildgenerierung Lesezeit ~10 Min Stand: Juli 2026

ComfyUI ist nicht nur eine grafische Oberfläche – jeder Workflow lässt sich auch per API auslösen. Damit automatisierst du die Bildgenerierung, bindest sie in Apps ein oder erzeugst ganze Serien. Dieses Tutorial zeigt den kompletten Weg vom Server-Start bis zum abgeholten Bild.

Voraussetzungen & Use-Cases

Du solltest ComfyUI bereits über die GUI bedienen können und einen einfachen Text-zu-Bild-Workflow kennen. Außerdem brauchst du Python 3.9+ und die Bibliothek requests (pip install requests).

Typische Anwendungsfälle für die API:

Die API ist eine gewöhnliche HTTP-Schnittstelle. Wenn dir Begriffe wie POST oder Status-Codes noch nichts sagen, lies zuerst die REST-API-Grundlagen.

API-Server starten

Die ComfyUI-API läuft im selben Prozess wie die GUI – du musst nichts Zusätzliches installieren. Starte ComfyUI wie gewohnt:

bash
# Startet Server + API unter http://127.0.0.1:8188
python main.py

# Prüfen, ob der Server antwortet:
curl http://127.0.0.1:8188/system_stats
Sicherheit

Mit --listen 0.0.0.0 ist der Server im gesamten Netzwerk erreichbar – ComfyUI hat keine eingebaute Authentifizierung. Nutze das nur in vertrauenswürdigen Netzen oder hinter einem Reverse-Proxy mit Zugriffsschutz.

Workflow im API-Format exportieren

Wichtig: Die API erwartet nicht das normale Workflow-JSON aus dem Browser, sondern das schlankere API-Format (ohne Layout-Infos wie Positionen oder Farben). So kommst du dran:

Das exportierte JSON ist eine Sammlung von Nodes. Jeder Node hat eine ID, einen class_type und seine inputs. Verweise auf andere Nodes stehen als Liste ["node_id", output_index]:

json — workflow_api.json (Auszug)
{
  "3": {
    "class_type": "KSampler",
    "inputs": {
      "seed": 42,
      "steps": 20,
      "cfg": 8,
      "sampler_name": "euler",
      "model": ["4", 0],
      "positive": ["6", 0],
      "latent_image": ["5", 0]
    }
  },
  "6": {
    "class_type": "CLIPTextEncode",
    "inputs": { "text": "a red sports car", "clip": ["4", 1] }
  }
}
Merke dir die Node-IDs

Um den Prompt später per Skript zu ändern, musst du wissen, welcher Node dein Text-Prompt ist (hier Node "6", ein CLIPTextEncode). Die IDs siehst du im exportierten JSON.

Ersten API-Call senden

Ein Workflow wird per POST /prompt in die Warteschlange gelegt. Der Body muss die Form { "prompt": <workflow> } haben. Als Antwort kommt eine prompt_id zurück.

python — generate.py
import requests
import json
import time

SERVER = "http://127.0.0.1:8188"

# 1. Workflow im API-Format laden
with open("workflow_api.json", "r", encoding="utf-8") as f:
    workflow = json.load(f)

# 2. Prompt-Text dynamisch setzen (Node "6" = CLIPTextEncode)
workflow["6"]["inputs"]["text"] = "a red sports car, studio lighting"

# 3. Workflow in die Queue legen
resp = requests.post(f"{SERVER}/prompt", json={"prompt": workflow})
resp.raise_for_status()
prompt_id = resp.json()["prompt_id"]
print("Prompt-ID:", prompt_id)

Als curl-Variante muss der Body ebenfalls in {"prompt": …} verpackt sein. Die exportierte Datei enthält aber nur den Workflow selbst – mit jq wickelst du sie passend ein:

bash
jq '{prompt: .}' workflow_api.json \
  | curl -X POST http://127.0.0.1:8188/prompt \
      -H "Content-Type: application/json" \
      -d @-

Bilder abrufen

Die Generierung läuft asynchron. Du fragst GET /history/{prompt_id} ab, bis dein Auftrag dort auftaucht. Die fertigen Bilder holst du dann über GET /view – dieser Endpunkt braucht drei Parameter: filename, subfolder und type (meist output).

python — generate.py (Fortsetzung)
# 4. Auf Fertigstellung warten (Polling der History)
while True:
    history = requests.get(f"{SERVER}/history/{prompt_id}").json()
    if prompt_id in history:
        outputs = history[prompt_id]["outputs"]
        break
    time.sleep(1)

# 5. Bild(er) herunterladen
for node_id, node_output in outputs.items():
    for image in node_output.get("images", []):
        params = {
            "filename": image["filename"],
            "subfolder": image["subfolder"],   # oft leer ""
            "type": image["type"],             # meist "output"
        }
        data = requests.get(f"{SERVER}/view", params=params).content
        with open(image["filename"], "wb") as out:
            out.write(data)
        print("Gespeichert:", image["filename"])

Die History-Antwort ist dabei so aufgebaut (vereinfacht):

json — Antwort von /history/{prompt_id}
{
  "<prompt_id>": {
    "outputs": {
      "9": {
        "images": [
          { "filename": "ComfyUI_00001_.png", "subfolder": "", "type": "output" }
        ]
      }
    }
  }
}

Robuster: WebSocket statt Polling

Polling funktioniert, ist aber ineffizient. Sauberer ist der WebSocket-Endpunkt ws://127.0.0.1:8188/ws?clientId=<uuid>: Er meldet in Echtzeit Fortschritt und Fertigstellung. Übergib dieselbe clientId zusätzlich im /prompt-Body, damit du die Events zuordnen kannst. Der Auftrag ist fertig, wenn eine executing-Nachricht mit node: null und passender prompt_id eintrifft. Für den Einstieg reicht das Polling oben – für produktive Setups lohnt sich der Umstieg auf WebSocket.

Troubleshooting

ProblemUrsache & Lösung
Port 8188 belegtAnderen Port wählen: python main.py --port 8189
Browser-Zugriff blockiert (CORS)Server mit --enable-cors-header starten
KeyError: 'prompt_id'node_errors in der Antwort prüfen – meist ist das Workflow-JSON ungültig oder ein Model fehlt
Bild wird nicht gefunden (404 bei /view)subfolder und type aus der History mitgeben, nicht nur filename
Model nicht gefundenDateinamen im JSON gegen models/checkpoints/ abgleichen
Zusammengefasst

Drei Endpunkte reichen für den Einstieg: POST /prompt (auslösen), GET /history/{prompt_id} (Status) und GET /view (Bild holen). Damit lässt sich jede GUI-Aktion automatisieren – die Grundlage für Batch-Generierung und eigene Tools.