Nodes/ComfyUI-custom-api/API Text / Vision
ComfyUI Node

API Text / Vision

Put a Cloud LLM in Your Graph (It Isn't a Model)

By Einzieg·Created a day ago·Updated a day ago· 0
API Text / Vision
  • image
  • text
  • response_json
  • metadata_json
provider_id
model_id
operation
prompt
parameters{}
cache_modereuse
request_nonce0
system

What it is, and why you'd wire one up

First thing to internalise: there is no model behind this node. Nothing loads into VRAM, nothing downloads. API Text / Vision packages your prompt - plus an image, if you wire one - into an HTTP request, sends it to a provider you configured earlier, and hands the reply back as a plain STRING. The key stays on the ComfyUI server, not in the workflow.

That's the point of it. The three jobs an LLM-in-the-graph actually does all sit upstream of the sampler: rewriting a rough idea into a structured prompt for an LLM-encoded base like Z-Image or Flux, captioning an image to seed an img2img pass or a LoRA dataset, and being a general text generator wired between nodes. None of it needs a GPU. If you already run an 8B abliterated model on your own card, keep it - free and uncensored per call is something no API offers. If you don't have the VRAM, this is how you borrow someone else's for one step.

How it works

The node is a shell around an engine. On run it resolves your model to a provider plus a request template bound to the operation, renders that template's placeholders ({{prompt}}, {{messages}}, {{params.temperature}}, {{image}}), attaches the credential per the provider's auth type, and fires the request over aiohttp. The reply is picked apart by a small JSONPath-ish selector - the stock Chat Completions template reads $.choices[0].message.content - and that string becomes text.

For vision, the incoming IMAGE tensor is PNG-encoded in memory and inlined as a base64 data URL in a multimodal content array. It never touches disk, and it leaves your machine the moment you run the node. That's the mechanism, not a bug.

Two behaviours worth knowing: Cancel works, because the request polls ComfyUI's interrupt check while it waits; and no paid submission is ever retried automatically (only read-only GETs are, twice). Secrets get redacted out of everything the node writes.

The inputs and outputs that matter

provider_id and model_id are filled by the node's own model picker. Leave them blank and you get an error, not a rogue billed call. operation is the real decision: text or vision.

  • prompt - the user turn. Multiline, with a "Connect input" socket so an upstream STRING node can drive it.
  • system - the system prompt, often the highest-leverage field here. "Reply with the prompt only, no preamble" belongs in it.
  • parameters - a JSON string, default {}. Only keys your template references get sent, so an invented key silently goes nowhere.
  • image - optional IMAGE. Wire it and switch operation to vision; vision with an empty socket raises missing_image.
  • cache_mode and request_nonce - the caching pair, covered below.

Outputs: text (into a CLIP text encode, a save node, or another text node), response_json (raw response, secrets and base64 stripped), and metadata_json (request_id, model, duration_ms, plus the usage block when the template maps one). The node also shows the result text on itself, since it's an output node.

Install it the real way

Manager search comfyui-custom-api, or clone and install with ComfyUI's own Python:

cd ComfyUI/custom_nodes
git clone https://github.com/Einzieg/ComfyUI-custom-api.git
/path/to/ComfyUI/venv/bin/python -m pip install -r ComfyUI-custom-api/requirements.txt

That's aiohttp, Pillow, numpy - no model files. Restart ComfyUI and hard-refresh the browser. The catalogue entry is new enough that the old default index may not list it, so git is the reliable route.

One setup step happens off-canvas, once: top-bar API (or Extensions → Model API), Base URL and auth, Save & fetch models, then bind a template to each model for the operations you want. That binding is why your model appears in the picker - discovery lists IDs, it doesn't guess capabilities. Set the request timeout (120s default) and concurrency while you're there.

Where people get burned

The model isn't in the picker. You fetched it but never bound it to text or vision.

You paid and got a chat preamble. A chat model's habits land verbatim in your conditioning. Nothing here strips role tokens or markdown for you, so system is your cleaner - and reasoning models leak their scratch-work worst.

You changed the prompt, re-ran, and nothing happened. reuse is the default, and a changed prompt alone doesn't invalidate a reused result. Bump request_nonce, or use refresh to call every run - knowing that refresh defeats ComfyUI's cache for every node behind it, so the whole graph re-executes. A nonce is not a seed.

Keys are plaintext. They sit in user/__custom_api/secrets.json under ComfyUI's system user directory, permission-protected rather than encrypted, and shared across that instance's workflows. Point COMFYUI_CUSTOM_API_DIR somewhere private or use the provider's environment-variable key source.

The trick nobody documents

Because the Base URL is free-form and auth can be No authentication, you can point a "provider" at your own machine. Run llama.cpp, Ollama, LM Studio or KoboldCpp with its OpenAI-compatible route up, set the Base URL to http://127.0.0.1:8080/v1, bind the Chat Completions template, and you get text and vision with no per-call cost and nothing leaving the box. Paths stay relative to the Base URL - /chat/completions, not a full URL.

CategoryModel API

Inputs (9)

NameTypeDefaultDescription
provider_idSTRINGSelect a provider using the model picker.
model_idSTRINGStable model configuration ID.
operationCOMBO2 options: text, vision
promptSTRING
parametersSTRING{}JSON parameters; can also be connected from another node.
cache_modeCOMBOreuse2 options: reuse, refresh
request_nonceINT00–2147483647Change this number to make a new API request.
systemSTRING
imageoptIMAGE

Outputs (3)

NameTypeDescription
textSTRING
response_jsonSTRING
metadata_jsonSTRING