ComfyUI Node

openrouter_node

One node, one API key, and every hosted model OpenRouter sells — inside your graph

By gabe-init·Created about a year ago·Updated 22 days ago· 84
openrouter_node
  • pdf_data
  • audio_data
  • Output
  • image
  • Stats
  • Credits
  • video
◄api_key►
◄system_promptYou are a helpful assistant.►
◄user_message_boxHello, how are you?►
◄model▾►
◄web_searchfalse►
◄cheapesttrue►
◄fastestfalse►
◄aspect_ratioauto►
◄image_resolutionauto►
◄reasoning_effortauto►
◄seed0►
◄temperature1.00►
◄pdf_engineauto►
◄chat_modefalse►
◄request_timeout120►
◄user_message_input—►
◄request_typechat►
◄service_tierauto►
◄image_qualityauto►
◄image_backgroundauto►
◄video_modetext_to_video►
◄video_durationauto►
◄video_resolutionauto►
◄video_generate_audiofalse►
◄video_wait_timeout900►
◄video_job_id►

This is the node that does not run a model. It is an HTTP client with your key in it - you fill in a prompt, it phones OpenRouter, and whatever comes back (text, an image, or a video) drops into the workflow like any other node output. The pack registers the single class as both OpenRouterNode and openrouter_node.

Why you'd want it: an LLM in the graph is routine by now, and OpenRouter is the cheap way to get one without a second server. The usual jobs are rewriting a rough idea into a structured prompt, captioning images for a LoRA set, and reaching closed models you were never allowed to download - Nano Banana and friends - without leaving ComfyUI.

How it works

Under the hood it's requests against openrouter.ai/api/v1/chat/completions, plus /images and /videos for the other request types. Three catalogs (/models, /images/models, /videos/models) are fetched on a background thread and cached, so the model dropdown fills itself without freezing the canvas.

The routing is done by appending suffixes to the model ID:

  • web_search appends :online
  • cheapest (on by default) appends :floor
  • fastest appends :nitro

Look at the order in the source, because it's a trap: if :online isn't already on the ID, cheapest is checked first and fastest only applies when cheapest is off. Turn both on and you quietly get floor routing. Pick one.

Your key resolves as UI field, then LLM_KEY env var, then openrouter_api_key.json in the node directory. The node clears the key from saved widget values, but that's damage control, not safety.

The inputs that matter

Most of the required list you can ignore forever. For chat you set four things: api_key (blank if you're using the env var or JSON file), system_prompt (be specific - it's a chat model), user_message_box (or wire in user_message_input instead), and model.

Then the knobs worth knowing: temperature (0–2, slider), reasoning_effort (auto sends no override, none disables thinking, minimal through xhigh request more where supported), chat_mode for multi-turn context, and request_timeout (120s default).

request_type changes everything else. On image you also get aspect_ratio, image_resolution (1K/2K/4K), image_quality, image_background and seed, validated against what the chosen model advertises - an unsupported explicit setting fails before it costs you anything. On video you get video_mode (text_to_video, first_frame, first_last_frame, reference_images), video_duration, video_resolution, video_generate_audio, video_wait_timeout and video_job_id.

Media inputs are optional and self-revealing: image_1 is always visible, image_2 through image_10 appear as you connect images, and pdf_data with pdf_engine (auto, mistral-ocr, pdf-text) handles documents. audio_data takes one Load Audio clip, chat mode only.

The outputs

Output is the text response - connect a ShowText node to read it. image is an IMAGE tensor when the response contains a generated image, and an empty 1×1 placeholder when it doesn't. Stats carries TPS, token counts, temperature, the resolved model, and cost or tier when the API reports them. Credits is your remaining balance ("Remaining: $9.792"). video goes to Save Video - that one needs ComfyUI 0.3.31+ with PyAV, though chat and image keep working on older installs.

Install

ComfyUI Manager, search the pack title "ComfyUI OpenRouter Node" (registry ID gabe-init/ComfyUI-Openrouter_node). Or by hand:

