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 namePyComfy/
├── __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