API Image
Run a Model You Can't Download, Inside Your Graph
- image
- mask
- images
- response_json
- metadata_json
Why you'd want it
There's a category of model you cannot download at any VRAM - Nano Banana, GPT Image, Seedream, the closed frontier. No weights, no local option, the API is the only door, and API Image is a generic keyhole for it: an HTTP call to a model you configured, sitting where a sampler normally would, returning what comes back as an ordinary IMAGE you can preview, save, upscale or feed anywhere.
It's also the honest answer to "my card can't do it" - the same node calling a hosted FLUX or Qwen-Image-Edit is renting a GPU per call instead of buying one. Keep those moves separate: one is a capability you'll never have locally, the other is a convenience better hardware would solve. And like its sibling nodes, this pack has almost no community trail yet, so you're early; in exchange you get a short, MIT, readable source - not a small thing for the category that holds credentials by design.
How it works
Two operations, two genuinely different requests. image is text-to-image: the stock Images · Generate template POSTs JSON to /images/generations with model, prompt, size and n. image_edit is the multipart path: Images · Edit POSTs to /images/edits, uploading your input as image[] and, if you wired one, the mask as a mask file. operation picks a template binding - no binding, and you get unsupported_operation.
Responses come back through a small JSONPath-ish selector like $.data[*] plus image_value = auto, which understands a bare URL string, b64_json, url or raw base64 - and providers that stream image/* bytes need no mapping at all. Returned URLs get fetched, redirects followed, but your credential is deliberately not forwarded to a different host, so an image CDN never gets handed your key.
Then the bytes become IMAGE tensors: RGB, EXIF-rotated, one per returned image. Because they arrive as a list, each keeps its original size - nothing is resized to match anything. Perfect for a 1536×1024 next to a 1024×1536; a problem if you meant to stack them into a video node.
The call runs interruptibly, so Cancel works while you wait, and paid submissions are never retried. Slow providers should use a template with the optional async polling block: a task ID polled every 2s (600s default timeout), progress reported on the node, and an optional remote cancellation that cancels the wait but can't guarantee a refund.
The inputs that matter
provider_id and model_id are set by the node's own model picker - leave them and you'll get an error rather than a rogue billed call.
operation-imageorimage_edit. Pick the edit path and you must wireimage, or you getmissing_image.prompt- multiline, with a "Connect input" socket so another node can drive it.parameters- JSON, default{}, connectable.sizeandncome from the stock templates, but only if the template references them; unreferenced keys are dropped silently. API Parameter chains are the tidy way to build this.image/mask- the edit image, and a mask with a real convention trap. ComfyUI paints1as the area to edit, while image-edit APIs read the transparent region as the edit region, so the node inverts your mask into PNG alpha. The mask only uploads if the template has a file entry withsource: mask- the stock edit template does, a hand-rolled one may not, and then your masking does nothing.cache_mode(reuse/refresh) andrequest_nonce-reusere-runs an unchanged graph from cache instead of billing you again. Change the nonce for a fresh result;refreshcalls every run, and also poisons ComfyUI's cache for every node behind it. A nonce is not a seed.
Outputs: images - an IMAGE list, straight into PreviewImage, SaveImage or an upscaler. Ask for n: 2 and you get two list items; since sizes are preserved rather than unified, nodes expecting a stack will complain. Also response_json (raw response, secrets and base64 stripped) and metadata_json (request_id, model, duration_ms, and usage if the template maps one) - the dull output that pays for itself the first time you wonder what a session cost.
Install
Manager search comfyui-custom-api, or the git route with ComfyUI's own interpreter:
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
aiohttp, Pillow, numpy. No weights, no download step. Restart, refresh the browser. Then configure a provider once from the top-bar API panel: Base URL, auth, Save & fetch models, and bind Images · Generate / Images · Edit to the models that support them. Discovery lists IDs and never guesses capabilities - the number-one reason a fetched model doesn't show up in the picker.
Where people get burned
unsupported_operation - no template bound for the image operation on that model.
Nothing happens, twice, and you were only billed once. That's reuse doing its job.
Timeout at 120 seconds. Slow image models blow past the provider default; raise it in the provider's advanced network settings or move to an async provider template. The shipped Async image template is labelled "customize before use" - its paths and status strings are placeholders, not a working provider.
The download fails even though the API answered. If a provider hands back a URL on a different host that also wants the auth header, the node won't send your key there. That's a privacy feature working; a failed fetch is the cost. Prefer base64 or signed URLs.
Cost, the real one. Every run is metered and you can't see the number until the invoice. Use reuse, watch metadata_json, and remember your prompt and reference images leave the machine by design - whatever the provider's filter refuses, your graph refuses too.
Inputs (9)
| Name | Type | Default | Description |
|---|---|---|---|
| provider_id | STRING | Select a provider using the model picker. | |
| model_id | STRING | Stable model configuration ID. | |
| operation | COMBO | 2 options: image, image_edit | |
| prompt | STRING | — | |
| parameters | STRING | {} | JSON parameters; can also be connected from another node. |
| cache_mode | COMBO | reuse | 2 options: reuse, refresh |
| request_nonce | INT | 00–2147483647 | Change this number to make a new API request. |
| imageopt | IMAGE | — | |
| maskopt | MASK | — |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| images | IMAGE | — |
| response_json | STRING | — |
| metadata_json | STRING | — |