Skip to main content
Comfy

comfy-api-proxy

The Comfy API v2, in front of your own ComfyUI.
Point SDK code written for Comfy Cloud at a self-hosted ComfyUI — same contract, no forked integration.

PyPI Python 3.10+ CI License: MIT Comfy Cloud


A local service that puts the Comfy API v2 in front of a self-hosted ComfyUI instance. The same client SDK code that talks to Comfy Cloud can drive a ComfyUI on your own machine instead — and, since this proxy is Python + aiohttp (the same stack as ComfyUI core), the same adapter can later move into core itself. That's the "one contract, three surfaces" idea: Comfy Cloud, this proxy, and (eventually) ComfyUI core all speak the same /api/v2/ shape, so integrator code doesn't need to fork depending on where it's pointed.

Requirements & install

  • Python 3.10+ (CI runs 3.10, 3.11, and 3.12 on every pull request).

  • Install from PyPI:

    pip install comfy-api-proxy
    

Quickstart

Against a real ComfyUI

Point the proxy at an already-running ComfyUI and it serves /api/v2/* on its own port. It runs in the foreground until you stop it with Ctrl+C:

# Defaults: proxy the ComfyUI at 127.0.0.1:8188, serve the v2 API on :8189.
comfy-api-proxy

# Point it elsewhere / co-locate with ComfyUI to enable model-directory uploads:
comfy-api-proxy --comfyui http://127.0.0.1:8188 --port 8189 \
  --comfyui-base-dir /path/to/ComfyUI

# Background it with your shell (& ) if you want your prompt back:
comfy-api-proxy &

No-GPU demo

demo/fake_comfyui.py is a stand-in ComfyUI — just enough of the native /prompt / /history / /queue / /view / /ws surface to run a workflow end to end without a GPU (or ComfyUI installed at all):

python demo/fake_comfyui.py &          # a stand-in ComfyUI on :8188
comfy-api-proxy &                      # the proxy on :8189
python demo/run_demo.py                # submit → wait → download

demo/run_demo.py drives the proxy through the real Python SDK (comfy_sdk.Comfy) — the same client code you'd point at Comfy Cloud — and imports it from a sibling checkout, so clone the SDK repo (see SDKs and the API contract below) next to this one before running the demo:

some-parent-dir/
├── comfy-api-proxy/   (this repo)
└── comfy-python-sdk/

CI never depends on that checkout being present: the test suite (tests/test_smoke.py) drives the proxy's own HTTP surface directly with only the standard library, so pytest works with nothing but this repo installed.

The /api/v2/ surface

Wraps ComfyUI's native HTTP + WebSocket API one-to-one:

v2 operation Backed by
POST /api/v2/jobs Resolves any core/ASSET reference in the workflow to the filename ComfyUI expects, then POST /prompt
GET /api/v2/jobs Lists jobs this proxy recorded (optional status / limit), newest first, with truncated when the scan stopped early; proxy-local
GET /api/v2/jobs/{id} GET /history/{id} (+ /queue while queued/running) — the authoritative, pollable state
POST /api/v2/jobs/{id}/cancel ComfyUI's atomic POST /api/jobs/{id}/cancel
GET /api/v2/jobs/{id}/events Server-Sent Events, driven by ComfyUI's /ws (the only live signal ComfyUI exposes)
POST /api/v2/assets Multipart upload; blake3-hashed and deduped locally; routed to ComfyUI's /upload/image for workflow inputs, or placed directly in a model directory (see below) for model weights
POST /api/v2/assets/from-hash, HEAD /api/v2/assets/by-hash/{hash} Local hash index
POST /api/v2/assets/from-path Zero-copy register of a file already under --comfyui-base-dir
GET /api/v2/assets/{id}, GET /api/v2/assets/{id}/content Local index / ComfyUI /view, Range-capable; missing bytes → 404 output_unavailable
GET /api/v2/health Cheap process probe (does not call ComfyUI)

Poll-first, same as the canonical contract: GET /api/v2/jobs/{id} is always the source of truth for a job's state; the SSE stream (GET /api/v2/jobs/{id}/events) is a live convenience layered on top of it — a client that never opens it still sees the same state by polling. See Live events (SSE) below for what the stream carries and its concurrent-connection limit.

Partner (API) node auth — extra_data

Workflows that use partner/API nodes (Gemini, etc.) need a Comfy API key to authenticate them. Pass it alongside the workflow on submit:

POST /api/v2/jobs
{
  "workflow": { /* API-format graph */ },
  "extra_data": { "api_key_comfy_org": "comfyui-…" }
}

extra_data is a closed, typed object — the only accepted field is api_key_comfy_org (any other shape is rejected 400 invalid_request). One key authenticates every partner node in the workflow. The proxy forwards it verbatim onto ComfyUI's /prompt call and never stores or logs it (ComfyUI likewise strips it from history). Omit extra_data entirely when you have no key.