cd ComfyUI/custom_nodes
git clone https://github.com/gabe-init/ComfyUI-Openrouter_node
pip install -r requirements.txt   # tiktoken, Pillow; requests ships with ComfyUI

Restart ComfyUI, drop in an API key from openrouter.ai, and run. Put it in a file rather than the widget if you ever share workflows:

echo '{"api_key": "sk-or-..."}' > openrouter_api_key.json
chmod 600 openrouter_api_key.json

What goes wrong

The failure mode to internalise: chat and image errors don't raise. They land as text in the Output slot - Error: API Request Error: ... | Status: 401, with "Stats N/A due to error". A run that "succeeded" and produced a paragraph of error text is a normal symptom; read the box. Video deliberately raises instead, so Save Video doesn't cache a failure as a result.

  • Model list empty or stale: catalog discovery is public and needs no key. Hit Refresh Models; a failed refresh keeps the last good snapshot rather than blanking the list.
  • "Cannot verify the selected chat model's media capabilities": you sent an image, audio, or image settings while the catalog was unavailable. Refresh and rerun.
  • "The selected chat model does not support image input": you're on a text-only model. Plenty of cheap ones are.
  • Video job died mid-poll: stopping ComfyUI does not cancel the remote job. The last accepted ID is stashed in workflow properties - click Resume Last Video to fill video_job_id and run again. Clear it before starting a fresh generation.
  • Rate limits: on free :free models, the 429 in Output is OpenRouter's per-day cap, not a bug in the node.
  • Chat context lost between runs: sessions live in chats/ in the node directory and expire after an hour of inactivity. There's a helper script:
python manage_chats.py list
python manage_chats.py view session_20261206_143022_hello_how_are_you_today
python manage_chats.py clean -d 30

One last thing, the honest one: this is the node shape that has already been weaponised - it holds a credential and calls the network by design, so misbehaviour doesn't look like misbehaviour. Prefer the official ComfyUI API nodes where they cover you, and read a fresh pack before its first run.

CategoryLLM

Inputs (28)

NameTypeDefaultDescription
api_keySTRING—
system_promptSTRINGYou are a helpful assistant.—
user_message_boxSTRINGHello, how are you?—
modelCOMBO1 options: openai/gpt-4o
web_searchBOOLEANfalse—
cheapestBOOLEANtrue—
fastestBOOLEANfalse—
aspect_ratioCOMBOauto15 options: auto, 1:1 (1024x1024), 2:3 (832x1248), 3:2 (1248x832), 3:4 (864x1184), 4:3 (1184x864), +9
image_resolutionCOMBOauto5 options: auto, 0.5K, 1K, 2K, 4K
reasoning_effortCOMBOauto7 options: auto, none, minimal, low, medium, high, +1
seedINT00–18446744073709550000—
temperatureFLOAT1.000–2—
pdf_engineCOMBOauto3 options: auto, mistral-ocr, pdf-text
chat_modeBOOLEANfalse—
request_timeoutINT1201–3600—
pdf_dataopt*—
user_message_inputoptSTRING—
audio_dataoptAUDIO—
request_typeoptCOMBOchat3 options: chat, image, video
service_tieroptCOMBOauto5 options: auto, default, flex, priority, ultrafast
image_qualityoptCOMBOauto4 options: auto, low, medium, high
image_backgroundoptCOMBOauto3 options: auto, opaque, transparent
video_modeoptCOMBOtext_to_video4 options: text_to_video, first_frame, first_last_frame, reference_images
video_durationoptSTRINGauto—
video_resolutionoptSTRINGauto—
video_generate_audiooptBOOLEANfalse—
video_wait_timeoutoptINT9001–3600—
video_job_idoptSTRING—

Outputs (5)

NameTypeDescription
OutputSTRING—
imageIMAGE—
StatsSTRING—
CreditsSTRING—
videoVIDEO—