UniAPI Model Call
Call any OpenAI-compatible image API straight from your graph
- image1
- image2
- image3
- image4
- image
- image_url
- task_id
- response
One node, one job: UniAPI Model Call is a gateway that lets a ComfyUI workflow call any third-party image service that speaks the OpenAI Images API format - and hands the result back as a real IMAGE tensor you can preview, save, or pipe into more nodes.
Most of your graph is local - checkpoints, samplers, VAE - but every so often the best tool for a job lives in the cloud. Maybe it's a proprietary model you don't have weights for, a hosted model that beats what your GPU can fit, or you just don't want to burn the VRAM on a quick generation. This node is the "bring the API in" bridge: no hand-rolled curl script and copy-pasting images back in, just a node in the graph.
How it works
Under the hood it's two HTTP calls dressed up as a node. In text2img mode it POSTs your prompt to {base_url}/v1/images/generations with ?async=true; in img2img mode it POSTs multipart form data to {base_url}/v1/images/edits, converting up to four IMAGE tensors to PNG along the way. If the service is async and returns a task_id, it polls {base_url}/v1/images/tasks/{task_id} every 10 seconds, up to 60 attempts - ten minutes - with a native ComfyUI progress bar. If the service answers synchronously with a data array, it just decodes that: b64_json gets base64-decoded, url responses get downloaded, and everything gets batched into one tensor.
The resume trick is the genuinely nice touch. Because the poller is synchronous, a long async job can outlast your patience or your internet. The node emits task_id; feed it back into the task_id input and it skips the submit entirely and just queries that task. Disconnected mid-generation? Plug the ID back in and carry on.
The inputs that matter
Only four are required:
- api_key - the bearer token for the service. It lives in the workflow, so treat it like a secret (more below).
- base_url - the API root. Give it
https://your-provider.com, not the full/v1/images/generationspath; the node appends that itself. - prompt - your text prompt, multiline.
- mode -
text2imgorimg2img.
The rest are optional. model is the string your provider expects, like gpt-image-1; with many services you can leave it blank and get the default. n generates 1–4 images and batches them into one tensor. seed has a catch: 0 means "random" and is omitted from the request - anything above zero gets sent through. size, quality, background, output_format, and moderation pass through as-is, so they only do something if your provider honors them. response_format defaults to b64_json; flip it to url if your API only hands back URLs (the node still downloads them into a tensor either way).
Outputs
- image - the
IMAGEtensor, batched whenn > 1. Wire it into a preview or Save Image node. - image_url - the first image's URL (empty for base64 responses).
- task_id - save this string to resume async jobs later.
- response - the full JSON as text, with status, model, and image count. Your debugging friend.
Install
No model downloads, no dependency hell - the README is explicit that it needs only torch, Pillow, and requests, all already in ComfyUI's base environment. Two routes:
cd ComfyUI/custom_nodes
git clone https://github.com/xiaojuzi2024/ComfyUI-UniAPI.git
then restart ComfyUI. Or search "ComfyUI-UniAPI" in ComfyUI Manager and install from there.
Where people get burned
- It's a network node. Your prompt, your api_key, and in
img2imgmode your actual images leave your machine for whoever runs the API. ComfyUI embeds workflows into output PNGs by default, so a shared workflow means a shared key - use a scoped key you can revoke. - A blank white image is the failure signature. The node doesn't throw - it returns a white 1024×1024 placeholder and stuffs the error into the
responseoutput. Read that before tearing your hair out. A red placeholder means the task itself failed; a small yellow one means the 10-minute poll timed out (resume withtask_id). base_urlformat. Appending the full endpoint path instead of the root breaks every request.- Not every "OpenAI-compatible" provider is equal. Some skip the async tasks endpoint, some never implemented
/v1/images/editsat all.img2imgneeds an edits endpoint that actually exists - check the provider's docs first. - Long async jobs hold the queue. The poller is synchronous, so a slow provider ties up a queue slot for up to ten minutes. Fine on a personal box, annoying on shared setups.
The pack is young and quiet - essentially no community footprint yet - so the README and the small, readable source are the docs.
Inputs (18)
| Name | Type | Default | Description |
|---|---|---|---|
| api_key | STRING | — | |
| base_url | STRING | — | |
| prompt | STRING | — | |
| mode | COMBO | text2img | 2 options: text2img, img2img |
| modelopt | STRING | — | |
| image1opt | IMAGE | — | |
| image2opt | IMAGE | — | |
| image3opt | IMAGE | — | |
| image4opt | IMAGE | — | |
| qualityopt | COMBO | auto | 4 options: auto, high, medium, low |
| sizeopt | COMBO | auto | 4 options: auto, 1024x1024, 1536x1024, 1024x1536 |
| backgroundopt | COMBO | auto | 3 options: auto, transparent, opaque |
| output_formatopt | COMBO | png | 3 options: png, jpeg, webp |
| moderationopt | COMBO | auto | 2 options: auto, low |
| nopt | INT | 11–4 | — |
| task_idopt | STRING | — | |
| response_formatopt | COMBO | b64_json | 2 options: url, b64_json |
| seedopt | INT | 00–2147483647 | — |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| image_url | STRING | — |
| task_id | STRING | — |
| response | STRING | — |