Model-file uploads (checkpoints/, loras/, vae/, ...)

ComfyUI's own /upload/image only understands input/output/temp — it has no endpoint for placing a file into a model directory. This proxy can do that itself, but only when it is co-located with ComfyUI (same host, sharing a filesystem) and started with --comfyui-base-dir pointing at the ComfyUI install root. Without that flag, model-directory uploads are rejected with a clear error; workflow-input uploads (images, etc.) work either way.

When enabled, a model upload must clear all of the following before a byte touches disk:

  • safetensors-only, verified by parsing the file's own header (the length-prefixed JSON tensor index) — never a pickle/torch.load path.
  • Allowlisted destination roots only — the real ComfyUI model directories (checkpoints, loras, vae, controlnet, ...). configs and custom_nodes are deliberately excluded even though ComfyUI itself has directories by those names, since one holds arbitrary YAML and the other arbitrary Python.
  • No path traversal, including through a symlink — the resolved, real (symlink-followed) destination path must still land inside the configured model directory.
  • Atomic, no-clobber writes — a temp file plus O_EXCL on the final destination, so two uploads can never race into a torn or silently overwritten file.

Once placed, the asset's file_path — and the value substituted for any core/ASSET reference to it in a submitted workflow — is the filename relative to the model-root directory (e.g. my_model.safetensors, not checkpoints/my_model.safetensors). That matches how ComfyUI's own combo widgets/loaders reference a model internally; a category-qualified path would be rejected as an unknown filename.

Live events (SSE)

GET /api/v2/jobs/{id}/events opens one WebSocket connection to ComfyUI, performs its feature_flags handshake, and translates the native progress/progress_state/preview/terminal messages into the v2 SSE event catalog (status, progress, preview, output), throttled to ~2 progress/preview events per second. If ComfyUI's WebSocket is unreachable, the stream falls back to polling /history so it still resolves to an authoritative terminal status rather than failing outright.

A proxy instance also caps concurrent event streams (8 by default, since each one holds open a ComfyUI WebSocket connection). Past that limit, a new stream request gets 429 too_many_streams with a Retry-After hint instead of queuing or degrading — GET /api/v2/jobs/{id} polling is always available regardless, and a slot frees up as soon as the stream it belongs to ends (the job finishes, or the client disconnects).

Security defaults

