StabilityMatrix + ComfyUI → Web API: From Local Image Generation to a Real Service
Manage your ComfyUI environment with StabilityMatrix, enable --listen mode, wrap it with FastAPI, and ship a REST API that anyone can call to generate AI images over HTTP.
You have StabilityMatrix installed, ComfyUI running, images looking great. But every session means opening a browser and dragging nodes around — no way to share it, no way to plug it into a product.
This post fixes that: turn your local ComfyUI into a callable API service. Someone sends an HTTP request, your machine returns an image.
Step 1 — Environment: Manage with StabilityMatrix
StabilityMatrix is the best AI model package manager for Windows/macOS/Linux. It handles:
- Isolated Python virtual environments (no system pollution)
- One-click install/upgrade for ComfyUI, A1111, Forge
- Unified model storage directory
- GUI for launch arguments
Setup:
- Download the latest release from github.com/LykosAI/StabilityMatrix/releases
- On first launch, pick a storage directory (separate drive recommended — models are large)
- Click Add Package → ComfyUI, install
- Hit Launch once to confirm everything works
Step 2 — Enable API Mode
ComfyUI has a built-in HTTP API, but it only listens on 127.0.0.1 by default. To expose it to the LAN or a container, you need one launch argument.
In StabilityMatrix:
Click the ⚙️ next to ComfyUI → Extra Launch Arguments, add:
--listen 0.0.0.0 --port 8188
⚠️
0.0.0.0binds all interfaces. Fine for LAN; add auth before exposing to the public internet.
After launch, anyone on your LAN can reach http://your-ip:8188.
Verify the API is up:
curl http://localhost:8188/system_stats
JSON response means you’re good.
Step 3 — Understand the ComfyUI API
The ComfyUI API is small and clean. Three endpoints cover everything:
| Endpoint | Method | Purpose |
|---|---|---|
/prompt | POST | Submit a workflow, get back a prompt_id |
/history/{prompt_id} | GET | Poll status + fetch results |
/view | GET | Download the output image |
Getting the workflow payload:
Design your workflow in the ComfyUI UI, then click Save (API format) in the top-right menu. The saved workflow_api.json is your request body.
Polling for the result:
import requests, time
def generate(workflow: dict, host="http://localhost:8188"):
r = requests.post(f"{host}/prompt", json={"prompt": workflow, "client_id": "demo"})
pid = r.json()["prompt_id"]
while True:
h = requests.get(f"{host}/history/{pid}").json()
if pid in h:
output = h[pid]["outputs"]
img = list(output.values())[0]["images"][0]
return requests.get(f"{host}/view", params=img).content
time.sleep(1)
Step 4 — Wrap It with FastAPI
Exposing the raw ComfyUI API is too low-level for callers — they’d need to know the workflow format. Add a FastAPI layer to present a clean business interface:
# app.py
from fastapi import FastAPI
from fastapi.responses import Response
import json, requests, time, copy
app = FastAPI()
COMFY = "http://localhost:8188"
with open("workflow_api.json") as f:
WORKFLOW_TPL = json.load(f)
@app.post("/generate")
def generate(prompt: str, width: int = 512, height: int = 512, steps: int = 20):
wf = copy.deepcopy(WORKFLOW_TPL)
for node in wf.values():
if node["class_type"] == "KSampler":
node["inputs"]["steps"] = steps
if node["class_type"] == "EmptyLatentImage":
node["inputs"]["width"] = width
node["inputs"]["height"] = height
if node["class_type"] == "CLIPTextEncode":
node["inputs"]["text"] = prompt
r = requests.post(f"{COMFY}/prompt", json={"prompt": wf, "client_id": "api"})
pid = r.json()["prompt_id"]
while True:
h = requests.get(f"{COMFY}/history/{pid}").json()
if pid in h:
imgs = list(h[pid]["outputs"].values())[0]["images"]
img_data = requests.get(f"{COMFY}/view", params=imgs[0]).content
return Response(content=img_data, media_type="image/png")
time.sleep(1)
Run it:
pip install fastapi uvicorn requests
uvicorn app:app --host 0.0.0.0 --port 8000
Call it:
curl "http://localhost:8000/generate?prompt=a+cat+in+space&width=768&height=768" \
--output cat.png
Step 5 — Add API Key Auth
Don’t run a naked API. A simple Bearer token check is enough to start:
from fastapi import Header, HTTPException, Depends
API_KEYS = {"sk-your-key-here"}
def verify(authorization: str = Header(...)):
token = authorization.removeprefix("Bearer ").strip()
if token not in API_KEYS:
raise HTTPException(status_code=401, detail="Unauthorized")
@app.post("/generate")
def generate(prompt: str, _=Depends(verify)):
...
curl -H "Authorization: Bearer sk-your-key-here" \
"http://localhost:8000/generate?prompt=a+cat+in+space" \
--output result.png
Step 6 — Expose to the Internet
Option A: Cloudflare Tunnel (recommended, free)
No public IP required. Cloudflare handles TLS and routing.
winget install Cloudflare.cloudflared
cloudflared tunnel login
cloudflared tunnel create comfyui-api
cloudflared tunnel --url http://localhost:8000
You get a *.trycloudflare.com URL instantly. You can bind a custom domain too.
Option B: nginx reverse proxy (if you have a public IP)
server {
listen 443 ssl;
server_name api.yourdomain.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_read_timeout 120s;
}
}
Option C: ngrok (fastest, good for testing)
ngrok http 8000
Step 7 — Real-time Progress via WebSocket (optional)
Staring at a blank page while waiting for an image is bad UX. ComfyUI pushes progress events over WebSocket:
const ws = new WebSocket(`ws://localhost:8188/ws?clientId=my-app`);
ws.onmessage = (e) => {
const msg = JSON.parse(e.data);
if (msg.type === "progress") {
console.log(`Progress: ${msg.data.value}/${msg.data.max}`);
}
if (msg.type === "executing" && msg.data.node === null) {
console.log("Done!");
}
};
Wire this to a progress bar on the frontend and the experience feels instant.
Summary
| Step | Tool | Purpose |
|---|---|---|
| Environment | StabilityMatrix | Isolated Python + one-click ComfyUI |
| API exposure | --listen 0.0.0.0 | ComfyUI built-in HTTP API |
| Business layer | FastAPI | Clean interface, hide workflow details |
| Auth | Bearer token | Prevent abuse |
| Public access | Cloudflare Tunnel | Free, secure, no public IP needed |
| Live progress | WebSocket | Better user experience |
At the end of this, you have your own AI image generation API — pluggable into any product, shareable with friends, or wrappable in a frontend to ship as a full Web App.
Posted by Seraph @ u14.co · May 2026