google/nano-banana-2.1/edit8 积分起 /张· 编辑图片Nano Banana 2.1 是 Google 最新的图像模型,画面里的字必须写对时就选它:加引号的标题、封面文案、招牌都能一字不差地出现。它还能在多次修改中稳住同一张脸或同一件商品,一次融合最多 14 张照片,支持 1K、2K、4K 输出,比例从 1:8 的竖长条到 8:1 的横幅都能出。
在 POST /jobs/createTask 里把模型名写成 google/nano-banana-2.1/edit 即可调用,8 积分/张起(US$0.08)。新账户送 30 积分,可以先在 Playground 里免费试。
- 生成图片
- 编辑图片
- 最高 4K
- 最多 14 张参考图
- 14 种画幅
google/nano-banana-2.1/editPOST https://api.popnow.ai/api/v1/jobs/createTask# 1. Create the task. Keep the Idempotency-Key if you retry after a network error.
export IDEMPOTENCY_KEY="$(uuidgen)"
curl -X POST https://api.popnow.ai/api/v1/jobs/createTask \
-H "Authorization: Bearer $POPNOW_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $IDEMPOTENCY_KEY" \
-d '{
"model": "google/nano-banana-2.1/edit",
"input": {
"prompt": "A ceramic teapot on a linen cloth, soft window light, muted palette",
"image_urls": [
"https://media.popnow.ai/influencer/examples/marco.webp"
],
"aspect_ratio": "auto",
"resolution": "1k",
"output_format": "png"
}
}'
# 2. Copy data.taskId from the response, then poll until state is succeeded, failed, canceled or expired.
TASK_ID="TASK_ID_FROM_RESPONSE"
curl "https://api.popnow.ai/api/v1/jobs/recordInfo?taskId=$TASK_ID" \
-H "Authorization: Bearer $POPNOW_API_KEY"// Node 18+ (or any runtime with fetch). Keep the API key on your server.
const BASE = "https://api.popnow.ai/api/v1";
const headers = { Authorization: `Bearer ${process.env.POPNOW_API_KEY}` };
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function call(path, init = {}) {
const res = await fetch(BASE + path, { ...init, headers: { ...headers, ...init.headers } });
const body = await res.json();
if (body.code !== 200) throw new Error(`${body.code} ${body.msg}`);
return body.data;
}
const { taskId } = await call("/jobs/createTask", {
method: "POST",
headers: { "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID() },
body: JSON.stringify({
"model": "google/nano-banana-2.1/edit",
"input": {
"prompt": "A ceramic teapot on a linen cloth, soft window light, muted palette",
"image_urls": [
"https://media.popnow.ai/influencer/examples/marco.webp"
],
"aspect_ratio": "auto",
"resolution": "1k",
"output_format": "png"
}
}),
});
// Poll with backoff. A finished task may still be copying files: wait while an asset is pending.
let task;
for (let delay = 2000; ; delay = Math.min(delay * 1.5, 15000)) {
await sleep(delay);
task = await call(`/jobs/recordInfo?taskId=${taskId}`);
const copying = (task.output?.assets ?? []).some((a) => a.pending && !a.unavailable);
if (["succeeded", "failed", "canceled", "expired"].includes(task.state) && !copying) break;
}
if (task.state !== "succeeded") throw new Error(`${task.state}: ${task.errorMessage ?? "no detail"}`);
// Each asset has a signed url (no API key needed) that expires at expiresAt: save the file.
for (const asset of task.output.assets) console.log(asset.url);import os, time, uuid, requests
BASE = "https://api.popnow.ai/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['POPNOW_API_KEY']}"}
def call(method, path, **kwargs):
body = requests.request(method, BASE + path, headers={**HEADERS, **kwargs.pop("headers", {})}, timeout=30, **kwargs).json()
if body["code"] != 200:
raise RuntimeError(f'{body["code"]} {body["msg"]}')
return body["data"]
task_id = call(
"POST",
"/jobs/createTask",
headers={"Idempotency-Key": str(uuid.uuid4())}, # reuse it if you retry
json={
"model": "google/nano-banana-2.1/edit",
"input": {
"prompt": "A ceramic teapot on a linen cloth, soft window light, muted palette",
"image_urls": [
"https://media.popnow.ai/influencer/examples/marco.webp"
],
"aspect_ratio": "auto",
"resolution": "1k",
"output_format": "png"
}
},
)["taskId"]
# Poll with backoff. A finished task may still be copying files: wait while an asset is pending.
delay = 2.0
while True:
time.sleep(delay)
task = call("GET", "/jobs/recordInfo", params={"taskId": task_id})
assets = (task.get("output") or {}).get("assets") or []
copying = any(a.get("pending") and not a.get("unavailable") for a in assets)
if task["state"] in ("succeeded", "failed", "canceled", "expired") and not copying:
break
delay = min(delay * 1.5, 15.0)
if task["state"] != "succeeded":
raise RuntimeError(f'{task["state"]}: {task.get("errorMessage", "no detail")}')
# Each asset has a signed url (no API key needed) that expires at expiresAt: save the file.
for asset in assets:
print(asset["url"])input 按下面的 Schema 生成,原样就能通过校验。把提示词和链接换成你自己的即可。
价格
| 选项 | 积分 /张 | ≈ 美元 |
|---|---|---|
resolution 1K | 7.2 | US$0.072 |
resolution 2K | 10.8 | US$0.11 |
resolution 4K | 24.3 | US$0.24 |
1 积分 = $0.01 · 全部选项及价格:GET /models/google/nano-banana-2.1/edit
每个任务按整数积分向上取整,所以最低档跑一次是 8 积分/张(US$0.08)。
限制
- 分辨率
- 1K · 2K · 4K
- 画面比例
- auto · 1:1 · 3:2 · 2:3 · 3:4 · 4:3 +9
- 参考图
- 14
- 提示词长度(字符)
- 5,000
- 单个上传上限
- 10 MB
参数
这些字段来自我们校验每个请求所用的同一份 Schema。没列出的字段会被拒绝。
promptstring必填Say what to change and what must stay the same.
image_urlsarray<string>必填影响价格要编辑或合成的照片。在提示词里说明每张的作用。
aspect_ratiostring可选画面形状:横版、方形或竖版。
resolutionstring可选影响价格输出尺寸。越大越清晰,价格也越高。
enable_web_searchboolean可选影响价格先联网查日期、地点和最新信息。
enable_image_searchboolean可选影响价格先搜索地点或商品的真实图片。
output_formatstring可选结果的文件格式。
原始 JSON Schema
{
"additionalProperties": false,
"properties": {
"aspect_ratio": {
"default": "auto",
"description": "Output aspect ratio; auto keeps the shape of the source image.",
"enum": [
"auto",
"1:1",
"3:2",
"2:3",
"3:4",
"4:3",
"4:5",
"5:4",
"9:16",
"16:9",
"21:9",
"1:4",
"4:1",
"1:8",
"8:1"
],
"type": "string"
},
"enable_image_search": {
"default": false,
"description": "Look up reference pictures on the web before drawing.",
"type": "boolean"
},
"enable_web_search": {
"default": false,
"description": "Look up current facts on the web before drawing.",
"type": "boolean"
},
"image_urls": {
"description": "One to 14 source images, in the order your prompt refers to them.",
"items": {
"contentMediaType": [
"image/jpeg",
"image/png",
"image/webp",
"image/gif"
],
"format": "uri",
"minLength": 1,
"type": "string"
},
"maxItems": 14,
"minItems": 1,
"type": "array"
},
"output_format": {
"default": "png",
"description": "File format of the result.",
"enum": [
"png",
"jpeg"
],
"type": "string"
},
"prompt": {
"description": "Say what to change and what must stay the same.",
"maxLength": 5000,
"minLength": 1,
"type": "string"
},
"resolution": {
"default": "1k",
"description": "Output size tier.",
"enum": [
"1k",
"2k",
"4k"
],
"type": "string"
}
},
"required": [
"prompt",
"image_urls"
],
"type": "object"
}Nano Banana 2.1 的真实成片

