jz OpenAI Image
Gpt-image edits in your own graph, billed to your own OpenAI key
- image
- mask
- images
- mask
- cost
- usage
What it's for
Local models closed most of the gap on generating an image. Editing is where they still wobble - instruction-style edits, changing the coat without touching the pose, adding a sign and getting the lettering right. That's the niche gpt-image holds, and jz OpenAI Image is what puts it in your graph without going through Comfy's prepaid Partner Nodes. Your OpenAI key, your billing, your org's rate limits, no credits in the middle.
How it works
One node, two endpoints, and which one you hit is decided by whether anything is wired into the image input.
Nothing wired: POST /images/generations with your prompt, size and quality. An image wired: POST /images/edits, in the JSON form rather than multipart - each frame becomes an image_url data reference, up to 16 of them. So a batch of reference images is one call, not sixteen.
The mask input applies to the first image and follows ComfyUI's convention (1 = the area to change), which is the inverse of what the API wants. The node handles that: it flips it, packs it as the alpha channel of a PNG, and resizes it to the first image with nearest-neighbour so the mask edges don't soften. Wire a mask with no image and it raises, because a mask needs an edit to apply to.
The guiding rule everywhere else is auto omits the field. Supported parameters vary sharply per model, and sending a default you didn't ask for is how you get a 400. Concretely: xhigh and max quality exist only on gpt-image-2.5-*, sizes beyond the three classic ones - and custom - need gpt-image-2 or newer, and input_fidelity is rejected by gpt-image-2 up. Custom sizes are checked locally before anything is sent: multiples of 16, aspect ratio within 1:3 to 3:1, max edge 3840, and total pixels between 655,360 and 8,294,400.
The inputs that matter
prompt- the instruction. Empty raises.model-gpt-image-2.5-sunburst,gpt-image-2.5-flare,gpt-image-2, orcustom+custom_model. The dropdown is the live model list, cached.size-auto(model picks), the three classic squares/landscapes, the larger 2K set, orcustomwithcustom_width/custom_height.quality-lowthroughmax; cost scales steeply, so start atlowwhile you iterate on the prompt.background: transparent- puts the alpha on themaskoutput. It needs png or webp, and pairing it with jpeg raises.n- each image is billed separately, so this is a money lever, not a convenience.
Outputs: images (the batch), mask (alpha of the result, 1 = transparent), cost (USD, computed from the usage block against OpenAI's published per-token rates) and usage (raw JSON - handy through jz Display JSON). Billing is per output token, not per image. The author measured a 1024² low-quality edit on gpt-image-2.5-flare at roughly $0.014; the bigger models and higher quality settings are multiples of that, so read the cost output on your first few runs instead of guessing.
And if you came looking for an OpenAI video node: there isn't one, in this pack or anywhere. OpenAI shut Sora and the whole /v1/videos API down on 2026-09-24 with no replacement, and those routes 404 now.
Install
ComfyUI Manager → search comfyui-jz, or:
cd ComfyUI/custom_nodes
git clone https://github.com/j-zhang19/comfyui-jz
Restart ComfyUI. There are no models to download and no heavy dependencies - the pack declares only requests, pillow and numpy, which ComfyUI already has.
Key: OPENAI_API_KEY=... in a .env at the pack root, or [OPENAI] OPENAI_API_KEY=... in config.ini (both gitignored). Resolution is widget → environment variable → .env → config.ini, and the widget should stay empty: whatever you type there is serialised into the workflow you share.
Where people get burned
- The POST is sent exactly once, with no retries. There's no idempotency key on these endpoints, so a retried request whose first attempt actually landed is a second, separately billed generation. If a run dies on a network blip, re-queue it yourself and accept the ambiguity.
- 403 mentioning "verified" means your OpenAI organisation isn't verified yet - the node says so and points at the org settings page, and propagation can take a quarter of an hour. It is not a key problem.
insufficient_quotais out of credit, not a rate limit. Retrying won't help; the error says as much.moderation_blockedcomes with the stage and categories attached. Unsurprising for OpenAI, and worth knowing before you blame the node.- Refusals are the model's, not the node's. There's nothing to patch locally - if the hosted model won't do it, no node on the registry will.
- Sizes that "should" work fail locally. If
customraises about multiples of 16 or pixel counts, that check just saved you a paid 400.
Inputs (16)
| Name | Type | Default | Description |
|---|---|---|---|
| prompt | STRING | a photo of a cute dog | — |
| model | COMBO | gpt-image-2.5-sunburst | 4 options: gpt-image-2.5-sunburst, gpt-image-2.5-flare, gpt-image-2, custom |
| size | COMBO | 1024x1024 | auto lets the model pick. anything beyond the first three, and custom, needs gpt-image-2+ |
| custom_width | INT | 204816–3840 | — |
| custom_height | INT | 204816–3840 | — |
| quality | COMBO | auto | auto omits it. xhigh/max: gpt-image-2.5 only. cost scales steeply with quality |
| background | COMBO | auto | transparent needs png or webp; the alpha comes out on the mask output |
| output_format | COMBO | png | 3 options: png, jpeg, webp |
| n | INT | 11–10 | each image is billed |
| imageopt | IMAGE | wired = edit. every frame is one reference, max 16 | |
| maskopt | MASK | applies to the first image; 1 = area to change | |
| input_fidelityopt | COMBO | auto | edits only. auto omits it, which gpt-image-2+ requires |
| moderationopt | COMBO | auto | 2 options: auto, low |
| output_compressionopt | INT | 1000–100 | jpeg/webp only |
| custom_modelopt | STRING | — | |
| api_keyopt | STRING | — |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| images | IMAGE | — |
| mask | MASK | — |
| cost | STRING | — |
| usage | STRING | — |