Images
Generate and edit pictures across OpenAI, Google and Black Forest Labs, in the OpenAI images envelope.
Generating a picture
POST /v1/images/generations takes the same key and the same namespaced slugs as
everything else, and returns OpenAI's images envelope — data[].b64_json, with
model and a usage object beside it:
curl https://api.intfaucet.com/v1/images/generations \
-H "Authorization: Bearer $FAUCET_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-image-2",
"prompt": "a leaky bucket, watercolour",
"size": "1024x1024",
"quality": "medium"
}'From the AI SDK it is imageModel:
import { generateImage } from 'ai';
const { image, usage } = await generateImage({
model: faucet.imageModel('openai/gpt-image-2'),
prompt: 'a leaky bucket, watercolour',
});Served by OpenAI, Google and Black Forest Labs. openai/gpt-image-2.5-flare
and openai/gpt-image-2.5-sunburst — Flare for everyday generation, Sunburst
where a picture has to survive a long chain of edits — alongside
openai/gpt-image-2 and its siblings; the Gemini image models —
google/gemini-3-pro-image, google/gemini-3.1-flash-image and
google/gemini-3.1-flash-lite-image; and the FLUX.2 family — bfl/flux-2-max,
bfl/flux-2-pro, bfl/flux-2-flex, bfl/flux-2-klein-9b and
bfl/flux-2-klein-4b, which take different parameters and are billed differently
enough to have their own section below.
model is the slug that answered, in Faucet's own namespaced spelling. OpenAI
reports null in this field and Gemini has no field for it, so Faucet stamps its
own — which means a spend ledger can record the model that served a request
rather than only the one that was asked for.
Billing is tokens — and the picture is the output tokens
FLUX.2 is billed in credits instead; see its section below. This is the one place
the parallel with the chat endpoint holds more exactly than people expect.
Neither OpenAI nor Google bills per image: OpenAI quotes gpt-image-2 at
$5.00/1M input and $30.00/1M output, Google quotes gemini-3-pro-image at
$2.00/1M and $120.00/1M, and the familiar per-image figures you see quoted
elsewhere are derived — each size-and-quality tier consumes a fixed number of
output tokens, and multiplying by the output rate reproduces them. So the price
of an image is decided entirely by the tier you ask for, and the same usage
object and the same console line explain it as for any other request.
The two providers size an image differently
This is the one place a model swap is not purely a string change:
| OpenAI | ||
|---|---|---|
| Size | size, any conforming resolution | size, square only — or an aspect ratio |
| Quality | quality: low, medium, high, auto — and xhigh, max on GPT Image 2.5 | no quality tier; resolution is the lever |
| Multiple images | n | one image per request |
Ask the gateway rather than copying this table
GET /v1/models/{id} publishes sizes (or min_edge where width and height
are free), qualities, max_n, output_formats and max_input_images for
the models Faucet itself narrows — the Gemini image models and FLUX.2. OpenAI
image models publish no block, because they are passthrough and OpenAI stays
authoritative about its own endpoint. See
Model capabilities.
An OpenAI image is a resolution plus a quality tier. A Gemini image is an aspect
ratio times a resolution tier — 1K, 2K, 4K, and 512 on some models — and
the pixel dimensions fall out of that pair. Square sizes mean the same thing in
both systems, so 1024x1024 and 2048x2048 work unchanged on either. Anything
else does not translate, and Faucet refuses it rather than quietly returning a
different shape:
{
"model": "google/gemini-3-pro-image",
"prompt": "a leaky bucket, watercolour",
"provider_options": {
"google": { "aspect_ratio": "16:9", "image_size": "4K" }
}
}Ratios are 1:1, 3:2, 2:3, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9 and
21:9. Which resolution tiers a given model offers is per model — the Pro model
starts at 1K, and gemini-3.1-flash-lite-image serves 1K and nothing else —
so asking for a tier a model cannot produce is a 400 naming the ones it can,
rather than a smaller picture than you asked for and a bill for it.
The same goes for the rest: quality on a Gemini model is a 400 explaining that
resolution is the lever, and n above 1 is a 400 rather than one image where
you expected three. A parameter that cannot be honoured is refused, never dropped
— on this endpoint a silently ignored parameter is a charge you did not agree to.
Background, output format and response format
These three are accepted on both endpoints and are worth stating plainly, because they behave differently across the three providers:
| OpenAI | FLUX.2 | ||
|---|---|---|---|
background | transparent on GPT Image 2.5; opaque and auto anywhere | opaque and auto only | opaque and auto only |
output_format | png, jpeg, webp | not adjustable — a 400 | jpeg, png, webp |
response_format | b64_json, url | b64_json only | b64_json only |
background: "transparent" with output_format: "png" on
openai/gpt-image-2.5-flare is how you get a cutout with a real alpha channel.
openai/gpt-image-2.5-sunburst does the same at the same price, and is the better
of the two when edit precision matters more than latency.
Not `openai/gpt-image-2`
Transparency on that model was always an OpenAI preview, and OpenAI withdraws
it on 2026-09-30. Faucet refuses that one combination ahead of the date — a
400 naming the model to move to, rather than a forwarded request that comes
back as a picture with no alpha channel. Nothing else about gpt-image-2
changes: opaque, auto, and omitting background entirely all work exactly
as before.
Gemini cannot produce a cutout at all, so asking it for one is a 400 naming
the model that can — rather than a 200 and an opaque JPEG, which is what a
picture with no alpha looks like until you inspect it.
FLUX.2
bfl/flux-2-max, bfl/flux-2-pro, bfl/flux-2-flex, bfl/flux-2-klein-9b and
bfl/flux-2-klein-4b serve both /v1/images/generations and /v1/images/edits,
in the same OpenAI envelope as everything else: you send the same fields and get
data[].b64_json back.
The parameters map differently, and anything that cannot be honoured is refused rather than quietly dropped:
| OpenAI parameter | FLUX.2 |
|---|---|
size | WIDTHxHEIGHT becomes width / height, each at least 64px; auto or omitted lets the model choose |
output_format | jpeg, png, webp |
n | 1 only |
quality | refused — FLUX.2 has no quality tier |
background | auto and opaque; transparent is refused |
response_format | b64_json only |
image (edits) | up to 8 input pictures, or 4 on the klein models |
mask (edits) | refused — FLUX.2 has no masked edit |
Billing is credits, not tokens, which is the one place the token parallel
above does not hold. FLUX.2 reports no token counts at all; it reports a cost in
provider credits worth $0.01 each, and that figure is what the request is billed
on. A credit-billed request therefore debits none of your tokens-per-minute
allowance — there are no tokens to debit — so it is bounded by your
requests-per-minute allowance and your credit balance alone.
Upstream it is a job queue, and you never see it. A FLUX request answers with an id and a polling URL, and the picture exists only once polling reports it ready. Faucet submits, polls and downloads within your single request, so what you get back is the same synchronous response as any other image model — at the cost of a longer wall-clock wait on the larger models.
Editing a picture
POST /v1/images/edits is the same endpoint with a picture going in. Send the
image you already have and say what to change about it:
curl https://api.intfaucet.com/v1/images/edits \
-H "Authorization: Bearer $FAUCET_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemini-3-pro-image",
"prompt": "move the apartment building to the centre of the square",
"image": "data:image/png;base64,iVBORw0KGgo…"
}'Both JSON and multipart/form-data are accepted. OpenAI's own edits endpoint
is multipart only, so their SDK works here unchanged; JSON is for the clients that
already post JSON everywhere else and hold the bytes in hand. The two are
equivalent — pick whichever you already have.
curl https://api.intfaucet.com/v1/images/edits \
-H "Authorization: Bearer $FAUCET_API_KEY" \
-F "model=openai/gpt-image-2" \
-F "prompt=make the sign read OPEN" \
-F "image=@storefront.png"image takes one picture or several — a bare base64 string, a data: URL, or an
array of either. Several inputs is how you ask a model to combine them. PNG, JPEG
and WebP are accepted; the format is read from the bytes rather than from what you
called the file, so a mislabelled upload still works. A remote https:// URL is
not accepted: Faucet does not fetch images on your behalf.
mask is OpenAI-only inpainting — transparent where the model may redraw, opaque
where it may not. Gemini has no equivalent, so a mask sent to a Gemini model is a
400 rather than a mask quietly ignored.
An edit is billed at two input rates on OpenAI, and this is the one number
that surprises people. OpenAI charges $8.00/1M for image input against $5.00/1M
for text on gpt-image-2, and an edit's input is mostly picture — so the input
half of an edit costs roughly 60% more per token than the same count of prompt
text. The response says so: usage.input_tokens_details.image_tokens is the
picture's share of usage.input_tokens. Google quotes one input rate covering
both, so the split is reported as zero there and the arithmetic is simpler.
Sending an image to `/v1/images/generations` is a `400`
Generation draws from the prompt alone; if image, images, image_url or
mask appears on that endpoint the request is refused with a message pointing
here. That refusal exists because the alternative — accepting the field,
ignoring it, and returning a picture invented from the words in your
instruction — looks exactly like an edit that came out badly.
Images and your rate limit
Image output tokens are the picture, and they are counted like any others. One high-quality 1024×1024 image is a few thousand output tokens, so a modest tokens-per-minute allowance can be spent in a handful of pictures. See Errors and limits.