AI Gateway Image Generation via Chat Completions
AI Gateway supports image generation using the Chat Completions API for the models listed under the Image Gen filter at the AI Gateway Models page, including multimodal LLMs and image-only models.
Multimodal LLMs like Nano Banana, Nano Banana Pro, and GPT-5 variants can generate images alongside text using the /v1/chat/completions endpoint. Images are returned in the response's images array.
{
"id": "chatcmpl-123",
"object": "chat.completion",
"created": 1677652288,
"model": "google/gemini-3.1-flash-image-preview",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "I've generated a beautiful sunset image for you.",
"images": [
{
"type": "image_url",
"image_url": {
"url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."
}
}
]
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 15,
"completion_tokens": 28,
"total_tokens": 43
}
}For streaming requests, images are delivered in delta chunks:
{
"id": "chatcmpl-123",
"object": "chat.completion.chunk",
"created": 1677652288,
"model": "google/gemini-3.1-flash-image-preview",
"choices": [
{
"index": 0,
"delta": {
"images": [
{
"type": "image_url",
"image_url": {
"url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."
}
}
]
},
"finish_reason": null
}
]
}Image-only models use the OpenAI-compatible /v1/images/generations endpoint, not /v1/chat/completions. Call them with openai.images.generate from the OpenAI SDK.
OpenAI's openai/gpt-image-2 is an image-only model. Call it directly with openai.images.generate.
import OpenAI from 'openai';
const openai = new OpenAI({
apiKey: process.env.AI_GATEWAY_API_KEY,
baseURL: 'https://ai-gateway.vercel.sh/v1',
});
const result = await openai.images.generate({
model: 'openai/gpt-image-2',
prompt: 'A Devon Rex peering into a koi pond in the style of ukiyo-e',
});
console.log(`Generated ${(result.data?.length ?? 0)} image(s)`);import os
from openai import OpenAI
client = OpenAI(
api_key=os.getenv('AI_GATEWAY_API_KEY'),
base_url='https://ai-gateway.vercel.sh/v1',
)
result = client.images.generate(
model='openai/gpt-image-2',
prompt='A Devon Rex peering into a koi pond in the style of ukiyo-e',
)
print(f'Generated {len(result.data)} image(s)')curl --fail-with-body https://ai-gateway.vercel.sh/v1/images/generations \
-H "Authorization: Bearer $AI_GATEWAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-image-2",
"prompt": "A Devon Rex peering into a koi pond in the style of ukiyo-e"
}'Black Forest Labs' Flux models offer advanced image generation with various capabilities. Multiple models are available including but not limited to:
bfl/flux-2-probfl/flux-2-flexbfl/flux-kontext-maxbfl/flux-kontext-probfl/flux-pro-1.0-fillbfl/flux-pro-1.1
View available Black Forest Labs provider options for configuration details.
import OpenAI from 'openai';
import 'dotenv/config';
async function main() {
const openai = new OpenAI({
apiKey: process.env.AI_GATEWAY_API_KEY,
baseURL: 'https://ai-gateway.vercel.sh/v1',
});
const result = await openai.images.generate({
model: 'bfl/flux-2-pro',
prompt: `Render an echidna swimming across the Mozambique channel at sunset with phosphorescent jellyfish`,
});
// Process the generated images
for (const image of result.data ?? []) {
if (image.b64_json) {
console.log(
'Generated image (base64):',
image.b64_json.substring(0, 50) + '...',
);
}
}
}
main().catch(console.error);import OpenAI from 'openai';
import 'dotenv/config';
async function main() {
const openai = new OpenAI({
apiKey: process.env.AI_GATEWAY_API_KEY,
baseURL: 'https://ai-gateway.vercel.sh/v1',
});
const result = await openai.images.generate({
model: 'bfl/flux-2-pro',
prompt: `Draw a gorgeous image of a river made of white owl feathers snaking through a serene winter landscape`,
// AI Gateway extension fields are not included in the upstream SDK types.
...{
providerOptions: {
blackForestLabs: {
outputFormat: 'jpeg',
safetyTolerance: 2,
},
},
},
});
// Process the generated images
for (const image of result.data ?? []) {
if (image.b64_json) {
console.log(
'Generated image (base64):',
image.b64_json.substring(0, 50) + '...',
);
}
}
}
main().catch(console.error);import base64
import json
import os
from datetime import datetime
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()
def main():
api_key = os.getenv("AI_GATEWAY_API_KEY") or os.getenv("VERCEL_OIDC_TOKEN")
base_url = (
os.getenv("AI_GATEWAY_BASE_OPENAI_COMPAT_URL")
or "https://ai-gateway.vercel.sh/v1"
)
client = OpenAI(
api_key=api_key,
base_url=base_url,
)
result = client.images.generate(
model="bfl/flux-2-pro",
prompt=(
"A mystical aurora borealis dancing over a frozen lake "
"with snow-covered mountains reflected in the ice"
),
n=1,
response_format="b64_json",
extra_body={
"providerOptions": {
"blackForestLabs": {
"outputFormat": "jpeg",
"safetyTolerance": 2,
}
}
},
)
if not result or not result.data or len(result.data) == 0:
raise Exception("No image data received from OpenAI-compatible endpoint")
print(f"Generated {len(result.data)} image(s)")
for i, image in enumerate(result.data):
if hasattr(image, "b64_json") and image.b64_json:
# Decode base64 to get image size
image_bytes = base64.b64decode(image.b64_json)
print(f"Image {i+1}:")
print(f" Size: {len(image_bytes)} bytes")
print(f" Base64 preview: {image.b64_json[:50]}...")
# Save image to file with timestamp
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_file = f"output/output_image_{timestamp}_{i+1}.jpg"
print(f" Saving image to {output_file}")
with open(output_file, "wb") as f:
f.write(image_bytes)
if hasattr(result, "provider_metadata"):
print("\nProvider metadata:")
print(json.dumps(result.provider_metadata, indent=2))
if __name__ == "__main__":
main()curl --fail-with-body https://ai-gateway.vercel.sh/v1/images/generations \
-H "Authorization: Bearer $AI_GATEWAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "bfl/flux-2-pro",
"prompt": "A mystical aurora borealis over a frozen lake.",
"n": 1,
"response_format": "b64_json"
}'SpaceXAI's Grok Imagine models generate high-quality images from text prompts with support for various aspect ratios. Browse the current lineup in the model list.
import OpenAI from 'openai';
import 'dotenv/config';
async function main() {
const openai = new OpenAI({
apiKey: process.env.AI_GATEWAY_API_KEY,
baseURL: 'https://ai-gateway.vercel.sh/v1',
});
const result = await openai.images.generate({
model: 'spacexai/grok-imagine-image',
prompt: `A serene Japanese garden with a koi pond, stone lanterns, and cherry blossoms in full bloom`,
});
// Process the generated images
for (const image of result.data ?? []) {
if (image.b64_json) {
console.log(
'Generated image (base64):',
image.b64_json.substring(0, 50) + '...',
);
}
}
}
main().catch(console.error);import base64
import os
from datetime import datetime
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()
def main():
api_key = os.getenv("AI_GATEWAY_API_KEY") or os.getenv("VERCEL_OIDC_TOKEN")
base_url = (
os.getenv("AI_GATEWAY_BASE_OPENAI_COMPAT_URL")
or "https://ai-gateway.vercel.sh/v1"
)
client = OpenAI(
api_key=api_key,
base_url=base_url,
)
result = client.images.generate(
model="spacexai/grok-imagine-image",
prompt=(
"A serene Japanese garden with a koi pond, "
"stone lanterns, and cherry blossoms in full bloom"
),
n=1,
response_format="b64_json",
)
if not result or not result.data or len(result.data) == 0:
raise Exception("No image data received from OpenAI-compatible endpoint")
print(f"Generated {len(result.data)} image(s)")
for i, image in enumerate(result.data):
if hasattr(image, "b64_json") and image.b64_json:
image_bytes = base64.b64decode(image.b64_json)
print(f"Image {i+1}:")
print(f" Size: {len(image_bytes)} bytes")
print(f" Base64 preview: {image.b64_json[:50]}...")
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_file = f"output/output_image_{timestamp}_{i+1}.png"
print(f" Saving image to {output_file}")
with open(output_file, "wb") as f:
f.write(image_bytes)
if __name__ == "__main__":
main()curl --fail-with-body https://ai-gateway.vercel.sh/v1/images/generations \
-H "Authorization: Bearer $AI_GATEWAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "spacexai/grok-imagine-image",
"prompt": "A futuristic city at sunset.",
"n": 1,
"response_format": "b64_json"
}'Image-only models can also edit an existing image. Send one or more source images and a prompt describing the change to the OpenAI-compatible /v1/images/edits endpoint, using openai.images.edit from the OpenAI SDK.
Support varies by model. Models that accept image inputs include openai/gpt-image-2, bfl/flux-kontext-pro, bfl/flux-pro-1.0-fill, google/gemini-3.1-flash-image, and spacexai/grok-imagine-image. Edited images are returned as base64 strings in data, the same as image generation.
import { createReadStream } from 'node:fs';
import OpenAI from 'openai';
const openai = new OpenAI({
apiKey: process.env.AI_GATEWAY_API_KEY,
baseURL: 'https://ai-gateway.vercel.sh/v1',
});
const result = await openai.images.edit({
model: 'openai/gpt-image-2',
image: createReadStream('source.png'),
prompt: 'Add a watercolor effect to this image',
});
console.log(`Edited ${(result.data?.length ?? 0)} image(s)`);import os
from openai import OpenAI
client = OpenAI(
api_key=os.getenv('AI_GATEWAY_API_KEY'),
base_url='https://ai-gateway.vercel.sh/v1',
)
result = client.images.edit(
model='openai/gpt-image-2',
image=open('source.png', 'rb'),
prompt='Add a watercolor effect to this image',
)
print(f'Edited {len(result.data)} image(s)')curl --fail-with-body https://ai-gateway.vercel.sh/v1/images/edits \
-H "Authorization: Bearer $AI_GATEWAY_API_KEY" \
-F "model=openai/gpt-image-2" \
-F "prompt=Add a watercolor effect to this image" \
-F "image=@source.png"Pass up to 16 source images to compose them into a single result:
import { createReadStream } from 'node:fs';
import OpenAI from 'openai';
const openai = new OpenAI({
apiKey: process.env.AI_GATEWAY_API_KEY,
baseURL: 'https://ai-gateway.vercel.sh/v1',
});
const result = await openai.images.edit({
model: 'openai/gpt-image-2',
image: [
createReadStream('body-lotion.png'),
createReadStream('bath-bomb.png'),
createReadStream('soap.png'),
],
prompt: 'Create a lovely gift basket with these three items in it',
});
console.log(result.data);import os
from openai import OpenAI
client = OpenAI(api_key=os.environ["AI_GATEWAY_API_KEY"], base_url="https://ai-gateway.vercel.sh/v1")
result = client.images.edit(
model='openai/gpt-image-2',
image=[
open('body-lotion.png', 'rb'),
open('bath-bomb.png', 'rb'),
open('soap.png', 'rb'),
],
prompt='Create a lovely gift basket with these three items in it',
)
print(result.data)curl --fail-with-body https://ai-gateway.vercel.sh/v1/images/edits \
-H "Authorization: Bearer $AI_GATEWAY_API_KEY" \
-F "model=openai/gpt-image-2" \
-F "prompt=Create a lovely gift basket with these three items in it" \
-F "image[]=@body-lotion.png" \
-F "image[]=@bath-bomb.png" \
-F "image[]=@soap.png"Pass a mask to restrict the edit to a specific region. The mask must be the same size as the source image, and its transparent areas mark the region to replace:
import { createReadStream } from 'node:fs';
import OpenAI from 'openai';
const openai = new OpenAI({
apiKey: process.env.AI_GATEWAY_API_KEY,
baseURL: 'https://ai-gateway.vercel.sh/v1',
});
const result = await openai.images.edit({
model: 'openai/gpt-image-2',
image: createReadStream('living-room.png'),
mask: createReadStream('mask.png'),
prompt: 'Place a potted fern in the empty corner',
});
console.log(result.data);import os
from openai import OpenAI
client = OpenAI(api_key=os.environ["AI_GATEWAY_API_KEY"], base_url="https://ai-gateway.vercel.sh/v1")
result = client.images.edit(
model='openai/gpt-image-2',
image=open('living-room.png', 'rb'),
mask=open('mask.png', 'rb'),
prompt='Place a potted fern in the empty corner',
)
print(result.data)curl --fail-with-body https://ai-gateway.vercel.sh/v1/images/edits \
-H "Authorization: Bearer $AI_GATEWAY_API_KEY" \
-F "model=openai/gpt-image-2" \
-F "prompt=Place a potted fern in the empty corner" \
-F "image=@living-room.png" \
-F "mask=@mask.png"To reference images you already host instead of uploading bytes, send a JSON body. Each entry in images takes an image_url that is either an https URL or a base64 data: URL:
curl -X POST "https://ai-gateway.vercel.sh/v1/images/edits" \
-H "Authorization: Bearer $AI_GATEWAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-image-2",
"prompt": "Add a watercolor effect to this image",
"images": [{ "image_url": "https://example.com/source.png" }]
}'You can use the OpenAI Python client to generate images with the AI Gateway:
import base64
import os
from datetime import datetime
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()
def main():
# Initialize the OpenAI client with AI Gateway
client = OpenAI(
api_key=os.getenv("AI_GATEWAY_API_KEY"),
base_url="https://ai-gateway.vercel.sh/v1",
)
# Generate an image
result = client.images.generate(
model="bfl/flux-2-pro",
prompt="A majestic blue whale breaching the ocean surface at sunset",
n=1,
response_format="b64_json",
)
if not result.data:
raise Exception("No image data received")
print(f"Generated {len(result.data)} image(s)")
# Save images to disk
for i, image in enumerate(result.data):
if image.b64_json:
image_bytes = base64.b64decode(image.b64_json)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_file = f"output/image_{timestamp}_{i+1}.png"
with open(output_file, "wb") as f:
f.write(image_bytes)
print(f"Saved image to {output_file}")
if __name__ == "__main__":
main()You can use the OpenAI Images API directly via REST without a client library:
import 'dotenv/config';
async function main() {
const apiKey = process.env.AI_GATEWAY_API_KEY;
const baseURL = 'https://ai-gateway.vercel.sh/v1';
// Send POST request to images/generations endpoint
const response = await fetch(`${baseURL}/images/generations`, {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'bfl/flux-2-pro',
prompt: `A playful dolphin pod jumping through ocean waves at sunrise with seabirds flying overhead`,
providerOptions: {
blackForestLabs: { outputFormat: 'jpeg' },
},
n: 3,
}),
});
if (!response.ok) {
throw new Error(`Image generation failed: ${response.status}`);
}
const json = await response.json();
// Images are returned as base64 strings in json.data
for (const image of json.data) {
if (image.b64_json) {
console.log(
'Generated image (base64):',
image.b64_json.substring(0, 50) + '...',
);
}
}
console.log('Generated', json.data.length, 'image(s)');
}
main().catch(console.error);Was this helpful?