Design the blank album sleeve she is holding: a moody duotone photograph of ocean waves at night in deep blue and rust, with the title "MIDNIGHT TIDE" in large bold sans serif and the band name "The Low Hours" in small capitals beneath it. Keep both women, their clothes, their pose and the record store exactly as they are.
{
"model": "google/nano-banana-2.1/edit",
"input": {
"prompt": "Design the blank album sleeve she is holding: a moody duotone photograph of ocean waves at night in deep blue and rust, with the title \"MIDNIGHT TIDE\" in large bold sans serif and the band name \"The Low Hours\" in small capitals beneath it. Keep both women, their clothes, their pose and the record store exactly as they are.",
"image_urls": [
"https://media.popnow.ai/influencer/examples/marco.webp"
],
"aspect_ratio": "auto",
"resolution": "2k",
"output_format": "png"
}
}这条是从上传的文件开始的:把示例链接换成你自己的文件链接。
响应、轮询与回调
所有模型的用法都一样:先建任务,再轮询,或者等回调。
先报价
curl -X POST https://api.popnow.ai/api/v1/jobs/quote \
-H "Authorization: Bearer $POPNOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "google/nano-banana-2.1/edit",
"input": {
"prompt": "A ceramic teapot on a linen cloth, soft window light, muted palette",
"image_urls": [
"https://media.popnow.ai/influencer/examples/marco.webp"
],
"aspect_ratio": "auto",
"resolution": "1k",
"output_format": "png"
}
}'请求体和 createTask 相同。它返回预计扣费和最高可能扣费,不会真正运行。
已受理
HTTP/1.1 202 Accepted
{
"code": 200,
"msg": "success",
"data": {
"taskId": "task_01k3m8x9q2z4v7n5p6r8s0t1w3",
"state": "queued"
},
"request_id": "req_example_create"
}HTTP 202 表示任务已受理,积分先冻结、还没有扣。最终扣费不会超过冻结额。
查询状态
curl "https://api.popnow.ai/api/v1/jobs/recordInfo?taskId=$TASK_ID" \
-H "Authorization: Bearer $POPNOW_API_KEY"每隔几秒查一次并逐步拉长间隔,state 变成 succeeded、failed、canceled 或 expired 时停止。
完成的任务
{
"code": 200,
"msg": "success",
"data": {
"taskId": "task_01k3m8x9q2z4v7n5p6r8s0t1w3",
"model": "google/nano-banana-2.1/edit",
"state": "succeeded",
"output": {
"assets": [
{
"url": "https://cdn.popnow.ai/tasks/task_01k3m8x9q2z4v7n5p6r8s0t1w3/output.png?signature=FROM_RESPONSE",
"expiresAt": "2026-10-17T12:20:00Z",
"mime": "image/png",
"width": 1024,
"height": 1024
}
]
},
"settled": true
},
"request_id": "req_example_record"
}在 expiresAt 之前从 url 下载。之后要新链接,请调用 POST /common/download-url。
校验回调
import { createHash, createHmac, timingSafeEqual } from "node:crypto";
import express from "express";
const app = express();
// express.raw keeps the exact bytes that were signed; express.json would not match.
app.post("/hooks/popnow", express.raw({ type: "application/json" }), (req, res) => {
const timestamp = req.header("X-Webhook-Timestamp");
const signature = req.header("X-Webhook-Signature") ?? "";
const body = JSON.parse(req.body.toString("utf8"));
const taskId = body.data?.taskId ?? body.task_id;
const digest = createHash("sha256").update(req.body).digest("hex");
const expected = createHmac("sha256", process.env.POPNOW_WEBHOOK_SECRET)
.update(`${taskId}.${timestamp}.${digest}`)
.digest("base64");
const ok = expected.length === signature.length && timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
// Check the signature first, then reject anything older than five minutes.
if (!ok || Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return res.status(401).end();
res.status(200).end(); // answer fast, then fetch the task and store its files
});设置了 callBackUrl,我们会把完成的任务发给你。先用原始请求体校验签名,再信任它。
上传本地文件
curl -X POST https://api.popnow.ai/api/v1/common/upload-url \
-H "Authorization: Bearer $POPNOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{"contentType": "image/png", "bytes": 482113}'
# PUT the file bytes to data.uploadUrl with every header in data.headers, then commit it:
curl -X POST https://api.popnow.ai/api/v1/files/$FILE_ID/commit \
-H "Authorization: Bearer $POPNOW_API_KEY"
# The commit returns a popnow://f/... reference. Put it in any image, video or
# audio field of the task input instead of a public https URL.公开的 https 链接可以直接用。本地文件要先经过 upload-url 和 commit。

