Gemini Image Generation
Overview
AIone supports the Gemini family of image generation models through two endpoints:
| Endpoint | Use it for | Capabilities |
|---|---|---|
POST /v1/chat/completions |
Quick integration with your existing OpenAI SDK | Generation, image-to-image, streaming; size control for Nano 2.1 |
POST /v1beta/models/{model}:generateContent |
When you need a specific resolution or aspect ratio | Everything above + resolution / aspect ratio control |
Nano Banana 2.1 supports resolution and aspect ratio control on both endpoints: use image_size / aspect_ratio in Chat, or generationConfig.imageConfig on the native endpoint.
Available models
| Model | Notes |
|---|---|
gemini-nano-banana-2.1 |
Nano Banana 2.1, with 1K / 2K / 4K output |
gemini-3.1-flash-image |
Primary model, balanced speed and quality |
gemini-3-pro-image |
More consistent output, suited to final artwork |
gemini-2.5-flash-image |
Previous-gen Flash, default size only |
For the full model list, refer to
GET /v1/modelsand the Portal model list page.
1. Quick generation: /v1/chat/completions
Minimal request:
curl https://api.aiin1.ai/v1/chat/completions \
-H "Authorization: Bearer sk-nex-your-key-here" \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-3.1-flash-image",
"messages": [
{"role": "user", "content": "Draw a cute cat"}
]
}'Response shape
The image is returned inside message.content as a Markdown-embedded data URI:
{
"id": "chatcmpl-xxx",
"object": "chat.completion",
"model": "gemini-3.1-flash-image",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": ""
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 6,
"completion_tokens": 1120,
"total_tokens": 1126
}
}Extracting the image: message.content is a string — pull the data URI out with a regex.
import re, base64
content = response.choices[0].message.content
images = re.findall(r"data:(image/[\w.+-]+);base64,([A-Za-z0-9+/=]+)", content)
extensions = {"image/png": "png", "image/jpeg": "jpg", "image/webp": "webp"}
for index, (mime_type, encoded) in enumerate(images):
image_bytes = base64.b64decode(encoded)
extension = extensions.get(mime_type, "img")
open(f"out-{index}.{extension}", "wb").write(image_bytes)Images may be returned as PNG, JPEG or another format. Select the filename extension from the returned MIME type.
Python SDK
from openai import OpenAI
client = OpenAI(
api_key="sk-nex-your-key-here",
base_url="https://api.aiin1.ai/v1",
)
resp = client.chat.completions.create(
model="gemini-3.1-flash-image",
messages=[{"role": "user", "content": "Draw a cute cat"}],
)
print(resp.choices[0].message.content[:80])Nano Banana 2.1: choose a resolution
Use the recommended model ID gemini-nano-banana-2.1, or the compatibility alias nano-banana-2.1. Pass the resolution as a parameter; do not add a -2k or -4k suffix to the model name.
curl --max-time 600 https://api.aiin1.ai/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-nano-banana-2.1",
"messages": [
{
"role": "user",
"content": "Draw a cute cat"
}
],
"image_size": "2K",
"aspect_ratio": "1:1"
}'With the OpenAI Python SDK, pass image parameters through extra_body:
resp = client.chat.completions.create(
model="gemini-nano-banana-2.1",
messages=[{"role": "user", "content": "Draw a cute cat"}],
extra_body={"image_size": "2K", "aspect_ratio": "1:1"},
timeout=600,
)For native Gemini requests, use the same model ID and put the size in generationConfig.imageConfig:
curl --max-time 600 https://api.aiin1.ai/v1beta/models/gemini-nano-banana-2.1:generateContent \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contents": [
{
"role": "user",
"parts": [
{
"text": "Draw a cute cat"
}
]
}
],
"generationConfig": {
"responseModalities": [
"IMAGE"
],
"imageConfig": {
"imageSize": "2K",
"aspectRatio": "1:1"
}
}
}'A response may contain both a preview and the original image. Extract all images and check their dimensions; saving only the first data URI may save a lower-resolution preview. Set a timeout of at least 600 seconds for high-resolution or queued requests. The gateway waits for the result; clients do not need a separate polling step.
Image-to-image
Pass the reference image with the standard OpenAI multimodal content array:
{
"model": "gemini-3.1-flash-image",
"messages": [
{
"role": "user",
"content": [
{"type": "text", "text": "Replace the apple in this picture with an orange"},
{
"type": "image_url",
"image_url": {"url": "data:image/png;base64,iVBORw0KGgoAAA..."}
}
]
}
]
}Reference images may be a data: URI (base64) or a public https:// URL. base64 is recommended: some CDNs apply hotlink protection or format conversion, so a direct link may not be retrievable.
Streaming
"stream": true is supported. This is pseudo-streaming — the finished image is delivered in one SSE event once generation completes, not token by token. The benefit is that data keeps flowing on the connection, so intermediate network devices are less likely to drop it as idle.
curl https://api.aiin1.ai/v1/chat/completions \
-H "Authorization: Bearer sk-nex-your-key-here" \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-3.1-flash-image",
"messages": [{"role": "user", "content": "Draw a cute cat"}],
"stream": true
}'Image size on this endpoint
For Nano Banana 2.1, set the top-level image_size to 1K (default), 2K, or 4K, and set aspect_ratio to a ratio such as 1:1. Size controls for other image models depend on their capabilities; native parameters are described below. Use the returned image to determine the actual pixel dimensions.
2. Controlling resolution and aspect ratio: the /v1beta native endpoint
POST https://api.aiin1.ai/v1beta/models/{model}:generateContent
The model name goes in the URL path; size parameters go in generationConfig.imageConfig:
curl https://api.aiin1.ai/v1beta/models/gemini-3.1-flash-image:generateContent \
-H "Authorization: Bearer sk-nex-your-key-here" \
-H "Content-Type: application/json" \
-d '{
"contents": [
{"role": "user", "parts": [{"text": "Draw a cute cat"}]}
],
"generationConfig": {
"responseModalities": ["IMAGE"],
"imageConfig": {
"imageSize": "4K",
"aspectRatio": "16:9"
}
}
}'imageSize — resolution tier
Nano Banana 2.1 supports only
1K/2K/4K, not512. The pixel examples below are for Gemini 3.1 Flash Image; determine Nano 2.1 dimensions from the returned image.
Values: 512 / 1K (default) / 2K / 4K. Actual pixel dimensions at 16:9:
imageSize |
Pixels (16:9) | Area |
|---|---|---|
| omitted (default 1K) | 1376×768 | 1.06 MP |
"2K" |
2752×1536 | 4.23 MP |
"4K" |
5504×3072 | 16.9 MP |
"512" |
688×384 | 0.26 MP |
gemini-2.5-flash-image outputs the default size only (1024×1024) and ignores imageSize.
aspectRatio — aspect ratio
14 values are supported. Actual pixels at the default tier (1K):
| Ratio | Pixels | Ratio | Pixels |
|---|---|---|---|
1:1 |
1024×1024 | 9:16 |
768×1376 |
3:2 |
1264×848 | 16:9 |
1376×768 |
2:3 |
848×1264 | 21:9 |
1584×672 |
4:3 |
1200×896 | 1:4 |
512×2064 |
3:4 |
896×1200 | 4:1 |
2064×512 |
5:4 |
1152×928 | 1:8 |
352×2928 |
4:5 |
928×1152 | 8:1 |
2928×352 |
imageSize and aspectRatio combine freely — e.g. 4K + 21:9 gives an ultra-wide high-resolution image.
The model renders at the native resolution of the chosen tier and ratio; take the actual pixel size from the returned image.
Response shape
The image is in candidates[0].content.parts[] under inlineData; data is raw base64 with no prefix:
{
"candidates": [
{
"content": {
"parts": [
{
"inlineData": {
"mimeType": "image/png",
"data": "iVBORw0KGgoAAA..."
}
}
]
},
"finishReason": "STOP"
}
],
"usageMetadata": {
"promptTokenCount": 6,
"candidatesTokenCount": 1120,
"totalTokenCount": 1126,
"candidatesTokensDetails": [{"modality": "IMAGE", "tokenCount": 1120}]
}
}import base64, json, requests
resp = requests.post(
"https://api.aiin1.ai/v1beta/models/gemini-3.1-flash-image:generateContent",
headers={"Authorization": "Bearer sk-nex-your-key-here"},
json={
"contents": [{"role": "user", "parts": [{"text": "Draw a cute cat"}]}],
"generationConfig": {
"responseModalities": ["IMAGE"],
"imageConfig": {"imageSize": "2K", "aspectRatio": "16:9"},
},
},
timeout=600,
)
for index, part in enumerate(resp.json()["candidates"][0]["content"]["parts"]):
if "inlineData" in part:
inline = part["inlineData"]
extension = {"image/png": "png", "image/jpeg": "jpg", "image/webp": "webp"}.get(
inline.get("mimeType"), "img"
)
open(f"out-{index}.{extension}", "wb").write(base64.b64decode(inline["data"]))
partsmay also contain non-image entries such asthoughtSignature; when iterating, check for the presence ofinlineData.
Native streaming generation and editing
Use the same request body with /v1beta/models/{model}:streamGenerateContent?alt=sse. The response is SSE; images in each JSON event are still under candidates[].content.parts[].inlineData.
4K generation may take several minutes. Allow a 600-second client timeout; native SSE or the Chat streaming mode described above keeps the connection active while waiting.
Image-to-image (native endpoint)
Put the reference image in parts as inlineData, alongside the text. data is raw base64 without the data:image/png;base64, prefix:
{
"contents": [
{
"role": "user",
"parts": [
{"inlineData": {"mimeType": "image/png", "data": "iVBORw0KGgoAAA..."}},
{"text": "Replace the apple in this picture with an orange"}
]
}
],
"generationConfig": {
"responseModalities": ["IMAGE"],
"imageConfig": {"aspectRatio": "1:1"}
}
}Add several inlineData parts to blend multiple images.
Billing
Image output is billed per image output token. The token count is determined by the resolution tier, independent of prompt length:
| Tier | gemini-3.1-flash-image |
gemini-3-pro-image |
gemini-nano-banana-2.1 |
|---|---|---|---|
| 512 | 747 | — | — |
| default / 1K | 1,120 | 1,120 | 1,120 |
| 2K | 1,680 | 1,120 | 1,680 |
| 4K | 2,520 | 2,000 | 3,780 |
gemini-2.5-flash-image is 1,290 tokens per image.
The image portion follows the table above. usage.completion_tokens (chat endpoint) and usageMetadata.candidatesTokenCount (native endpoint) are the total output tokens for the request and may include a small number of text tokens besides the image; each portion is priced at its own rate. See "Pricing" for unit prices.
Nano 2.1 uses its own image output tiers above. Text and thinking tokens use the text output rate. When both a preview and an original are returned, do not multiply image tokens solely by the number of image entries; use the bill for the exact charge.
Request parameters
/v1/chat/completions
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Image model ID |
messages |
array | Yes | Standard OpenAI Chat message array; pass reference images as image_url parts |
stream |
boolean | No | Pseudo-streaming; the image is delivered in one SSE event after generation |
image_size |
string | No | Nano 2.1: 1K (default) / 2K / 4K |
aspect_ratio |
string | No | Nano 2.1 aspect ratio, e.g. 1:1 |
temperature |
number | No | Sampling temperature |
/v1beta/models/{model}:generateContent
| Parameter | Type | Required | Description |
|---|---|---|---|
contents |
array | Yes | Conversation content; parts holds text and inlineData |
generationConfig.responseModalities |
array | No | Recommended: ["IMAGE"] |
generationConfig.imageConfig.imageSize |
string | No | 512 / 1K / 2K / 4K |
generationConfig.imageConfig.aspectRatio |
string | No | One of the 14 values above |
Notes
- Timeouts: high-resolution generation takes longer; set your client HTTP timeout to ≥ 600 seconds.
- Image extraction differs by endpoint: on the chat endpoint, extract the Markdown data URI from
message.content; on the native endpoint, readparts[].inlineData.data(raw base64, no prefix). - Reference image format: the chat endpoint takes a full data URI with the
data:prefix; the native endpoint takes raw base64 without it. The two rules are opposite — do not mix them up. - No Anthropic format: Gemini image models are not available on
/v1/messages. - Model access: make sure your API key is allowed to use the image model.