# Nano Banana Pro API：Python 调用、改图和价格

> 用 Python 调 Nano Banana Pro：模型 ID、改图字段、1K/2K/4K 怎么选、Google 官方单张价格，以及每次调用返回的 JSON。

- Canonical HTML: https://omniakey.com/zh/blog/nano-banana-pro-api
- Markdown: https://omniakey.com/zh/blog/nano-banana-pro-api.md
- Published: 2026-11-03 (updated 2026-11-03)
- Author: Kiki, COO at OmniaKey
- Language: zh
- Category: API setup
- Reading time: 9 min
- All posts: https://omniakey.com/blog/llms.txt

---

Nano Banana Pro 就是 Google 的 `gemini-3-pro-image`。在 OmniaKey 上一次 `POST /v1/media/tasks` 就能调。我们跑了三张 3:4 的图，像素分别是 896x1200、1792x2400 和 3584x4800。海报上的字一个都没错。

## 先说结论

- **模型：** `gemini-3-pro-image` 是 Google 的 GA 模型 ID。Vertex 模型卡写的发布日期是 2026 年 5 月 28 日。
- **调用：** 向 `POST https://api.omniakey.com/v1/media/tasks` 发请求，带上 `Idempotency-Key`；轮询 `GET /v1/media/tasks/{id}` 到 succeeded，再下载 `assets[0].url`。
- **参数：** `parameters.aspect_ratio` 和 `parameters.image_size`，尺寸值写 1K、2K、4K，K 要大写。
- **请求上限：** 整个请求体 1 MiB，内联 Base64 图片也算在里面。
- **Google 价格：** 1K/2K 每张 $0.134，4K 每张 $0.24，数字来自 Google Gemini API 价格页。
- **我们的 token 数：** 三次任务分别报了 1,133、1,225、2,480 个输出 token。按每百万图片 token $120 算，是 $0.136、$0.147、$0.298。
- **改图：** 用 `operation: "edit"`，源图放进 `input.image`，格式是 `{data, mime_type}`。

