Nano Banana Pro API: Python Tutorial, Edits and Price
One POST, one poll, one download: a working Nano Banana Pro script.

On this page
- Short answer
- Model id and what you need
- The Python script
- Editing an existing image
- 1K, 2K and 4K: what each buys
- Errors you will hit
- Common questions
- What is the Nano Banana Pro API model id?
- How much does one Nano Banana Pro image cost?
- Can Nano Banana Pro render Chinese text?
- Should I request 1K, 2K or 4K?
- Why did my edit return 400 invalid_request?
- Sources checked
- Update log
Nano Banana Pro is Google's gemini-3-pro-image, and on OmniaKey you call it with one POST /v1/media/tasks. Our three 3:4 tests came back at 896x1200, 1792x2400 and 3584x4800, and every word in the poster was spelled right.
Short answer
- Model:
gemini-3-pro-imageis Google's GA model id. The Vertex model card lists a release date of May 28, 2026. - Call: send
POST https://api.omniakey.com/v1/media/taskswith anIdempotency-Key, pollGET /v1/media/tasks/{id}until the status is succeeded, then downloadassets[0].url. - Parameters:
parameters.aspect_ratioandparameters.image_size; the size values are 1K, 2K and 4K, with an uppercase K. - Request limit: 1 MiB for the whole body, including inline Base64 image data.
- Google's price: $0.134 per 1K/2K image and $0.24 per 4K image, from Google's Gemini API pricing page.
- Our token counts: the three tasks reported 1,133, 1,225 and 2,480 output tokens. At $120 per million image tokens that is $0.136, $0.147 and $0.298.
- Edits: use
operation: "edit"and put the source ininput.imageas{data, mime_type}.
The Nano Banana Pro model page on OmniaKey also accepts gemini-3-pro-image-preview, the older snapshot id, at the same price.
Model id and what you need
gemini-3-pro-image is the id to write in your code. Google's Vertex model card lists it with launch stage GA and release date May 28, 2026. It supports 1K, 2K and 4K output.
The -preview id is the same model, not a separate one. OmniaKey's image model table lists gemini-3-pro-image-preview as "Same model, -preview snapshot id" at the same per-call rate.
You need two things:
- An OmniaKey API key, exported so the script can read it:
export OMNIAKEY_API_KEY="your-key". - Python 3. The script below uses only the standard library: urllib, json, time, uuid, base64 and pathlib. No SDK to install.
The Python script
Three steps: create the task, poll it, download the image. Save this as nano_banana.py and run it with python3 nano_banana.py.
import base64
import json
import os
import time
import urllib.request
import uuid
from pathlib import Path
API = "https://api.omniakey.com/v1/media/tasks"
HEADERS = {
"Authorization": f"Bearer {os.environ['OMNIAKEY_API_KEY']}",
"Content-Type": "application/json",
}
def call(url, payload=None, headers=None, method=None):
body = json.dumps(payload).encode() if payload is not None else None
req = urllib.request.Request(url, data=body, headers=headers or HEADERS, method=method)
with urllib.request.urlopen(req, timeout=60) as resp:
return resp.status, json.loads(resp.read())
def create_task(prompt, aspect_ratio="3:4", image_size="2K", image_path=None):
body = {
"model": "gemini-3-pro-image",
"type": "image",
"operation": "generate" if image_path is None else "edit",
"input": {"prompt": prompt},
"parameters": {"aspect_ratio": aspect_ratio, "image_size": image_size},
}
if image_path:
encoded = base64.b64encode(Path(image_path).read_bytes()).decode("utf-8")
body["input"]["image"] = {"data": encoded, "mime_type": "image/png"}
headers = {**HEADERS, "Idempotency-Key": str(uuid.uuid4())}
status, task = call(API, body, headers, "POST")
print("created", task["id"], "HTTP", status)
return task
def wait(task_id, interval=3, timeout=300):
deadline = time.time() + timeout
while time.time() < deadline:
_, task = call(f"{API}/{task_id}")
print(task["status"], task["progress"]["percent"])
if task["status"] in ("succeeded", "failed", "canceled"):
return task
time.sleep(interval)
# A timeout is not a failure: keep the task id and poll it again.
raise TimeoutError(f"task {task_id} is still running")
def download(task, out_path):
asset = task["assets"][0]
with urllib.request.urlopen(asset["url"], timeout=120) as resp:
Path(out_path).write_bytes(resp.read())
print("saved", out_path, asset["mime_type"], asset["byte_size"], "bytes")
if __name__ == "__main__":
task = create_task(
"A vintage travel poster for the Night Train from Zürich to Milano, "
"departing 21:40 from platform 7. A steam train crosses a stone arch "
"bridge below snowy mountains under a deep blue night sky, with a cream "
"border. Spell every word exactly as written.",
aspect_ratio="3:4",
image_size="2K",
)
task = wait(task["id"])
if task["status"] != "succeeded":
raise SystemExit(f"{task['status']}: {task.get('error')}")
download(task, "night-train.jpg")
image_size takes 1K, 2K or 4K. Google's image guide is explicit: "You must use an uppercase 'K' … Lowercase parameters (e.g., 1k) will be rejected." Send 2K, not 2k.
Idempotency-Key is required. A fresh UUID per request is the simple rule; reuse a key only when you retry the exact same body. OmniaKey returns the original task with HTTP 200 instead of charging twice, and answers 409 if the same key arrives with a different body.
The create call returns 202 with an id. OmniaKey's media task docs describe the full lifecycle; the finished task carries the fields you read:
{
"id": "task_AbMRVqzSnVh0VwBj0S7NuMUxy5sZtfHP",
"status": "succeeded",
"progress": {"percent": 100, "stage": "completed"},
"usage": {"total_tokens": 1133},
"assets": [
{
"kind": "image",
"mime_type": "…",
"url": "https://…signed-url…"
}
]
}
Read assets[0].url and download it right away: the signed link lasts about an hour, and the task record lasts 24 hours. The mime_type field tells you the format; Gemini models usually return JPEG.
The same script at 1:1 and 2K, with a Chinese sign prompt, returned this:

