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, bedrock, gemini). Default is resolved per-model from the catalog. |
Request body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
model |
string | Yes | — | Model identifier (e.g. dall-e-3, gpt-image-1, amazon.nova-canvas-v1:0, imagen-4.0-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. DALL-E 3 only — stripped on other models |
background |
string | No | null | transparent, opaque, auto. gpt-image-1 only — stripped on DALL-E |
output_format |
string | No | null | png, jpeg, webp. gpt-image-1 only — stripped on DALL-E |
output_compression |
integer | No | null | 0..100 JPEG/WebP quality. gpt-image-1 only |
moderation |
string | No | null | auto or low. gpt-image-1 only |
seed |
integer | No | null | Deterministic seed. Forwarded to Bedrock + Stability when present; DALL-E + gpt-image-1 don’t accept seeds today |
negative_prompt |
string | No | null | Text describing what to avoid. Forwarded to Bedrock Titan/Nova (negativeText) and Stability (negative_prompt). DALL-E + gpt-image-1 don’t accept negative prompts — 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. Bedrock and Imagen have 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": "dall-e-3",
"prompt": "A whimsical watercolor of a sleepy hedgehog under a mushroom",
"size": "1024x1024",
"quality": "standard"
}'
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",
"prompt": "A logo on a fully transparent background, single hedgehog character",
"quality": "medium",
"background": "transparent",
"output_format": "png"
}'
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="dall-e-3",
prompt="A whimsical watercolor of a sleepy hedgehog",
size="1024x1024",
quality="standard",
)
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",
"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": 1777975004,
"model": "dall-e-3",
"data": [
{
"b64_json": "iVBORw0KGgoAAAANSUhEUgAA...",
"revised_prompt": "A whimsical watercolor illustration ...",
"seed": null,
"watermark": "none",
"flagged": false
}
],
"usage": {
"images": 1,
"input_tokens": null,
"output_tokens": null,
"total_tokens": null,
"megapixels": null,
"credits_used": 640000,
"credits_remaining": 99360000
}
}
Response fields
| Field | Type | Description |
|---|---|---|
created |
integer | Unix timestamp from the upstream provider (0 if the provider doesn’t supply one — Bedrock, Imagen) |
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 gpt-image-1) |
data[].revised_prompt |
string or null | Upstream-rewritten prompt. Populated on DALL-E 3 + Gemini Flash Image; null on other models |
data[].seed |
integer or null | Seed used by upstream when reported (Bedrock Stability echoes the seed; others return null) |
data[].watermark |
string | One of c2pa, provenance, synthid, none — see Watermarking |
data[].flagged |
boolean | true if upstream content moderation blocked the image (currently exercised by Bedrock Titan/Nova path) |
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-1 + Gemini Flash Image only |
usage.output_tokens |
integer or null | Output (image) token count — gpt-image-1 + Gemini Flash Image only |
usage.total_tokens |
integer or null | Sum of input + output tokens — gpt-image-1 + Gemini Flash Image only |
usage.megapixels |
number or null | Megapixels rendered — Bedrock + Imagen surrogate when token counts aren’t available |
usage.credits_used |
integer | Micro-credits consumed by this request |
usage.credits_remaining |
integer or null | Micro-credits remaining (null for unlimited licenses) |
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 | gpt-image-1 family |
provenance |
AWS provenance metadata | Amazon Titan Image v2, Nova Canvas |
synthid |
Google SynthID | Google Imagen 4, Gemini Flash Image |
none |
No watermark | DALL-E 2, DALL-E 3, Bedrock Stability |
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 | Max n |
Sizes | quality values |
Pricing | Notes |
|---|---|---|---|---|---|
dall-e-2 |
— | — | — | — | Retired by OpenAI in 2026. Catalog row exists but is inactive — requests return 400 No active providers |
dall-e-3 |
— | — | — | — | Retired by OpenAI in 2026. Catalog row exists but is inactive — requests return 400 No active providers |
gpt-image-1 |
1 | up to 3840px on the longer edge | low, medium, high, auto |
token-priced (~$0.005 to ~$0.21 per 1024² image) | Org verification required. Always base64 + C2PA. Accepts background, output_format, output_compression, moderation. The only active OpenAI image model today |
amazon.titan-image-generator-v2:0 |
5 | 512x512, 1024x1024 |
standard, premium |
$0.008–$0.014 per image | Accepts negative_prompt, seed |
amazon.nova-canvas-v1:0 |
5 | up to 2048x2048 |
standard, premium |
$0.040–$0.080 per image | Same body as Titan |
stability.stable-image-core-v1:0 |
1 | aspect-ratio-based (1:1, 16:9, …) |
— | $0.030 per image | Region-restricted to us-west-2 |
stability.stable-image-ultra-v1:0 |
1 | aspect-ratio-based | — | $0.080 per image | Region-restricted to us-west-2 |
stability.sd3-5-large-v1:0 |
1 | aspect-ratio-based | — | $0.065 per image | Region-restricted to us-west-2 |
imagen-4.0-fast-generate-001 |
4 | 1K (aspect ratios: 1:1, 3:4, 4:3, 9:16, 16:9) |
— | $0.020 per image | SynthID watermark |
imagen-4.0-generate-001 |
4 | 1K, 2K |
— | $0.040 per image | SynthID watermark |
imagen-4.0-ultra-generate-001 |
4 | 1K, 2K |
— | $0.060 per image | SynthID watermark |
gemini-2.5-flash-image |
1 | chat-style (no size knob) |
— | token-priced (~$0.039 per 1024²) | Routes through :generateContent. Returns text + image parts |
Discovery via /v1/models
Image-gen-capable models are listed in GET /v1/models alongside chat models. Filter for image generation by:
supported_featurescontains"image_generation"(recommended, semantic):image_models = [m for m in models if "image_generation" in m.get("supported_features", [])]output_modality.image == true— image models declare image output; LLMs and embedding models do not.
The catalog also exposes per-model pricing in cost (JSONB — shape varies by model: per_image, per_image_by_size, per_image_by_size_quality, or per_token).
Providers
| Provider | Slug | Default? | Status |
|---|---|---|---|
| OpenAI Direct | openai |
Yes | Active for gpt-image-1 only. DALL-E 2 and DALL-E 3 catalog rows are present but inactive — OpenAI retired both models in 2026 |
| AWS Bedrock | bedrock |
No | Catalog rows seeded but is_active=false — pending ops to re-enable Titan/Nova in AWS Console and switch the region to us-west-2 for Stability |
| Google Gemini | gemini |
No | Catalog rows seeded but is_active=false — pending the Gemini API project being upgraded to a paid tier |
Routing is resolved per-model from the catalog. The default for image_generation is OpenAI, but if you request amazon.nova-canvas-v1:0 (once active) the router automatically picks the bedrock provider — no X-Quantized-Provider header needed.
# Once Bedrock is active, this routes through Bedrock automatically:
curl -X POST https://api.quantized.us/v1/images/generations \
-H "Authorization: Bearer sk-quantized-YOUR-KEY" \
-H "Content-Type: application/json" \
-d '{"model": "amazon.nova-canvas-v1:0", "prompt": "a hedgehog"}'
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 requested model has output_modality.image == false (e.g. an LLM or embedding model id) |
400 |
X-Quantized-Provider slug has no ProviderModel row for the requested model |
400 |
Model exists in the catalog but no active ProviderModel rows (e.g. Bedrock/Gemini models awaiting ops re-enable) |
401 |
Invalid or missing API key |
402 |
Insufficient credits |
404 |
Unknown model id |
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, gpt-image-1 org-verification failure) |
flaggedWhen upstream (currently Bedrock Titan/Nova) blocks a generation for content-policy reasons, it returns 200 with an empty images array and an inline error string instead of a 4xx. The router surfaces this as a successful response with data: [{flagged: true, ...}] and usage.images: 0. Billing is $0 for blocked generations (no images were generated). Check data[0].flagged if you need to detect this case programmatically.
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) — DALL-E 2 only 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.