Nodes/comfyui-jz/jz OpenAI Image
ComfyUI Node

jz OpenAI Image

Gpt-image edits in your own graph, billed to your own OpenAI key

By j-zhang19·Created 3 months ago·Updated 14 days ago· 3
jz OpenAI Image
  • image
  • mask
  • images
  • mask
  • cost
  • usage
◄prompta photo of a cute dog►
◄modelgpt-image-2.5-sunburst►
◄size1024x1024►
◄custom_width2048►
◄custom_height2048►
◄qualityauto►
◄backgroundauto►
◄output_formatpng►
◄n1►
◄input_fidelityauto►
◄moderationauto►
◄output_compression100►
◄custom_model►
◄api_key►

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, or custom + custom_model. The dropdown is the live model list, cached.
  • size - auto (model picks), the three classic squares/landscapes, the larger 2K set, or custom with custom_width / custom_height.
  • quality - low through max; cost scales steeply, so start at low while you iterate on the prompt.
  • background: transparent - puts the alpha on the mask output. 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_quota is out of credit, not a rate limit. Retrying won't help; the error says as much.
  • moderation_blocked comes 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 custom raises about multiples of 16 or pixel counts, that check just saved you a paid 400.
Categoryjz/api

Inputs (16)

NameTypeDefaultDescription
promptSTRINGa photo of a cute dog—
modelCOMBOgpt-image-2.5-sunburst4 options: gpt-image-2.5-sunburst, gpt-image-2.5-flare, gpt-image-2, custom
sizeCOMBO1024x1024auto lets the model pick. anything beyond the first three, and custom, needs gpt-image-2+
custom_widthINT204816–3840—
custom_heightINT204816–3840—
qualityCOMBOautoauto omits it. xhigh/max: gpt-image-2.5 only. cost scales steeply with quality
backgroundCOMBOautotransparent needs png or webp; the alpha comes out on the mask output
output_formatCOMBOpng3 options: png, jpeg, webp
nINT11–10each image is billed
imageoptIMAGEwired = edit. every frame is one reference, max 16
maskoptMASKapplies to the first image; 1 = area to change
input_fidelityoptCOMBOautoedits only. auto omits it, which gpt-image-2+ requires
moderationoptCOMBOauto2 options: auto, low
output_compressionoptINT1000–100jpeg/webp only
custom_modeloptSTRING—
api_keyoptSTRING—

Outputs (4)

NameTypeDescription
imagesIMAGE—
maskMASK—
costSTRING—
usageSTRING—