git, curl, ComfyUI, one Stable Diffusion checkpoint in .safetensors format, and either an NVIDIA GPU with current drivers or a CPU-only fallback.
/ with \.python3 --version. ComfyUI documents which Python versions it supports — check the docs for the current requirement instead of guessing.git --version.curl --version (used to test the HTTP API).Pre-flight checklist:
python3 --version prints a versiongit --version prints a version.safetensors checkpoint file on disk (or a download link)> Note: this guide deliberately quotes no benchmark numbers, VRAM figures, or version pins — those change constantly. Where a value is environment-specific, it is marked *check the documentation*.
If this is your first local model run, the general setup patterns — virtual environments, model folders, run scripts — are described in How to run AI models locally on your PC: a beginner's guide, and the licensing side of choosing a checkpoint is covered in Open-weight AI models: how to run them for free in 2026.
What to do: clone the repository, create an isolated Python environment, install PyTorch and the remaining dependencies, then place your checkpoint file where ComfyUI looks for it.
git clone https://github.com/comfyanonymous/ComfyUI.git
cd ComfyUI
python3 -m venv venv
source venv/bin/activate # Windows PowerShell: .\venv\Scripts\Activate.ps1
Install PyTorch using the command from the official PyTorch "Get Started" selector for your OS and accelerator (CUDA / ROCm / CPU). The exact flags change per release — *check the documentation*; do not copy a flag set from an old tutorial. Then:
pip install -r requirements.txt
mkdir -p models/checkpoints
cp /path/to/<your-model>.safetensors models/checkpoints/
Why: ComfyUI is a node-graph tool — it composes the generation pipeline visually — while the checkpoint is the actual Stable Diffusion model that produces pixels. A virtual environment keeps ComfyUI's packages from colliding with your system Python, and models/checkpoints is the directory the "Load Checkpoint" node scans.
How to check:
python -c "import torch; print(torch.__version__, torch.cuda.is_available())"
ls -la models/checkpoints
Expected output: a line such as 2.x.x True (the exact version string will differ — that is fine), and a directory listing containing your <your-model>.safetensors. If torch.cuda.is_available() prints False on a machine with an NVIDIA GPU, you installed a CPU-only build — rerun the PyTorch install with the CUDA command from the selector.

What to do: launch ComfyUI as a local web server bound to localhost, build one minimal workflow in the browser, export it in API format, and call it with curl.
python main.py --listen 127.0.0.1 --port 8188
Open http://127.0.0.1:8188 in a browser. Build the smallest working graph:
1. Load Checkpoint — select <your-model>.safetensors.
2. CLIP Text Encode — positive prompt, connected to the checkpoint's CLIP output; a second one for the negative prompt.
3. Empty Latent Image — set width/height (start small, e.g. 512×512, so you can iterate).
4. KSampler — connect model, positive, negative, latent.
5. VAE Decode — connect the sampler's LATENT output and the checkpoint's VAE.
6. Save Image — connect the decoded IMAGE.
Press Queue once to confirm the graph renders an image in the UI. Then use Save (API Format) in the menu to write workflow_api.json — this is the machine-readable version of the same graph.
Why: the browser UI and the HTTP API are two front ends for one engine. The API accepts exactly the JSON you just exported, so anything you can build by hand you can also trigger from your own application.
curl -s -X POST http://127.0.0.1:8188/prompt \
-H "Content-Type: application/json" \
--data-binary @workflow_api.json
Expected response: a JSON object containing a prompt_id (for example {"prompt_id": "8f0c...", "number": 1, "node_errors": {}}). The field names are stable, but the full schema is version-specific — *check the documentation*.
How to check: take the returned prompt_id and poll the history endpoint:
curl -s http://127.0.0.1:8188/history/<prompt_id>
The response is {} (or empty of outputs) while the job is queued and running, then contains the node outputs once finished. A new PNG appears under ComfyUI/output/.
import json, time, urllib.request
BASE = "http://127.0.0.1:8188"
with open("workflow_api.json") as f:
workflow = json.load(f)
body = json.dumps({"prompt": workflow}).encode()
req = urllib.request.Request(f"{BASE}/prompt", data=body,
headers={"Content-Type": "application/json"})
prompt_id = json.loads(urllib.request.urlopen(req).read())["prompt_id"]
print("queued", prompt_id)
while True:
hist = json.loads(urllib.request.urlopen(f"{BASE}/history/{prompt_id}").read())
if prompt_id in hist:
print(json.dumps(hist[prompt_id], indent=2)[:500])
break
time.sleep(1)
Always send the object wrapped as {"prompt": ...} — a bare workflow JSON is rejected with a validation error.
| Endpoint | Method | Purpose |
|---|---|---|
| `/prompt` | POST | Queue a workflow, returns `prompt_id` |
| `/history/{prompt_id}` | GET | Fetch results once the job finishes |
| `/view?filename=...` | GET | Download a generated image by filename |
| `/queue` | GET | Inspect pending and running jobs |
| `/interrupt` | POST | Stop the currently executing job |
Paths and payload fields are the ones documented for the version you installed — *check the documentation* if a call returns 404.

What to do: run one full generation through the API only (no browser), confirm the file on disk, then restrict access.
python client.py
ls -la output/ | tail -n 5
Why: a browser success only proves the UI works; a scripted success proves your application path works, which is the actual goal.
How to check: the script prints a prompt_id and then a JSON history block, and output/ shows a freshly timestamped .png. Open the file — it must match the prompt's subject, not be a black or noise image. If the folder stays empty while the history shows a completed job, your Save Image node wrote to a different filename prefix — inspect the node's filename_prefix.
For remote access, change the bind address and put the server behind a reverse proxy with authentication:
python main.py --listen 0.0.0.0 --port 8188
python main.py --port 8189 # second instance on another port
ComfyUI ships without user accounts: anyone who reaches the port can queue jobs on your GPU. Whether your build includes any auth option — *check the documentation*; otherwise terminate TLS and basic auth at nginx/Caddy, and never expose port 8188 directly to the internet.
Problem. ComfyUI exits immediately with a missing-module error
ModuleNotFoundError: No module named 'torch'venv.source venv/bin/activate
pip install -r requirements.txt
python -c "import torch; print(torch.__version__)"
Problem. Generation crashes on the first queue
RuntimeError: CUDA out of memory. Tried to allocate ... MiBpython main.py --lowvram --port 8188
Problem. The server refuses to start while the UI is still open in another tab
OSError: [Errno 98] Address already in uselsof -i :8188 # Windows: netstat -ano | findstr :8188
kill <PID>
python main.py --port 8189
You now have ComfyUI running as a local server that accepts workflows over HTTP, so any script or app can queue an image job and collect the result. Next, wrap the /prompt + /history pair in your own service endpoint, add a queue or timeout policy, and keep the GPU port private behind a reverse proxy.
Do I need an NVIDIA GPU to run this?
No — ComfyUI runs on CPU as well, but generation is significantly slower. Check the documentation for current supported accelerators before buying hardware.
Where exactly do I put the .safetensors file?
In ComfyUI/models/checkpoints/. After copying, refresh the browser page and the model name appears in the Load Checkpoint dropdown.
Why does my POST to /prompt return a validation error?
You probably sent the raw UI workflow or the file saved by the normal save action. Re-export with Save (API Format) and wrap the object as {"prompt": <workflow>}.
Is the ComfyUI API compatible with the OpenAI images API?
No. It has its own graph-based request format, so existing OpenAI SDK code needs an adapter that translates your prompt into the exported workflow JSON.