Image Generation
POST /v1/images/generations
Generate one or more images from a text prompt. Compatible (with extensions) with the OpenAI Images API.
Headers
| Header | Required | Description |
|---|---|---|
Authorization |
Yes | Bearer <api-key-or-jwt> |
Content-Type |
Yes | application/json |
X-Quantized-Provider |
No | Force a specific provider (openai or gemini). Otherwise resolved per-model from the catalog. |
Request body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
model |
string | Yes | — | Model identifier (e.g. gpt-image-1.5, gpt-image-2, gemini-3.1-flash-image, imagen-4.0-fast-generate-001) |
prompt |
string | Yes | — | Text description of the image to generate |
n |
integer | No | 1 |
Number of images to generate. Range 1..10 at the serializer; per-model upper bounds are stricter (see Per-model constraints) |
size |
string | No | "1024x1024" |
Image dimensions as WxH, or "auto". Normalized per-provider to the nearest supported aspect ratio |
quality |
string | No | provider default | One of standard, hd, low, medium, high, auto |
style |
string | No | null | vivid or natural. Accepted for compatibility; no currently routable model uses it |
background |
string | No | null | transparent, opaque, auto. gpt-image-* only — stripped elsewhere |
output_format |
string | No | null | png, jpeg, webp. gpt-image-* only — stripped elsewhere |
output_compression |
integer | No | null | 0..100 JPEG/WebP quality. gpt-image-* only |
moderation |
string | No | null | auto or low. gpt-image-* only |
seed |
integer | No | null | Deterministic seed. Accepted for compatibility; no currently routable model accepts one — silently dropped |
negative_prompt |
string | No | null | Text describing what to avoid. Accepted for compatibility; no currently routable model accepts one — silently dropped |
user |
string | No | null | End-user identifier forwarded to the provider for abuse monitoring |
The serializer uses extra="forbid" — any field not listed above is rejected with 422. This keeps typos from being silently dropped.
Notable fields not accepted in this release: response_format (transport is forced to base64 — see below), messages, dimensions, image/mask (those belong to the not-yet-shipped /v1/images/edits endpoint).
The router returns b64_json for every response. Read images from data[].b64_json. There is no data[].url field. OpenAI itself removed the response_format parameter from /v1/images/generations in 2026, so even on the upstream side everything is base64. Gemini has always been base64 natively, so the contract is uniform across providers.
Examples
curl -X POST https://api.quantized.us/v1/images/generations \
-H "Authorization: Bearer sk-quantized-YOUR-KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-image-1.5",
"prompt": "A whimsical watercolor of a sleepy hedgehog under a mushroom",
"size": "1024x1024",
"quality": "low"
}'
curl -X POST https://api.quantized.us/v1/images/generations \
-H "Authorization: Bearer sk-quantized-YOUR-KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-image-1.5",
"prompt": "A logo on a fully transparent background, single hedgehog character",
"quality": "medium",
"background": "transparent",
"output_format": "png"
}'
curl -X POST https://api.quantized.us/v1/images/generations \
-H "Authorization: Bearer sk-quantized-YOUR-KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-3.1-flash-image",
"prompt": "A whimsical watercolor of a sleepy hedgehog under a mushroom"
}'
import base64
from openai import OpenAI
client = OpenAI(
api_key="sk-quantized-YOUR-KEY",
base_url="https://api.quantized.us/v1",
)
resp = client.images.generate(
model="gpt-image-1.5",
prompt="A whimsical watercolor of a sleepy hedgehog",
size="1024x1024",
quality="low",
)
img_bytes = base64.b64decode(resp.data[0].b64_json)
with open("hedgehog.png", "wb") as f:
f.write(img_bytes)
import base64
import httpx
response = httpx.post(
"https://api.quantized.us/v1/images/generations",
headers={"Authorization": "Bearer sk-quantized-YOUR-KEY"},
json={
"model": "gpt-image-1.5",
"prompt": "An overhead view of an open notebook with a pen",
"size": "1024x1024",
"quality": "low",
},
timeout=180,
)
data = response.json()
img_bytes = base64.b64decode(data["data"][0]["b64_json"])
print(f"image: {len(img_bytes)} bytes, watermark: {data['data'][0]['watermark']}")
Response
{
"created": 1787941342,
"model": "gpt-image-1.5",
"data": [
{
"b64_json": "iVBORw0KGgoAAAANSUhEUgAA...",
"revised_prompt": null,
"seed": null,
"watermark": "c2pa",
"flagged": false
}
],
"usage": {
"images": 1,
"input_tokens": 16,
"output_tokens": 272,
"total_tokens": 288,
"megapixels": null,
"credits_used": 1790500
}
}
Response fields
| Field | Type | Description |
|---|---|---|
created |
integer | Unix timestamp from the upstream provider (0 if the provider doesn’t supply one — Gemini) |
model |
string | Model id echoed from the request (with any provider prefix preserved) |
data |
array | One entry per generated image, in upstream order |
data[].b64_json |
string | Base64-encoded image bytes (PNG by default, JPEG/WebP if requested on a gpt-image-* model) |
data[].revised_prompt |
string or null | Upstream-rewritten prompt. Gemini Flash Image returns mixed text and image parts, and the text part lands here when present, so the same model returns a string on one call and null on the next. null everywhere else |
data[].seed |
integer or null | Seed used by upstream when reported. No currently routable model reports one, so this is always null today |
data[].watermark |
string | One of c2pa, provenance, synthid, none — see Watermarking |
data[].flagged |
boolean | true if upstream answered a moderation block with 200 and no image. No currently routable provider does this, so it is always false today |
usage.images |
integer | Number of images successfully generated. Equals data.length; can be 0 when flagged is true |
usage.input_tokens |
integer or null | Input token count — gpt-image-* and Gemini Flash Image. null on Imagen |
usage.output_tokens |
integer or null | Output (image) token count — gpt-image-* and Gemini Flash Image. null on Imagen |
usage.total_tokens |
integer or null | Sum of input + output tokens — gpt-image-* and Gemini Flash Image. null on Imagen |
usage.megapixels |
number or null | Megapixels rendered, as a surrogate when token counts aren’t available. null on every currently routable model |
usage.credits_used |
integer | Micro-credits consumed by this request |
Image generation endpoints are not streamable on any provider. stream: true is not accepted.
Watermarking
Different providers/models embed different provenance metadata. The watermark enum in each data[] entry lets clients disclose this to end users if needed.
| Value | Standard | Models |
|---|---|---|
c2pa |
C2PA Content Credentials | the gpt-image-* family |
provenance |
AWS provenance metadata | Reserved for Amazon Titan / Nova; not currently routable |
synthid |
Google SynthID | Gemini Flash Image, Imagen 4 |
none |
No watermark | No currently routable model returns this |
Quantized targets education customers. If your application surfaces images to students, consider rendering a disclosure when watermark != "none". The C2PA and SynthID standards are designed to be detectable downstream, but a textual disclosure removes ambiguity.
Per-model constraints
Each model has stricter limits than the serializer’s generic [1, 10] range for n and unrestricted size. The router does not enforce these — they’re handled by upstream:
| Model | Provider | Max n |
Sizes | quality values |
Pricing |
|---|---|---|---|---|---|
gpt-image-2 |
openai | 1 | up to 3840px on the longer edge | low, medium, high, auto |
$5/M input + $30/M output tokens |
gpt-image-1.5 |
openai | 1 | up to 3840px on the longer edge | low, medium, high, auto |
token-priced |
gpt-image-1-mini |
openai | 1 | up to 3840px on the longer edge | low, medium, high, auto |
token-priced |
chatgpt-image-latest |
openai | 1 | up to 3840px on the longer edge | low, medium, high, auto |
token-priced |
gemini-3-pro-image |
gemini | 1 | chat-style (no size knob) |
— | $2/M input + $120/M output tokens |
gemini-3.1-flash-image |
gemini | 1 | chat-style (no size knob) |
— | $0.50/M input + $60/M output tokens |
gemini-3.1-flash-lite-image |
gemini | 1 | chat-style (no size knob) |
— | $0.25/M input + $30/M output tokens |
gemini-2.5-flash-image |
gemini | 1 | chat-style (no size knob) |
— | $0.30/M input + $30/M output tokens |
imagen-4.0-fast-generate-001 |
gemini | 4 | 1K (aspect ratios: 1:1, 3:4, 4:3, 9:16, 16:9) |
— | $0.020 per image |
The gpt-image-* models accept background, output_format, output_compression and moderation, and require OpenAI org verification on our account. The Gemini Flash Image models route through :generateContent and return mixed text and image parts. Imagen routes through :predict.
gpt-image-1 is still resolvable but is deprecated in the catalog. DALL-E 2 and DALL-E 3 were retired by OpenAI in 2026 and are no longer served.
imagen-4.0-fast-generate-001 resolves and routes correctly, but Gemini’s :predict endpoint is not available on a free-tier API key, so requests currently come back 404 from Google. The Flash Image models are unaffected.
Discovery via /v1/models
Filter GET /v1/models on output_modality.image == true:
models = response.json()["data"] # GET /v1/models returns {"object": "list", "data": [...]}
image_models = [m for m in models if m["output_modality"]["image"]]
supported_features does not carry image_generation, so there is no semantic filter for this endpoint — a "image_generation" in supported_features check returns an empty list.
output_modality.image is the closest available signal, but it also matches models served only by OpenRouter, which cannot serve this endpoint. Cross-check against the table above until the catalog exposes which provider serves a model.
The catalog exposes per-model pricing in cost, whose shape varies by model (per_image for Imagen, prompt / completion for the token-priced models, and {} where the adapter computes the rate itself).
Providers
| Provider | Slug | Models |
|---|---|---|
| OpenAI Direct | openai |
gpt-image-2, gpt-image-1.5, gpt-image-1-mini, chatgpt-image-latest, gpt-image-1 (deprecated) |
| Google Gemini | gemini |
gemini-3-pro-image, gemini-3.1-flash-image, gemini-3.1-flash-lite-image, gemini-2.5-flash-image, imagen-4.0-fast-generate-001 |
These are the only two providers holding the image_generation capability, so X-Quantized-Provider: bedrock is not usable here. Routing is resolved per-model from the catalog, so requesting gemini-3.1-flash-image reaches Gemini with no header needed.
Unlike embeddings, image generation does not expose /v1/aws-bedrock/images/generations or /v1/gemini/images/generations native passthroughs. The unified /v1/images/generations is the only image endpoint — each provider adapts to that shape internally.
Errors
| Status | Condition |
|---|---|
400 |
Modality mismatch — the model resolves but no provider serving it can produce image output (e.g. an LLM or embedding model id) |
400 |
X-Quantized-Provider names a provider that cannot serve this endpoint |
400 or 404 |
Unknown model id — forwarded to a provider, which decides. See below |
401 |
Invalid or missing API key |
402 |
Insufficient credits |
422 |
Validation error — missing required field, unsupported enum value, out-of-range integer, or any field not in the documented schema (extra="forbid") |
503 |
Upstream provider unavailable (timeout, rate limit, auth failure on the upstream key, OpenAI org-verification failure) |
Model validation belongs to the provider (see Who validates the model). An id the catalog cannot resolve is forwarded as sent, and because this endpoint has two eligible providers with nothing to choose between them, the same request can reach either one:
reached Gemini 404 models/<id> is not found for API version v1beta ...
reached OpenAI 400 The model '<id>' does not exist.
Pin X-Quantized-Provider if you need a predictable failure. Requests naming a model the catalog resolves are unaffected: the model selects its provider.
Both providers answer a content-policy block with a normal error response (400 with the upstream message), which the router maps to its standard error hierarchy.
The response schema also carries data[].flagged and allows usage.images: 0 for providers that answer a block with 200 and no image. No currently routable provider does this, so flagged is always false today.
The following are not accepted by /v1/images/generations in this release:
- Image edits (
/v1/images/edits) — requires multipart upload and mask handling - Image variations (
/v1/images/variations) response_format: "url"— transport is forced tob64_json- Streaming — no provider streams image bytes
- CDN rehosting — base64 only, no signed URLs
These may be added in a future release.