Gemini 3.1 Flash Image API
The image model gemini-3.1-flash-image (Nano Banana 2) uses the Gemini native protocol and does not use the OpenAI Images endpoints:
- Base URL:
https://api.teamorouter.com - Endpoint:
POST https://api.teamorouter.com/v1beta/models/{model}:generateContent - Authentication:
Authorization: Bearer sk-teamo-xxxxxx
Query GET /v1/models first to confirm the model is available. Treat the live response as the source of truth for available models and routing.
1. Generate an image
curl https://api.teamorouter.com/v1beta/models/gemini-3.1-flash-image:generateContent \
-H "Authorization: Bearer sk-teamo-xxxxxx" \
-H "content-type: application/json" \
-d '{
"contents": [
{"role": "user", "parts": [{"text": "A minimal tech poster, 16:9"}]}
],
"generationConfig": {
"responseModalities": ["IMAGE"],
"imageConfig": {"aspectRatio": "16:9", "imageSize": "2K"}
}
}'
The image comes back as Base64 in candidates[0].content.parts[].inlineData:
{
"candidates": [
{
"content": {
"parts": [
{
"inlineData": {
"mimeType": "image/png",
"data": "iVBORw0KGgo..."
}
}
]
}
}
]
}
Iterate over every parts entry to find inlineData; never assume the image is the first part, and never log the full Base64 payload.
2. Request parameters
| Field | Common values | Description |
|---|---|---|
contents[].role |
user |
Message role |
contents[].parts[].text |
Prompt string | Subject, style, composition, text, and constraints |
contents[].parts[].inlineData |
mimeType + Base64 data |
Source image when editing |
generationConfig.responseModalities |
["IMAGE"] or ["TEXT", "IMAGE"] |
Return image, text, or both |
generationConfig.imageConfig.aspectRatio |
1:1, 16:9, 9:16, and more |
Output aspect ratio |
generationConfig.imageConfig.imageSize |
512, 1K, 2K, 4K |
Output resolution, defaults to 1K |
Resolution tiers
The sizes below use 1:1; other aspect ratios produce the corresponding non-square size:
imageSize |
1:1 output | Image output tokens (approx.) | Recommended for |
|---|---|---|---|
512 |
512 × 512 | 747 | Previews, drafts, low-cost batch tests |
1K |
1024 × 1024 | 1120 | Default tier, social posts, product shots |
2K |
2048 × 2048 | 1680 | Posters, detail pages, text-heavy images |
4K |
4096 × 4096 | 2520 | High-resolution delivery, print, later cropping |
Supported aspect ratios: 1:1, 1:4, 1:8, 2:3, 3:2, 3:4, 4:1, 4:3, 4:5, 5:4, 8:1, 9:16, 16:9, 21:9.
Use the uppercase 1K, 2K, 4K values in production code. The gateway accepts lowercase 2k in testing, but do not rely on it.
3. Python
pip install requests
import base64
import os
from pathlib import Path
import requests
BASE_URL = "https://api.teamorouter.com"
API_KEY = os.environ["TEAMO_API_KEY"]
MODEL = "gemini-3.1-flash-image"
response = requests.post(
f"{BASE_URL}/v1beta/models/{MODEL}:generateContent",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"contents": [
{
"role": "user",
"parts": [{"text": "A minimal tech poster, 16:9"}],
}
],
"generationConfig": {
"responseModalities": ["IMAGE"],
"imageConfig": {"aspectRatio": "16:9", "imageSize": "2K"},
},
},
timeout=300,
)
response.raise_for_status()
result = response.json()
parts = result["candidates"][0]["content"]["parts"]
image_part = next(part for part in parts if "inlineData" in part)
image_bytes = base64.b64decode(image_part["inlineData"]["data"])
Path("image.png").write_bytes(image_bytes)
4. JavaScript / TypeScript
Node.js 18+ can use fetch directly:
import fs from "node:fs";
const baseURL = "https://api.teamorouter.com";
const apiKey = process.env.TEAMO_API_KEY;
const model = "gemini-3.1-flash-image";
const response = await fetch(
`${baseURL}/v1beta/models/${model}:generateContent`,
{
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"content-type": "application/json",
},
body: JSON.stringify({
contents: [
{
role: "user",
parts: [{ text: "A minimal tech poster, 16:9" }],
},
],
generationConfig: {
responseModalities: ["IMAGE"],
imageConfig: { aspectRatio: "16:9", imageSize: "2K" },
},
}),
signal: AbortSignal.timeout(300_000),
},
);
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);
const result = await response.json();
const parts = result.candidates?.[0]?.content?.parts ?? [];
const image = parts.find((part) => part.inlineData?.data);
if (!image) throw new Error("No image part in the response");
fs.writeFileSync("image.png", Buffer.from(image.inlineData.data, "base64"));
5. Edit an image
Editing uses the same endpoint. Pass the instruction and the Base64 source image together in parts:
{
"contents": [
{
"role": "user",
"parts": [
{"text": "Keep the subject and composition, change the background to a sunset beach, add no text"},
{
"inlineData": {
"mimeType": "image/png",
"data": "<BASE64_IMAGE>"
}
}
]
}
],
"generationConfig": {
"responseModalities": ["IMAGE"],
"imageConfig": {"imageSize": "2K"}
}
}
6. Production guidance
- Call
/v1/modelsat startup or on a schedule instead of relying on a static model table, and only surface models present in the live list. - Image generation is slow; set client timeouts to 300 seconds.
- Only retry 429 and recoverable 5xx responses with exponential backoff. A 400 usually means a request or routing problem, so retrying will not help.
- Keep API keys in server-side environment variables or a secrets manager. Never ship them to the browser, store them in a database, or write them to logs.
- Validate the HTTP status,
candidates,finishReason,inlineData.mimeType, and the Base64 decoding result. - Cap concurrency, output resolution, reference image count, and daily quota.
7. Common errors
401 / authentication failed
Confirm the header is Authorization: Bearer sk-teamo-xxxxxx, with a complete key and no extra whitespace. If you suspect a leak, delete the key in the console and create a new one immediately.
400 / invalid endpoint format
gemini-3.1-flash-image uses the Gemini native URL https://api.teamorouter.com/v1beta/models/{model}:generateContent. Do not add an extra /v1 prefix.
404 or model not found
Query /v1/models first. If the model is missing from the live list, treat it as not yet enabled on the gateway and stop retrying.
Request succeeds but returns no image
Confirm responseModalities includes IMAGE, iterate over every parts entry for inlineData, and check finishReason plus any safety blocking information.