API Chat (Environment Key) · Tank
Same node, key stays on the server
- image
- video_frames
- video
- response
- usage
- stats
- reasoning
- raw_response
API Chat (Environment Key) · Tank is the same node as API Chat (Direct Key) with one input swapped and one security rule bolted on. Instead of pasting a key into a node field, you name an environment variable that the ComfyUI server process reads at request time. The workflow then contains OPENAI_API_KEY - a string of letters, not a secret. Share it, screenshot it, upload the JSON anywhere.
The rule that comes with it is the interesting part, and it's the reason this node is worth choosing even when pasting a key would be easier. Environment-key mode only talks to a host on an administrator-controlled allow-list:
- The default list is
api.openai.complus loopback (localhost,127.0.0.1,::1). - HTTPS is required everywhere except loopback; the node refuses to send a key to a plain-HTTP address that isn't local.
- URLs carrying credentials, a query string or a fragment are rejected outright.
Read that as what it is: an anti-exfiltration design. A shared workflow can't quietly reroute your server's API key to somebody else's endpoint, because the workflow isn't allowed to change the process's allow-list. That's a genuinely thoughtful move in a category where the KB's standing warning is that an API node is a credential plus arbitrary Python, and the ecosystem has already shipped a vision node that stole credentials.
Set it up
Export the variable before ComfyUI starts, in the same environment that launches it:
export OPENAI_API_KEY='sk-...'
# then start ComfyUI from this same shell
python main.py
In the node, put OPENAI_API_KEY in api_key_env - the variable's name, not its value. That's the single most common mistake with this node, and it fails as an authentication error rather than a configuration complaint.
To reach a provider that isn't OpenAI, allow the exact host on the server side and restart:
export COMFYUI_API_ALLOWED_HOSTS='api.openai.com,api.deepseek.com,api.example.com'
Because loopback is allowed by default, this node is also the nicer way to talk to a local server - LM Studio at http://127.0.0.1:1234/v1, or a llama.cpp --server instance - without hardcoding anything into the workflow.
Everything else is shared with its sibling. base_url (no /chat/completions), the exact provider model ID, system_prompt, prompt, max_tokens and temperature where 0 means "omit this parameter," seed, and the seven-value thinking_mode where auto leaves the provider untouched. The optional media inputs are the same too: image (every frame in a batch goes in one request), video_frames sampled by max_video_frames, video plus video_transport (frames is the broadly supported choice, video_url only if the provider takes native video). Outputs are response, usage, stats, reasoning and raw_response; the node is a terminal, it returns text directly, and it never connects to the local loader.
Install
ComfyUI Manager → search Tank (registry package ID qwen38-vl), or clone and restart:
cd ComfyUI/custom_nodes
git clone https://github.com/xzbdqian10nian/ComfyUI-TankNodes.git
Unlike the two local nodes in this pack, nothing here needs llama-cpp-python, a CUDA build or a GGUF file - the API nodes run standalone, and the pack's dependency list is deliberately tiny (the OpenAI SDK if you already have it, otherwise ComfyUI's own aiohttp). You can install this node, set your key, and never touch the local half of the pack at all.
Where people get burned
Environment-key mode rejected the unallowlisted API host - you're pointing at a provider the server doesn't trust yet. Add the exact hostname to COMFYUI_API_ALLOWED_HOSTS and restart ComfyUI; changing the variable while it's running does nothing, because this setting is deliberately read as a process property.
Environment-key mode will not send a key to a non-loopback HTTP address - you typed http:// for a remote host. Use HTTPS.
Key not found, or the same 401 you'd get with no key at all - the variable exists in your terminal but not in the process that's running ComfyUI (common with systemd units, Docker containers, LaunchAgents, or a desktop launcher that never saw your shell profile). Put it in the service definition or the container's environment, not just your .bashrc.
It worked yesterday. Loopback aside, this node will not fall back to a direct key if the environment variable is missing - that's the point. Nothing about the failure is silent about which mode you're in.
For anything else - wrong model IDs, /v1 mistakes, surprise token bills from image batches, interrupt behaviour - the Direct Key article covers it, since the two nodes share one implementation and differ only in where the key comes from.
Inputs (14)
| Name | Type | Default | Description |
|---|---|---|---|
| base_url | STRING | https://api.openai.com/v1 | OpenAI-compatible API base URL; do not include /chat/completions. The environment-key node only permits the administrator's host allow-list. |
| model | STRING | Exact model ID exposed by the API provider. | |
| api_key_env | STRING | OPENAI_API_KEY | Environment variable name containing the API key. Its request host must be administrator-allow-listed. |
| system_prompt | STRING | Optional persona and system-level instruction; leave blank for the provider default. | |
| prompt | STRING | User instruction sent to the API, together with any attached image or video input. | |
| max_tokens | INT | 00–32768 | Maximum output token count. 0 omits this parameter and uses the provider default. |
| temperature | FLOAT | 0.000–2 | Sampling temperature. 0 means do not send this parameter; use the provider default. |
| seed | INT | 10–4294967295 | Seed sent for sampling when the provider supports it; providers may ignore it. |
| thinking_mode | COMBO | auto | Reasoning control: auto leaves the provider unchanged, off disables reasoning when supported, and low/medium/high/xhigh/max are the five unified effort tiers. Unsupported tiers may be rejected or mapped by the provider. |
| max_video_framesopt | INT | 81–64 | Sample at most this many frames from video or video frames. Every image in the image input is still sent. |
| video_transportopt | COMBO | frames | Choose sampled image frames, native video_url, or automatic native-video fallback. The provider must support the selected format. |
| imageopt | IMAGE | One still image or an IMAGE batch; every item is sent in the same API request. | |
| video_framesopt | IMAGE | Decoded video as an IMAGE batch; frames are sampled according to max_video_frames. | |
| videoopt | VIDEO | ComfyUI VIDEO input; use frames for broad support or video_url only when the provider supports native video. |
Outputs (5)
| Name | Type | Description |
|---|---|---|
| response | STRING | Final API response text. |
| usage | STRING | Usage JSON returned by the provider, when available. |
| stats | STRING | Request timing and backend information. |
| reasoning | STRING | Reasoning text when the provider returns it; otherwise empty. |
| raw_response | STRING | Original assistant content or complete tool-call message. |