Nano Banana 2.1
Generate 1–4 images from a prompt, optionally guided by reference images, up to 4k, billed per image.
Nano Banana 2.1 (product nano_banana) generates images from a prompt, optionally guided by up to 14 reference images. It goes up to 4k and can search the web and images while it works. One task makes 1–4 images, billed per image.
Create a task
https://api.nanoapi.ai/v1/tasksUse the shared create endpoint with model: "nano-banana-2.1". It usually responds in under 1 second, and the cost of num_outputs images is frozen within a few seconds. Get the result once the task has finished.
Headers
| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer <API key>, see Authentication. |
Content-Type | Yes | application/json |
Idempotency-Key | No | A unique string for every new task, ideally a UUID (1–255 characters). See Preventing duplicate tasks. |
Body
| Parameter | Type | Required | Description |
|---|---|---|---|
model | string | Yes | nano-banana-2.1. |
prompt | string | Yes | What to generate, at most 8000 characters. Refer to the reference images as @Image1, @Image2…, see Referring to images. |
image_urls | string[] | No | Up to 14 reference images; leave it out to generate from the prompt alone. See Media. |
resolution | string | Yes | 1k, 2k or 4k. Sets the price. |
aspect_ratio | string | Yes | auto, 1:1, 3:2, 2:3, 4:3, 3:4, 4:5, 5:4, 9:16, 16:9, 21:9, 4:1, 1:4, 8:1 or 1:8. auto follows the reference image when there's only one; with several the model decides, not tied to any one of them, and the size may not be in the size table; without reference images it gives 1:1. Pick a ratio when you need a fixed one. |
num_outputs | integer | Yes | How many images, 1–4. All of them are frozen; only the ones generated are charged, see Pricing. |
thinking_level | string | Yes | minimal, medium or high. |
enable_web_search | boolean | No | true turns on web search; off when left out. |
enable_image_search | boolean | No | true turns on image search; off when left out. |
seed | integer | No | Random seed, 0–2147483647. With several images, image i (counting from 0) uses seed + i, so seed + num_outputs - 1 can't be above 2147483647 either. |
webhook_url | string | No | Notified when the task succeeds or fails: https, up to 2048 characters, not a private, loopback or reserved address. See Webhooks. |
Any other parameter, such as the other image models' quality, returns 400 invalid_request, see Errors.
Two 16:9 images at 2k:
curl https://api.nanoapi.ai/v1/tasks \
-H "Authorization: Bearer $NANOAPI_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 8d3f1b6a-2c4e-4f7a-9b1d-6e0a5c3f2b84" \
-d '{
"model": "nano-banana-2.1",
"prompt": "A watercolor fox in a snowy forest",
"resolution": "2k",
"aspect_ratio": "16:9",
"thinking_level": "medium",
"num_outputs": 2
}'Response: 200 OK. data is the newly created task, with only these fields; each one is described in Create a task: Response. Keep data.id to get the result. The task can still fail after it's created (for example if your balance can't cover it), so go by what retrieving the task or the webhook tells you.
{
"code": 200,
"message": "success",
"data": {
"object": "task",
"id": "tk_01jbh4n7c2x5v8b3m6q9w1e4r7",
"model": "nano-banana-2.1",
"status": "queued",
"error": null,
"created_at": "2026-11-01T08:30:00Z"
}
}Get the result
As a rough guide (generation only, not counting the queue, plus a few seconds to store the files; slower when the provider is busy): about 25 seconds at 1k, 35 seconds at 2k and 1 minute at 4k, occasionally over 2 minutes. Web and image search don't make it noticeably slower. The images of a task are generated in parallel, so several take about as long as one. Once the task has finished (succeeded or failed), get the result either way:
- Webhook (recommended in production): if you passed
webhook_url, the full task is pushed to it once when the task finishes. See Webhooks. - Polling: call
GET /v1/tasks/{task_id}withdata.idat most once every 15 seconds untilstatusissucceededorfailed. Check several tasks at once withidsin List tasks.
The request above, once it has succeeded (other fields left out):
{
"status": "succeeded",
"input": {
"prompt": "A watercolor fox in a snowy forest",
"resolution": "2k",
"aspect_ratio": "16:9",
"thinking_level": "medium"
},
"num_outputs": 2,
"outputs": [
{
"index": 0,
"type": "image",
"url": "https://cdn.nanoapi.ai/h/tk_01jbh4n7c2x5v8b3m6q9w1e4r7_1_1793521800.png",
"preview_url": "https://cdn.nanoapi.ai/h/tk_01jbh4n7c2x5v8b3m6q9w1e4r7_1_1793521800_compressed.jpg"
},
{
"index": 1,
"type": "image",
"url": "https://cdn.nanoapi.ai/h/tk_01jbh4n7c2x5v8b3m6q9w1e4r7_2_1793521800.png",
"preview_url": "https://cdn.nanoapi.ai/h/tk_01jbh4n7c2x5v8b3m6q9w1e4r7_2_1793521800_compressed.jpg"
}
],
"billing": {
"currency": "USD",
"frozen": "…", // 2 × the 2k price per image
"charged": "…", // the same: both images were generated
"refunded": "0.00",
"billed_seconds": null
}
}| Field | Nano Banana 2.1 |
|---|---|
outputs | One item per generated image, at most num_outputs. |
outputs[].type | image. |
outputs[].url | The original image, a PNG. Use it to download or show the image full size; files are large (roughly 1–2 MB at 1k, around 30 MB at 4k). |
outputs[].preview_url | A JPEG preview, at most 1024 pixels on the long side (smaller images aren't enlarged). Use it for lists and thumbnails. null if it couldn't be made. |
billing.billed_seconds | Always null: images are billed per image. |
input | The parameters you sent; the optional image_urls, enable_web_search, enable_image_search and seed only if you sent them. |
On failure the frozen amount is returned in full and the reason is in data.error.type, see Errors. Every field is described in The task object.
Image sizes
Width × height in pixels; 2k and 4k are exactly 2 and 4 times 1k. The size of auto depends on the reference images and isn't in the table.
aspect_ratio | 1k | 2k | 4k |
|---|---|---|---|
1:1 | 1024×1024 | 2048×2048 | 4096×4096 |
3:2 | 1264×848 | 2528×1696 | 5056×3392 |
2:3 | 848×1264 | 1696×2528 | 3392×5056 |
4:3 | 1200×896 | 2400×1792 | 4800×3584 |
3:4 | 896×1200 | 1792×2400 | 3584×4800 |
5:4 | 1152×928 | 2304×1856 | 4608×3712 |
4:5 | 928×1152 | 1856×2304 | 3712×4608 |
16:9 | 1376×768 | 2752×1536 | 5504×3072 |
9:16 | 768×1376 | 1536×2752 | 3072×5504 |
21:9 | 1584×672 | 3168×1344 | 6336×2688 |
4:1 | 2064×512 | 4128×1024 | 8256×2048 |
1:4 | 512×2064 | 1024×4128 | 2048×8256 |
8:1 | 2928×352 | 5856×704 | 11712×1408 |
1:8 | 352×2928 | 704×5856 | 1408×11712 |
Fewer images than requested
Each image is generated on its own. If some can't be made (for example because moderation blocked them), the task still succeeded: outputs only has the images that were generated, fewer than num_outputs. Only those are charged and the rest of the frozen amount is returned (billing.refunded). The task fails only when no image could be generated, and then everything frozen is returned: if at least one of them was blocked by moderation, the reason is content_policy (not every image has to be blocked, the others may have failed for other reasons), otherwise usually generation_failed. outputs[].index numbers the generated images from 0 without gaps, so when there are fewer it doesn't match the position in your request: don't use it to pair images with anything.
Media
Reference images are passed in image_urls as public http(s) URLs. There's no upload endpoint: host the files yourself. URLs are at most 4096 characters, must download directly and can't point to localhost or private, loopback or reserved addresses.
Reference images (image_urls) | |
|---|---|
| Count | 0–14 (limits.max_reference_images in GET /v1/products) |
| Formats | JPEG, PNG, WebP, AVIF, GIF (the first frame), BMP and TIFF. HEIC / HEIF aren't supported: iPhone photos are HEIC by default, so convert them to JPEG first. |
| Transparency | A transparent PNG is flattened onto white before it's used. |
| If there's a problem | Read only when generation runs, after the cost was frozen: if an image can't be downloaded or its format isn't supported, the task fails with invalid_media and the frozen amount is returned in full. |
Keep every link reachable until the task finishes, including links that expire.
Referring to images in the prompt
In prompt | Refers to |
|---|---|
@Image1, @Image2, … | The images in image_urls, in order. |
- A reference beyond the images you sent (for example
@Image2with one image, or any@Imagewithoutimage_urls) returns400 invalid_requestwithparam"prompt", and no task is created. - An
@right after a letter, digit or dot isn't a reference, so an email address such asbob@image2go.comin the prompt is fine.
Pricing
frozen = price per image of the resolution × num_outputs
charged = price per image of the resolution × images generatedLoading the current prices…
- Only
resolutionchanges the price.aspect_ratio,thinking_level, the two search switches,seedand reference images don't. - The cost of all
num_outputsimages is frozen within a few seconds of creating the task. When it succeeds, each generated image is charged and the rest is returned, see Fewer images than requested; when it fails, all of it is returned.
Example: the request above (2k, num_outputs 2) freezes 2 × the 2k price per image and is charged the same when both images are generated; if only one is, it's charged 1 × the 2k price per image and the other is returned. How freezing and charging work in general: Billing.
The price is known from the request, so you can show it before submitting: read the current prices from GET /v1/products (one prices row per resolution). Prices change, so don't hard-code them; billing.frozen on the retrieved task is what counts.
Errors
Errors and failure reasons specific to this model. Every error of the create endpoint: Create a task → Errors; every failure reason: Failure reasons.
Create request errors: the request fails with an HTTP error and no task is created. param names the parameter.
| Status | error.type | When | What to do |
|---|---|---|---|
400 | invalid_request | A required parameter is missing (Field required, param names it) or prompt is empty (prompt is required). | Send every required parameter, see Create a task. |
400 | invalid_request | The prompt refers to an image you didn't send, with param "prompt" (for example prompt references @Image2, but image_urls has 1 image: use @Image1). | Fix the reference, or add the image to image_urls. |
400 | invalid_request | resolution, aspect_ratio or thinking_level isn't one of the values above (for example Input should be '1k', '2k' or '4k' for nano-banana-2.1), num_outputs isn't 1–4, image_urls has more than 14 images, or seed isn't 0–2147483647. | Use an allowed value. |
400 | invalid_request | seed + num_outputs - 1 is above 2147483647, with param "seed" (for example seed must be at most 2147483645 when num_outputs is 3: image i uses seed + i). | Use a smaller seed. |
400 | invalid_request | A parameter this model doesn't take: another image model's (quality, variant, background, remove_bg) or a video model's (Extra inputs are not permitted). | Remove it. |
Other errors of the create endpoint (401, 402, 429 and so on): Create a task → Errors.
Task failures: the create request succeeded (200) and the task later turned failed. There's no HTTP status: the reason is the task's error.type, from retrieving the task or the webhook.
error.type | When | What to do |
|---|---|---|
insufficient_balance | Your available balance can't cover num_outputs × the price per image. Within a few seconds of creating the task, nothing frozen. | Top up, or ask for fewer images, then create the task again. |
spend_limit_exceeded | This task's cost would go over one of the key's spend limits. Within a few seconds of creating the task, nothing frozen. | Raise the limit in Console → API keys, or wait for it to reset, then create the task again. |
invalid_media | A reference image can't be downloaded or read (for example a HEIC photo, or a corrupt file). Found when generation runs, after the cost was frozen: all of it is returned. | Replace the image error.message points to (convert HEIC to JPEG) and create the task again. |
content_policy | No image could be generated, and at least one was blocked because the prompt or a reference image breaks the content policy (not necessarily every one). If only some images are blocked, the task succeeds with the others instead, see Fewer images than requested. | Resubmitting it as is fails again: change the prompt or the images. |
generation_failed | No image could be generated. | Create it again later, or change the prompt or images. |
queue_timeout / internal_error | Not started within 30 minutes / an error on our side. | Create it again later. |
Every failed task has its frozen amount returned in full and billing.charged "0.00".