Image
Nano Banana 2.1 (Gemini Nano Banana 2.1)
Generate and edit images with Nano Banana 2.1 through one async endpoint using Google's generateContent body: 1K to 4K, thinking levels, 14 reference images.
Nano Banana 2.1 is Google's gemini-nano-banana-2.1 image model, the update to Nano Banana 2. Send Google's generateContent request body with a model field, keep the returned task ID, and check that task for the finished image. Reference images go in contents as fileData URLs.
Model IDs
| Model ID | Billing |
|---|---|
gemini-nano-banana-2.1 | One flat price per delivered image, at every size |
See the model page for the current price.
Quick example
curl https://api.seedrouter.ai/v1/images/generations \
-H "Authorization: Bearer $SEEDROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-nano-banana-2.1",
"contents": [{"parts": [{"text": "A ceramic teapot on a linen tablecloth, soft window light"}]}],
"generationConfig": {
"responseModalities": ["IMAGE"],
"imageConfig": {"aspectRatio": "16:9", "imageSize": "2K"}
}
}'Endpoint
POST https://api.seedrouter.ai/v1/images/generations| Header | Value |
|---|---|
| Authorization | Bearer YOUR_API_KEY |
| Content-Type | application/json |
The body is Google's generateContent request with one addition: model, because this endpoint carries no model in its path. The response contains a task ID, not the finished image. Keep API keys in server-side code. Calling /v1beta/models/...:generateContent directly is not supported; use this endpoint.
Parameters
| Name | Type | Required | Default | Notes |
|---|---|---|---|---|
model | string | Yes | — | gemini-nano-banana-2.1. |
contents | Content[] | Yes | — | 1–32 turns. Each has parts and an optional role (user or model); the last turn is user. |
contents[].parts[].text | string | — | — | A text part. At least one text part is required. |
contents[].parts[].fileData | object | No | — | {"mimeType": "...", "fileUri": "https://..."}; an image, video, or PDF reference. Up to 14 in total. |
systemInstruction | object | No | — | {"parts": [{"text": "..."}]}. |
safetySettings | object[] | No | — | {"category", "threshold"} pairs; see below. |
tools | object[] | No | — | [{"googleSearch": {}}] for web search, or {"googleSearch": {"searchTypes": {"webSearch": {}, "imageSearch": {}}}} with either or both types. Grounds the image in live search results; the price per image does not change. |
generationConfig.responseModalities | enum[] | No | text and image | ["IMAGE"] for images only, or ["TEXT", "IMAGE"]. |
generationConfig.imageConfig.aspectRatio | enum | No | Input image ratio, else 1:1 | 1:1, 1:4, 4:1, 1:8, 8:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9. |
generationConfig.imageConfig.imageSize | enum | No | 1K | 1K, 2K, 4K. Uppercase K. 512 is not available for this model. |
generationConfig.candidateCount | integer | No | 1 | Only 1. One request returns one image. |
generationConfig.temperature | number | No | Model default | 0–2. |
generationConfig.topP | number | No | Model default | 0–1. |
generationConfig.topK | integer | No | Model default | 1 or more. |
generationConfig.seed | integer | No | — | 32-bit integer. |
generationConfig.maxOutputTokens | integer | No | Model default | 1–32,768. |
generationConfig.stopSequences | string[] | No | — | Up to 5. |
generationConfig.mediaResolution | enum | No | Model default | MEDIA_RESOLUTION_LOW, MEDIA_RESOLUTION_MEDIUM, MEDIA_RESOLUTION_HIGH. Sets how many tokens input media use. |
generationConfig.thinkingConfig.thinkingLevel | enum | No | MEDIUM | MINIMAL, MEDIUM, HIGH. How much the model reasons before drawing; higher levels take longer. |
generationConfig.thinkingConfig.includeThoughts | boolean | No | false | Return the model's thought summaries as output.thoughts. |
Safety categories: HARM_CATEGORY_HARASSMENT, HARM_CATEGORY_HATE_SPEECH, HARM_CATEGORY_SEXUALLY_EXPLICIT, HARM_CATEGORY_DANGEROUS_CONTENT. Thresholds: BLOCK_NONE, BLOCK_ONLY_HIGH, BLOCK_MEDIUM_AND_ABOVE, BLOCK_LOW_AND_ABOVE, OFF.
Unknown fields are rejected. Not yet available: cached content and responseFormat; set the ratio and size with imageConfig. inlineData is not accepted; pass media as fileData URLs.
Output size
imageSize | 1:1 output | Image tokens |
|---|---|---|
1K | 1024×1024 | 1,120 |
2K | 2048×2048 | 1,680 |
4K | 4096×4096 | 2,520 |
Other aspect ratios keep the same token count; for example 16:9 at 1K is 1376×768, and 21:9 at 4K is 6336×2688.
Modes
There is no separate mode parameter or editing endpoint.
| Operation | Parameters |
|---|---|
| Text to image | a text part |
| Edit or compose | text part + one or more fileData parts |
| Multi-turn edit | earlier user and model turns, then a new user turn (see the note below) |
To continue a conversation, rebuild the model turn from the previous task's output.parts, in order: a text part becomes {"text": ..., "thoughtSignature": ...} and an image part becomes {"fileData": {"mimeType": "image/<output_format>", "fileUri": <data[image].url>}, "thoughtSignature": ...}. Keep each thoughtSignature exactly as returned: it is the URL of the signature we stored for you (a 4K image's signature is several megabytes), and we restore it before the request reaches the model. Only signatures from your own task results are accepted.
Edit with a reference image
curl https://api.seedrouter.ai/v1/images/generations \
-H "Authorization: Bearer $SEEDROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-nano-banana-2.1",
"contents": [{
"role": "user",
"parts": [
{"text": "Turn this photo into a watercolor painting. Keep the composition."},
{"fileData": {"mimeType": "image/jpeg", "fileUri": "https://example.com/photo.jpg"}}
]
}]
}'Replace the example URL with your own accessible image.
Media inputs
This API accepts URL references only. Base64 inlineData, data: URLs, and multipart uploads are not accepted. The Playground uploads selected files to storage before submitting their URLs.
References must be public HTTP(S) URLs, each smaller than 50 MB and 100 MB in total: images (image/png, image/jpeg, image/webp, image/heic, image/heif), videos (video/mp4, video/mpeg, video/mov, video/avi, video/x-flv, video/mpg, video/webm, video/wmv, video/3gpp), or PDF documents (application/pdf). mimeType must match the file. URLs are fetched during processing; an inaccessible image fails the task, and a failed task is not charged.
Pricing dimensions
Check the model pricing section for the current price. gemini-nano-banana-2.1 charges one flat price per delivered image, whatever the size, thinking level, or prompt.
View final charges in your account usage history. Failed tasks are not charged.
Output schema
Submission returns a task reference:
{
"id": "task_...",
"model": "gemini-nano-banana-2.1",
"status": "processing",
"created_at": 1790310979
}Poll the task
curl https://api.seedrouter.ai/v1/tasks/YOUR_TASK_ID \
-H "Authorization: Bearer $SEEDROUTER_API_KEY"Poll every few seconds until status is completed or failed. A network timeout while polling does not mean generation failed: keep the task ID and resume checking it. Do not create another task to check progress.
Complete polling example
Run this after the Python submission example above.
import time
deadline = time.monotonic() + 600
while time.monotonic() < deadline:
result = requests.get(
f"https://api.seedrouter.ai/v1/tasks/{task_id}",
headers={"Authorization": f"Bearer {os.environ['SEEDROUTER_API_KEY']}"},
timeout=30,
)
result.raise_for_status()
task = result.json()
if task["status"] == "completed":
for image in task["output"]["data"]:
print(image["url"])
break
if task["status"] == "failed":
raise RuntimeError(task["error"]["message"])
time.sleep(3)
else:
raise TimeoutError(f"Still waiting. Resume polling task {task_id}.")Completed task
{
"id": "task_...",
"model": "gemini-nano-banana-2.1",
"status": "completed",
"created_at": 1790310979,
"finished_at": 1790311001,
"output": {
"created": 1790310999,
"data": [{"url": "https://static.seedrouter.ai/media/tasks/task_example/0.jpg"}],
"output_format": "jpeg",
"usage": {
"input_tokens": 22,
"output_tokens": 2297,
"total_tokens": 2319
}
}
}| Field | Meaning |
|---|---|
id | Keep this ID for subsequent queries. |
status | processing, completed, or failed. |
created_at, finished_at | Unix timestamps in seconds. |
output.data[].url | The generated image URL. |
output.text | Text the model returned alongside the image, when responseModalities includes TEXT. Thoughts are not included. |
output.thoughts | The model's thought summaries, when includeThoughts is true. Interim images the model draws while thinking are not delivered. |
output.grounding_metadata | With Google Search: webSearchQueries, imageSearchQueries, searchEntryPoint.renderedContent (the Search Suggestions HTML you must display) and groundingChunks (sources), as Google returns them. |
output.output_format | Actual image format. |
output.parts | The final response parts in order, for multi-turn editing: {"text", "thoughtSignature"} or {"image": <index into data>, "thoughtSignature"}. thoughtSignature is a URL; send it back unchanged. |
output.usage | Token usage. output_tokens counts text, thinking, and image output. |
error | Structured error on a failed task. |
Streaming (streamGenerateContent) is not supported; results are delivered through the task.
Errors
Requests rejected before a task is created return an HTTP error with an error object. A task that fails after acceptance returns HTTP 200 when queried, with status: "failed" and an error object. An image withheld by the model's safety filters fails with content_policy_violation; a response with no image fails with no_output.
See the shared error catalog for codes, HTTP statuses, and retry guidance.
{
"id": "task_...",
"status": "failed",
"error": {
"code": 60001,
"message": "The request was rejected by the content policy. Please revise the prompt or input images."
}
}If submission itself times out, check your task history before submitting again: the first request may have been accepted.
Tips
- Describe subject, setting, lighting, and style in full sentences.
- For an edit, say what should change and what must stay the same.
- The price is the same at every size, so ask for the size you need:
1Kfor drafts,2Kor4Kfor final assets. - Put the exact words you want rendered in quotes, and keep them short.
- Use
MINIMALthinking for faster drafts andHIGHfor dense layouts such as infographics. - Save returned images to your own storage when you need a durable copy.