Editing an existing image
Set operation to "edit" and pass the source image in input.image as {data, mime_type}. The script above already does this when you give it an image_path:
task = create_task(
"Hang this poster in a thin black frame on a warm white wall, with a brass "
"picture light above it, a low wooden cabinet below and a trailing plant on "
"the right. Keep the poster exactly as it is.",
aspect_ratio="1:1",
image_size="2K",
image_path="night-train-2k.png",
)
The request body it sends looks like this:
{
"model": "gemini-3-pro-image",
"type": "image",
"operation": "edit",
"input": {
"prompt": "Hang this poster in a thin black frame on a warm white wall…",
"image": {"data": "…base64…", "mime_type": "image/png"}
},
"parameters": {"aspect_ratio": "1:1", "image_size": "2K"}
}
The example source is a PNG, so the script labels it image/png; change that label for a JPEG. For several references, use input.images and give each entry the same {data, mime_type} shape. Google's guide puts the limit at 14 reference images; for gemini-3-pro-image the split is 6 object images, 5 character images and 3 style references.
Keep the body under 1 MiB. Base64 makes the file about a third larger, so resize or compress the source before you encode it. Our edit used the 2K poster and kept every word and the layout intact:

1K, 2K and 4K: what each buys
We ran the same 3:4 poster prompt at all three sizes. The pixel sizes matched Google's table for 3:4 exactly: 896x1200 at 1K, 1792x2400 at 2K and 3584x4800 at 4K. The poster text was correct at every size.