OmniaKey 的 [Nano Banana Pro 模型页](https://omniakey.com/zh/gemini-3-pro-image) 也接受 `gemini-3-pro-image-preview` 这个旧快照 ID，价格一样。

## 模型 ID 和要准备的东西

代码里写 `gemini-3-pro-image`。Google 的 [Vertex 模型卡](https://cloud.google.com/vertex-ai/generative-ai/docs/models/gemini/3-pro-image) 把它列为 GA，发布日期 2026 年 5 月 28 日，支持 1K、2K、4K 输出。

`gemini-3-pro-image-preview` 指向同一个模型。OmniaKey 的 [图片模型表](https://docs.omniakey.com/cn/media/image) 把它标成“Same model, -preview snapshot id”，按次收费的价格一样。

准备两样东西：

1. OmniaKey API key，放进环境变量：`export OMNIAKEY_API_KEY="your-key"`。
2. Python 3。下面的脚本只用标准库：urllib、json、time、uuid、base64 和 pathlib，不用装 SDK。

## Python 脚本怎么跑

三步：创建任务、轮询状态、下载图片。保存为 `nano_banana.py`，运行 `python3 nano_banana.py`。

```python
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` 只认 1K、2K、4K。Google 的图片指南写得很明确：“K 必须大写……小写参数（例如 1k）会被拒绝。”写 2K，别写 2k。

`Idempotency-Key` 是必填项。最简单的做法是每次请求生成一个新的 UUID；只有重试同一个请求体时才复用同一个 key。OmniaKey 用 HTTP 200 返回原来的任务，不会重复扣费；同一个 key 换了请求体则返回 409。

创建任务返回 202 和一个 id。OmniaKey 的 [媒体任务文档](https://docs.omniakey.com/cn/media/overview) 写了完整流程。任务完成后，返回的字段就是下面这些：

```json
{
  "id": "task_AbMRVqzSnVh0VwBj0S7NuMUxy5sZtfHP",
  "status": "succeeded",
  "progress": {"percent": 100, "stage": "completed"},
  "usage": {"total_tokens": 1133},
  "assets": [
    {
      "kind": "image",
      "mime_type": "…",
      "url": "https://…signed-url…"
    }
  ]
}
```

拿到 `assets[0].url` 就马上下载：签名链接大约一小时过期，任务记录保留 24 小时。格式看 `mime_type` 字段，Gemini 模型通常返回 JPEG。

同一个脚本换成 1:1、2K，提示词改成中文招牌，得到的是：

![砖墙上的深色金属招牌，三行中文和一行电话号码](https://omniakey.com/_img/blog/nano-banana-pro-api/g2-sign.7e3e8e4d.1600.webp)

_2K、1:1：2048x2048，47.2 秒。三行中文和 010-6789 4321 全对，锈迹盖住了第一个 0 的下半截。_

## 怎么改一张现有的图

用 `operation: "edit"`，源图放进 `input.image`，格式是 `{data, mime_type}`。上面的脚本只要传 `image_path` 就会走这条路：

```python
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",
)
```

实际发出去的请求体是这样：

```json
{
  "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"}
}
```

示例源图是 PNG，所以脚本标的是 `image/png`；换成 JPEG 就改这个值。要放多张参考图，用 `input.images`，每个元素的格式同样是 `{data, mime_type}`。Google 的指南写最多 14 张参考图；`gemini-3-pro-image` 的分配是 6 张物体、5 张人物、3 张风格参考。

请求体要压在 1 MiB 以内。Base64 会让文件大约大三分之一，编码前先把源图缩小或压缩。我们这次改图用的是 2K 海报，海报上的每个字和版式都保住了：

![夜车海报装进黑色画框挂在墙上，上方有画灯，右侧有植物](https://omniakey.com/_img/blog/nano-banana-pro-api/g3-edit.98011b5c.1600.webp)

_2K、1:1 改图：2048x2048，49.9 秒。海报的文字和设计完整保留，画框是规整的矩形。_

## 1K、2K、4K 分别买到什么

同一段 3:4 海报提示词，三个尺寸各跑一次。像素和 Google 的 3:4 表完全对得上：1K 是 896x1200，2K 是 1792x2400，4K 是 3584x4800。三档的字都写对了。

![1K 的夜车海报：石拱桥上的蒸汽火车、雪山和深蓝夜空](https://omniakey.com/_img/blog/nano-banana-pro-api/g1-1k.12bc0ed0.896.webp)

_1K、3:4：896x1200，28.4 秒。海报四行字全对，车灯、车轮和连杆挤在一起。_

![同一张夜车海报的 2K 版，右侧山坡多了小村庄和两座尖塔](https://omniakey.com/_img/blog/nano-banana-pro-api/g1-2k.38a932bb.1600.webp)

_2K、3:4：1792x2400，48.2 秒。文字一样全对，右侧山坡多了小村庄和两座尖塔。_

![同一张夜车海报的 4K 版，车头后面画出三节车厢和煤水车](https://omniakey.com/_img/blog/nano-banana-pro-api/g1-4k.6c8048ad.1600.webp)

_4K、3:4：3584x4800，66.9 秒。三节车厢和煤水车都画了出来，砖缝粗细不一。_

尺寸变大，多出来的是细节：

- **1K：** 车头挤在一起，车灯、车轮和连杆靠得很近。
- **2K：** 右侧山坡出现小村庄和两座尖塔，烟囱冒出一缕浅色烟，石桥能看到 7 个拱洞。
- **4K：** 三节车厢和煤水车分得开，砖缝粗细不一。车底机械仍然简化。

耗时也跟着涨：28.4 秒、48.2 秒和 66.9 秒。每个尺寸只跑了一次，这三个数就是单次结果。

Google 按每百万图片 token $120 计费，表里 1K 和 2K 都是 1,120 token，4K 是 2,000 token。按它的数算，就是 Google 自己的估算：

```text
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
```

我们的任务报的 token 比 Google 举的例子多。同样的算法用我们的数：

```text
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
```

|尺寸|像素（3:4）|我们的耗时|我们的 token|Google 估算|
|-|-|-|-|-|
|1K|896x1200|28.4 秒|1,133|$0.134|
|2K|1792x2400|48.2 秒|1,225|$0.134|
|4K|3584x4800|66.9 秒|2,480|$0.24|

OmniaKey 按次收费，不看 token 数，所以扣费不随上面的 token 变；当前每次的价格在 [模型页](https://omniakey.com/zh/gemini-3-pro-image)，任务失败会退回。充值支持支付宝和微信支付，按人民币直充，一个 key 可以调多家模型。Google 对同样三张图的 token 费率估算是 $0.134、$0.134 和 $0.24。

## 会碰到哪些报错

这里的失败大多是字段名或文件大小的问题，跟模型本身关系不大。OmniaKey 的媒体文档列了下面这几种：

|HTTP|错误码或状态|什么意思|怎么办|
|-|-|-|-|
|400|`invalid_request`|请求体格式错、操作不支持或参数不对|检查 JSON 和下面的字段名|
|503|`no_available_channel`|当前没有渠道服务这个模型|换一个模型；重复调用解决不了|
|—|`failed`|任务结束但没有结果|看 `error.code` 和 `error.message`|

任务 failed 不是 HTTP 错误：创建请求已经成功，所以要轮询任务并读它的 `error` 对象。失败和取消的任务会自动退款。

原图直接转成 Base64 很容易把请求体推过 1 MiB。先缩图，再看编码后的长度，不要只看文件大小。

Google 的指南会拒绝小写 `1k`。写 1K、2K 或 4K。

OmniaKey 的图片文档也会拒绝 Gemini 模型上的 `size` 和顶层的 `mime_type`。这两个不是 Gemini 的参数。改用 `parameters.aspect_ratio` 和 `parameters.image_size`。输出格式从响应的 `mime_type` 字段读。

## 常见问题

### Nano Banana Pro 的 API 模型 ID 是什么？

`gemini-3-pro-image`。Google 的 Vertex 模型卡标为 GA，发布日期 2026 年 5 月 28 日，OmniaKey 用同一个 ID。旧的 `gemini-3-pro-image-preview` 还能用，指向同一个模型。

### 一张 Nano Banana Pro 图片多少钱？

Google 的估算是 1K/2K 每张 $0.134，4K 每张 $0.24，按每百万图片 token $120 算。我们的任务报了 1,133、1,225、2,480 个 token，按同样费率是 $0.136、$0.147、$0.298。OmniaKey 模型页按次标价。

### Nano Banana Pro 能写中文吗？

能。我们那张 2K 招牌回来了三行正确的中文——同福客栈、住宿 餐饮 茶水、010-6789 4321——11 个数字和连字符都对。

### 1K、2K、4K 选哪个？

像我们这张海报，2K 是比较合适的中间档。三个尺寸都把海报字写对了，4K 多的是细节，比如分开的车厢和砖缝，文字正确率不会更高。只要 896x1200 够用，1K 更省时间。

### 改图报 400 invalid\_request 怎么办？

先看三件事：请求体有没有超过 1 MiB、源图有没有放进 `input.image`、有没有给 Gemini 模型传 `size` 或顶层的 `mime_type`。源图写成 `{data, mime_type}`。编码后控制在 1 MiB 以内，参数只用 `aspect_ratio` 和 `image_size`。

## 来源

- [Google Cloud：Gemini 3 Pro Image 模型卡](https://cloud.google.com/vertex-ai/generative-ai/docs/models/gemini/3-pro-image) — 2026 年 11 月 3 日核对。
- [Google：Gemini API 图片生成指南](https://ai.google.dev/gemini-api/docs/image-generation) — 2026 年 11 月 3 日核对。
- [Google：Gemini API 价格](https://ai.google.dev/gemini-api/docs/pricing) — 2026 年 11 月 3 日核对。
- [Google：Nano Banana Pro 发布公告](https://blog.google/technology/ai/nano-banana-pro/) — 2026 年 11 月 3 日核对。

## 更新记录

- 2026-11-03：第一版，当天通过 OmniaKey 跑了 5 次测试。