Everything here is on by default — no flags needed to get to the safe configuration:

  • Binds to 127.0.0.1 only. Widening --host to a non-loopback address is refused (the process exits with an error) unless --token is set, or --allow-insecure-bind is passed to explicitly opt out of that guard.
  • An optional static bearer token (--token) gates all of /api/v2/* when configured — except GET /api/v2/health, which stays unauthenticated so a supervisor can probe liveness without holding the token. Unset by default, since a self-hosted single-user ComfyUI usually has nothing to authenticate against.
  • A default-on origin-check middleware — ported from ComfyUI core's own create_origin_only_middleware — rejects cross-site browser requests even when nothing else is configured, closing the DNS-rebinding / drive-by-CSRF hole any unauthenticated localhost server is exposed to. Opt in a hosted web app with --enable-cors-header <origin> (repeatable; * refused) — see Browser access.
  • Model-file uploads are safetensors-only, with path-traversal and symlink-escape guards (see Model-file uploads above) — and are rejected outright unless the proxy was started co-located with --comfyui-base-dir.

CLI reference

comfy-api-proxy --comfyui http://127.0.0.1:8188 --port 8189 [options]
Flag Default What it does
--comfyui http://127.0.0.1:8188 Base URL of the self-hosted ComfyUI to proxy.
--host 127.0.0.1 Address to bind. Widening past loopback requires --token or --allow-insecure-bind (see Security defaults).
--port 8189 Port to serve the v2 API on.
--token (unset) Require Authorization: Bearer <token> on every /api/v2/* request except GET /api/v2/health.
--comfyui-base-dir (unset) Filesystem root of a co-located ComfyUI install. Required to enable direct model-directory placement of model-file uploads; without it, model uploads are rejected (workflow-input uploads still work).
--max-upload-mb 100 Max single-request upload size, in MB.
--allow-insecure-bind false Permit binding a non-loopback --host without a --token. Unsafe — exposes an unauthenticated proxy to the network.
--state-dir (unset) Opt-in proxy-layer SQLite for job records, idempotency keys, the asset index, and the output-id signing secret (separate from ComfyUI's asset catalog). See docs/batch-workloads.md.
--enable-cors-header (unset) Allow a browser Origin to call this proxy (repeatable). Explicit origins only — * is refused. See Browser access.

Browser access (hosted origin → local proxy)

comfy-api-proxy --enable-cors-header https://your-app.example --token "$LOCAL_PROXY_TOKEN"

Enables CORS preflight, Authorization / Idempotency-Key, exposes Retry-After / range headers, and makes GET /api/v2/health readable from the allowlisted Origin. @comfyorg/sdk stays Node-only for v1 — browser apps should use fetch against /api/v2/*. Details: docs/browser-access.md.

SDKs and the API contract

Any real integration — and demo/run_demo.py — uses the same client SDKs Comfy Cloud users use, just pointed at this proxy's --host:--port instead of api.comfy.org:

Project Language Package
comfy-python-sdk Python comfy-sdk
comfy-typescript-sdk TypeScript @comfyorg/sdk

spec/openapi.yaml in this repo is a synced, filtered copy of that same Comfy API v2 contract — see spec/README.md for what "filtered" means, and Development below for how it's kept in sync.

Development

pip install -e ".[dev]"
ruff check .            # lint
ruff format --check .   # format check
mypy src/comfy_api_proxy   # type-check (lenient - see pyproject.toml)
pytest -v                # unit + end-to-end tests
python3 scripts/generate_models.py && git diff --exit-code src/comfy_api_proxy/schemas/_generated.py
                          # spec-drift check (also runs in CI)

These are exactly the checks CI runs (.github/workflows/ci.yml), each as its own job — lint/format, type-check, spec-drift, and test — with the test job running across Python 3.10, 3.11, and 3.12.

tests/test_smoke.py and tests/test_endpoints.py start the fake ComfyUI stand-in and the real proxy as subprocesses and drive both over plain HTTP (standard library only — no SDK, no third-party client, no dependency on another repo's credentials), covering upload → core/ASSET-reference → run → download, cancel, from-hash/by-hash, the SSE stream (including its concurrent-stream cap), and the model-placement security guards.

Keeping spec/openapi.yaml in sync

The vendored spec is generated, one-way (upstream → here), and never hand-edited. scripts/sync-spec.sh fetches the canonical spec from wherever it's passed (a local path or a URL), runs it through scripts/filter_openapi.py — which strips anything internal-only before a byte lands in this public repo — and writes the result to spec/openapi.yaml plus a spec/VERSION provenance pin. scripts/generate_models.py then regenerates the pydantic models (src/comfy_api_proxy/schemas/_generated.py) that tests/test_schema_conformance.py validates real handler responses against — those models are used only in tests, never on the request-handling hot path. CI's spec-drift job re-runs the generator and fails the build if the checked-in models don't match, so a spec sync without a regeneration gets caught immediately instead of drifting silently. See spec/README.md and docs/sync-workflow.md for the full design.

Scope

Implemented: submit (with core/ASSET resolution), poll, cancel, live SSE events, asset upload/download, from-hash/by-hash dedup, guarded model-directory placement, optional --state-dir persistence, caller metadata / advisory priority, GET /jobs, outputs_reused, POST /assets/from-path, and GET /health.

Batch topology, durability, priority, and cancel: docs/batch-workloads.md.

Known limitations:

  • --state-dir is opt-in. Proxy records (jobs, idempotency, asset index, signing secret) survive restarts. This is not ComfyUI's asset-catalog SQLite (--database-url / --enable-assets); history and output files remain ComfyUI's responsibility (404 output_unavailable when bytes are gone).
  • One proxy ↔ one ComfyUI. Multi-backend routing is out of scope.
  • priority is advisory only — never mapped to ComfyUI front: true.
  • Proxy-local extensions are not yet in the synced Cloud OpenAPI (spec/openapi.yaml is one-way from upstream; not hand-edited here).
  • Uploads still buffer; from-path avoids the copy when the file already lives under --comfyui-base-dir.

Metadata

Release files for comfy-api-proxy 0.1.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for comfy-api-proxy 0.1.4
File Size Uploaded
comfy_api_proxy-0.1.4.tar.gz 102.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for comfy-api-proxy 0.1.4
File Interpreter ABI Platform
comfy_api_proxy-0.1.4-py3-none-any.whl Python 3 none any Details

Total release size: 160.4 kB

Release files / comfy_api_proxy-0.1.4.tar.gz

Download URL comfy_api_proxy-0.1.4.tar.gz
Size 102.9 kB
Tags Source
SHA-256 checksum
How to use checksums
d04318d3c75703066f555bdd988158a50172ca9c8152a507b9fe7f0f4c5556d2
BLAKE2b-256 checksum
How to use checksums
12e2b8612bc908783e7876d3e1debb831873f7d2588a81702838acb215098732
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.12.9

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 5, 2026.

Transparency log

Release files / comfy_api_proxy-0.1.4-py3-none-any.whl

Download URL comfy_api_proxy-0.1.4-py3-none-any.whl
Size 57.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a19bf969f0ca437c9cf351345cdcf6470fe1c9eaeadcddd9281ae20baa41b4f6
BLAKE2b-256 checksum
How to use checksums
d557ef0db83df36406c772721de37d48c71c0d04ab23b72a4a1c3eefad748099
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.12.9

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 5, 2026.

Transparency log

Release history Release notifications | RSS feed

0.1.6

2 release files

0.1.5

2 release files

This release

0.1.4 This release

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page