A Python scripting API and embedded script editor for ComfyUI.
Write Python scripts that build, modify, and execute ComfyUI workflows — either interactively from the built-in editor panel or headlessly from external code.
- Full graph API — create, connect, clone, copy/paste, and delete nodes programmatically
- Built-in script editor — tabbed editor panel inside the ComfyUI web UI with syntax highlighting and a console
- Console history — all script executions, output, and errors are logged in the console panel with filterable streams (
gui,script,output,error) and live WebSocket updates - Workflow management — open, save, rename, and switch between workflow tabs
- Subgraphs and groups — create and navigate nested subgraphs, organize nodes with visual groups
- Batch execution — queue prompts, vary parameters across runs, wait for completion
- Reusable apps — register Python functions as mini-tools with typed parameters, accessible from the Apps panel or via
run_app() - Headless mode — run workflows from standalone Python scripts without a browser
- 19 ready-to-run examples covering every API surface
Clone or symlink this repository into your ComfyUI custom_nodes directory:
cd ComfyUI/custom_nodes
git clone https://github.com/danielefederico/PyComfy.gitRestart ComfyUI. The script editor panel and API routes register automatically.
- ComfyUI (provides the runtime and
aiohttp) - Python 3.10+
No additional pip dependencies are needed at runtime.
Open the Script Editor panel in the ComfyUI sidebar and try:
import pycomfyapi # Create a node and configure it sampler = pycomfyapi.create_node("KSampler") sampler.name = "My Sampler" sampler.set("steps", 25) sampler.set("cfg", 7.0) # Inspect it print(sampler.type, sampler.id) print([p.name for p in sampler.inputs]) # Queue the workflow pycomfyapi.queue()
import pycomfyapi pycomfyapi.clear() ckpt = pycomfyapi.create_node("CheckpointLoaderSimple") ckpt.set("ckpt_name", "dreamshaper_8.safetensors") pos = pycomfyapi.create_node("CLIPTextEncode") pos.set("text", "a photo of an astronaut riding a horse") neg = pycomfyapi.create_node("CLIPTextEncode") neg.set("text", "blurry, low quality") latent = pycomfyapi.create_node("EmptyLatentImage") latent.set("width", 512) latent.set("height", 512) sampler = pycomfyapi.create_node("KSampler") sampler.set("steps", 25) vae = pycomfyapi.create_node("VAEDecode") save = pycomfyapi.create_node("SaveImage") # Connect the pipeline sampler.input("model").connect_to(ckpt.output("MODEL")) pos.input("clip").connect_to(ckpt.output("CLIP")) neg.input("clip").connect_to(ckpt.output("CLIP")) sampler.input("positive").connect_to(pos.output("CONDITIONING")) sampler.input("negative").connect_to(neg.output("CONDITIONING")) sampler.input("latent_image").connect_to(latent.output("LATENT")) vae.input("samples").connect_to(sampler.output("LATENT")) vae.input("vae").connect_to(ckpt.output("VAE")) save.input("images").connect_to(vae.output("IMAGE")) pycomfyapi.queue()
import pycomfyapi pycomfyapi.set_server(port=8188) sampler = pycomfyapi.create_node("KSampler") # ... build your graph ... pycomfyapi.queue(wait=True) pycomfyapi.save_graph_to_file("output.json")
The module-level functions use a shared singleton session. You can access it directly for lower-level control:
import pycomfyapi session = pycomfyapi.get_session() sampler = session.create_node("KSampler") sampler.set("steps", 30) clip = session.create_node("CLIPTextEncode") clip.set("text", "a sunset over mountains") clip.output("CONDITIONING").connect_to(sampler.input("positive")) session.queue(wait=True) session.save_graph_to_file("my_workflow.json")
import pycomfyapi @pycomfyapi.app( name="Quick Upscale", params={"upscale_model": str, "save_prefix": str}, description="Upscale the output of the current workflow.", ) def quick_upscale(upscale_model="RealESRGAN_x4plus.pth", save_prefix="upscaled"): loader = pycomfyapi.create_node("UpscaleModelLoader") loader.set("model_name", upscale_model) # ... build the rest of the pipeline ... pycomfyapi.queue(wait=True) # Or run it programmatically pycomfyapi.run_app("Quick Upscale", save_prefix="my_output")
The examples/ directory contains 19 scripts covering every part of the API:
| # | Script | Description |
|---|---|---|
| 01 | hello_world |
Create a node, set properties, inspect it |
| 02 | txt2img_basic |
Build a full txt2img pipeline from scratch |
| 03 | batch_render |
Vary seeds across multiple renders |
| 04 | query_graph |
Inspect nodes, connections, and widget values |
| 05 | register_app |
Register a reusable mini-app |
| 06 | pipeline_integration |
Integrate with external Python pipelines |
| 07 | node_properties |
Read and write node properties (color, size, mode) |
| 08 | selection |
Select, deselect, and query selected nodes |
| 09 | clone_and_clipboard |
Clone nodes, copy/cut/paste |
| 10 | connections |
Connect and disconnect slots |
| 11 | node_types_info |
Query available node types and their metadata |
| 12 | graph_io |
Export and import workflows as JSON |
| 13 | workflow_management |
Create, switch, rename, and close workflow tabs |
| 14 | subgraphs |
Create and navigate nested subgraphs |
| 15 | node_lifecycle |
Node creation, muting, bypassing, and deletion |
| 16 | app_management |
Register, list, and run apps |
| 17 | execution_control |
Queue with wait, interrupt execution |
| 18 | tabs_and_containers |
Work with multiple workflow tabs |
| 19 | groups |
Create and manage visual group boxes |
import pycomfyapi # Nodes pycomfyapi.create_node(class_type, **defaults) -> Node pycomfyapi.delete_node(node_or_id) pycomfyapi.get_node_by_id(node_id) -> Node pycomfyapi.get_node_by_name(name) -> Node pycomfyapi.get_nodes() -> list[Node] pycomfyapi.clone_node(node_or_id) -> Node pycomfyapi.clear() # Connections pycomfyapi.connect(output_slot, input_slot) # Selection pycomfyapi.select(node_or_ids) pycomfyapi.deselect(node_or_ids) pycomfyapi.select_all() pycomfyapi.get_selected_nodes() -> list[Node] # Clipboard pycomfyapi.copy_nodes(ids) pycomfyapi.cut_nodes(ids) pycomfyapi.paste_nodes() # Execution pycomfyapi.queue(wait=False) pycomfyapi.interrupt() # Graph I/O pycomfyapi.get_graph() -> dict pycomfyapi.set_graph(json_data) pycomfyapi.load_graph_from_file(filepath) pycomfyapi.save_graph_to_file(filepath) # Workflows pycomfyapi.create_workflow(name) -> Workflow pycomfyapi.get_workflows() -> list[Workflow] pycomfyapi.get_active_workflow() -> Workflow pycomfyapi.set_active_workflow(workflow_or_name) pycomfyapi.save_workflow(workflow) pycomfyapi.save_workflow_as(name, workflow) pycomfyapi.close_workflow(workflow_id) # Subgraphs pycomfyapi.create_subgraph(name, nodes) -> Subgraph pycomfyapi.get_subgraphs() -> list[Subgraph] pycomfyapi.open_subgraph(subgraph) # Groups pycomfyapi.create_group(name, x, y, width, height) -> Group pycomfyapi.get_groups() -> list[Group] # Node info pycomfyapi.node_types() -> list[str] pycomfyapi.node_info(class_type) -> dict # Server pycomfyapi.set_server(host, port)
node.id # int — ComfyUI node ID node.type # str — node class type node.name # str — display title (read/write) node.x, node.y # float — canvas position node.color # str — node color node.inputs # list[Slot] node.outputs # list[Slot] node.widget_names # list[str] node.get(widget) # get a widget value node.set(widget, v) # set a widget value node.move(x, y) # reposition on canvas node.input(name) # get input slot by name node.output(name) # get output slot by name
PyComfy/
├── __init__.py # ComfyUI extension entry point
├── pycomfyapi/ # Python API package
│ ├── __init__.py # Public API surface
│ ├── constants.py # Enums (EntityType, NodeState, SlotDirection)
│ ├── entities/ # Graph entities (Node, Slot, Workflow, Subgraph, Group)
│ ├── serialization/ # LiteGraph JSON serialization
│ └── session/ # Session management and communication
├── server/ # aiohttp route handlers
├── web/ # Script editor frontend (JS/CSS)
├── examples/ # 19 example scripts
└── pyproject.toml
# Install dev dependencies pip install -e ".[dev]" # Run the test suite pytest