What changes with size is detail, not spelling:
- 1K: the train front is crowded: lamps, wheels and connecting rods sit close together.
- 2K: the village and its two spires appear on the right slope, the chimney gets a pale thread of smoke, and the bridge shows seven arches.
- 4K: the three carriages and the tender are separate, and the stonework shows brick seams of different thickness. The machinery under the locomotive is still simplified.
Speed went up with size: 28.4 s, 48.2 s and 66.9 s. We ran each size once, so read those seconds as a single run.
Google prices image output at $120 per million tokens, and its table gives 1,120 tokens for both 1K and 2K and 2,000 tokens for 4K. That works out to Google's own estimate:
1,120 × $120 ÷ 1,000,000 = $0.134 per 1K image
1,120 × $120 ÷ 1,000,000 = $0.134 per 2K image
2,000 × $120 ÷ 1,000,000 = $0.24 per 4K image
Our tasks reported more tokens than Google's examples. Here is the same arithmetic on our counts:
1,133 × $120 ÷ 1,000,000 = $0.136 per 1K image
1,225 × $120 ÷ 1,000,000 = $0.147 per 2K image
2,480 × $120 ÷ 1,000,000 = $0.298 per 4K image
| Size | Pixels, 3:4 | Our time | Our tokens | Google estimate |
|---|---|---|---|---|
| 1K | 896x1200 | 28.4 s | 1,133 | $0.134 |
| 2K | 1792x2400 | 48.2 s | 1,225 | $0.134 |
| 4K | 3584x4800 | 66.9 s | 2,480 | $0.24 |
OmniaKey bills an image call at a flat rate, not by token, so its charge does not move with those counts; the model page has the current per-call rate, and a failed task is refunded. Google's token-rate estimate for the same three images is $0.134, $0.134 and $0.24.
Errors you will hit
The failures listed here mostly come down to a field name or a file size, not the model. These are the ones OmniaKey's media docs list for this endpoint:
| HTTP | Code or status | What it means | What to do |
|---|---|---|---|
| 400 | invalid_request | Malformed body, unsupported operation or bad parameter | Check the JSON and the field names below |
| 503 | no_available_channel | No channel currently serves this model | Switch models; retrying will not fix it |
| — | failed | The job finished without a result | Read error.code and error.message |
A failed task is not an HTTP error: the create call succeeded, so poll the task and read its error object. Failed and canceled jobs are refunded automatically.
A full-resolution source sent as Base64 can push the body over 1 MiB. Resize first, and check the encoded length, not just the file size.
Google's guide rejects a lowercase 1k. Use 1K, 2K or 4K.
OmniaKey's image docs also reject size and a top-level mime_type on Gemini models, because those are not Gemini parameters. Use parameters.aspect_ratio and parameters.image_size instead, and read the output format from the response's mime_type field.
Common questions
What is the Nano Banana Pro API model id?
gemini-3-pro-image. Google's Vertex model card lists it as GA with a release date of May 28, 2026, and OmniaKey accepts the same id. The older gemini-3-pro-image-preview id still works on OmniaKey and points to the same model.
How much does one Nano Banana Pro image cost?
Google estimates $0.134 for a 1K or 2K image and $0.24 for a 4K image at its $120 per million image-token rate. Our tasks reported 1,133, 1,225 and 2,480 tokens, which comes to $0.136, $0.147 and $0.298 at that rate. OmniaKey's model page lists its own per-call rate.
Can Nano Banana Pro render Chinese text?
Yes. Our 2K sign came back with three correct Chinese lines — 同福客栈, 住宿 餐饮 茶水, and 010-6789 4321 — including all 11 digits and the hyphen.
Should I request 1K, 2K or 4K?
For a poster like ours, 2K is the useful middle. All three sizes spelled our poster correctly, so 4K buys finer detail — separate carriages and brick seams — rather than better text. Use 1K for drafts where 896x1200 is enough.
Why did my edit return 400 invalid_request?
The usual causes are a body over 1 MiB, the source in the wrong field, or a Gemini model receiving size or a top-level mime_type. Put the image in input.image as {data, mime_type}, keep the encoded body under 1 MiB, and use aspect_ratio and image_size.
Sources checked
- Google Cloud: Gemini 3 Pro Image model card — checked November 3, 2026.
- Google: Gemini API image generation guide — checked November 3, 2026.
- Google: Gemini API pricing — checked November 3, 2026.
- Google: Nano Banana Pro announcement — checked November 3, 2026.
Update log
- 2026-11-03: First version; five test calls run through OmniaKey that day.
Models in this article
Related articles
Nano Banana 2.1 Review: API Access & Pricing
Compare Nano Banana 2.1 with Nano Banana 2, then create supported Nano Banana image tasks through OmniaKey.
Nano Banana Product Photography: 8 Prompts Tested
Eight prompts, 22 edits, two models: which shots kept the label, where the shoe got mirrored and which model to pick for each job.
Seedream 5.0 Pro vs Nano Banana Pro: Same-Prompt Test and Price
Five shared prompts, two models, measured times and the price per 2K image.
