要点
开始之前要有一个 API 密钥和一笔余额。发送一次 POST /images/generate 请求,model 填 nano-banana-pro,并保存响应里的 id。再用 GET /creations/{id} 查询状态,频率不要高于每 5 秒一次;状态变为 completed 后,把 result_urls 里的文件保存到你自己的存储,因为文件只保存一天。一张图片在 1K 下为 $0.04,在 2K 下为 $0.04,在 4K 下为 $0.07。失败的请求免费:已扣金额会自动退回余额。
开始之前 需要准备什么?
第一次发送请求前,要做三件事:创建 API 密钥、给余额充值(在控制台的“余额与充值”页面中充值,或通过经理充值),然后写请求。任何能发送 HTTPS 请求的语言都可以。下面的示例用 curl、装了 requests 包的 Python,以及 Node.js 22+ 里的 JavaScript。
API 密钥在控制台里创建。它由前缀 rg_live_ 加 40 个字符组成,完整的密钥只在创建时显示一次:丢失的密钥无法找回,只能再创建一个新的。把它放进服务器的环境变量 RENDERGRID_API_KEY,不要让它进入代码和代码仓库。详见密钥文档。
API 密钥相当于你余额的密码。不要把它写进网站或移动应用的代码里,请求只从服务器端发出。
余额用 USDT 充值:在控制台的“余额与充值”页面中充值,或通过经理充值。支付有自己的时间窗口,细节见充值文档。每张图片的价格在接收请求时从余额里扣除,余额低于图片价格时,API 会返回 402。可以先小额充值:只有生成成功的图片才需要付费。
你的账号能用哪些模型,由 GET /models 返回;所有模型的参数见“模型与参数”。
怎样发送请求?
必填的字段只有两个:model 和 prompt。示例里还写了 resolution 和 aspect_ratio:resolution 选择分辨率,aspect_ratio 字段设置画面的宽高比。写明它们,结果就不取决于默认值。请求头 X-Idempotency-Key 用来避免重复扣费:每生成一张新图片,就换一个新的值;重试同一个请求时,沿用原来的值(见重试不重复扣费)。
示例里的提示词,就是下文三张图片所用的场景:深色纸上的一瓶香水。
# 需要 bash、curl 和 uuidgen。每个请求用一个幂等键。 IDEMPOTENCY_KEY="$(uuidgen)" || exit 1 curl --fail-with-body --max-time 30 \ -X POST https://api.rendergrid.io/ api/ public/ v1/ images/ generate \ -H "Authorization: Bearer $RENDERGRID_API_KEY" \ -H "Content-Type: application/json" \ -H "X-Idempotency-Key: $IDEMPOTENCY_KEY" \ -d '{ "model": "nano-banana-pro", "prompt": "打开的长方形琥珀香水瓶立在折起的炭灰纸上,圆润瓶盖单独放在旁边,瓶口清楚可见。右上方光线形成细金色反射,纸折内有阴影。方形画面中两件物品均完整,留白宽松。不出现文字、标志、人物或水印。", "resolution": "1K", "aspect_ratio": "1:1" }'
import os import uuid import requests API = "https://api.rendergrid.io/ api/ public/ v1" KEY = os.environ["RENDERGRID_API_KEY"] PROMPT = "打开的长方形琥珀香水瓶立在折起的炭灰纸上,圆润瓶盖单独放在旁边,瓶口清楚可见。右上方光线形成细金色反射,纸折内有阴影。方形画面中两件物品均完整,留白宽松。不出现文字、标志、人物或水印。" response = requests.post( f"{API}/images/generate", headers={ "Authorization": f"Bearer {KEY}", # 每张新图片用新的值;重试同一个请求时沿用原来的值 "X-Idempotency-Key": str(uuid.uuid4()), }, json={ "model": "nano-banana-pro", "prompt": PROMPT, "resolution": "1K", "aspect_ratio": "1:1", }, timeout=(10, 120), ) response.raise_for_status() creation = response.json() # 202:{id, status, task_id, cost} print(creation["id"], creation["status"], creation["cost"])
// Node.js 22+,文件扩展名为 .mjs import { randomUUID } from "node:crypto"; const API = "https://api.rendergrid.io/ api/ public/ v1"; const prompt = "打开的长方形琥珀香水瓶立在折起的炭灰纸上,圆润瓶盖单独放在旁边,瓶口清楚可见。右上方光线形成细金色反射,纸折内有阴影。方形画面中两件物品均完整,留白宽松。不出现文字、标志、人物或水印。"; const response = await fetch(`${API}/images/generate`, { method: "POST", headers: { Authorization: `Bearer ${process.env.RENDERGRID_API_KEY}`, "Content-Type": "application/json", // 每张新图片用新的值;重试同一个请求时沿用原来的值 "X-Idempotency-Key": randomUUID(), }, body: JSON.stringify({ model: "nano-banana-pro", prompt, resolution: "1K", aspect_ratio: "1:1", }), signal: AbortSignal.timeout(120_000), }); if (response.status !== 202) { throw new Error(`HTTP ${response.status}: ${await response.text()}`); } // 202 响应:{id, status, task_id, cost} const creation = await response.json(); console.log(creation.id, creation.status, creation.cost);
响应是 202 Accepted:请求已接收,图片还没有生成好。响应体有四个字段:id 和 task_id 是任务的标识(查询状态时代入 id),status 这时是 queued,cost 是为这张图片扣除的美元金额。请保存 id。
如果连接中断、你不知道请求有没有送达,就用同一个幂等键把同一个请求再发一次:得到的是同一个 202 响应,不会多生成图片,也不会多扣费。
怎样查询状态 并取回结果?
图片是在后台生成的,所以收到 202 之后还要查询状态:用同样的 Authorization 请求头发送 GET /creations/{id},代入上一步响应里的 id。任务只对创建它的账号可见,别人的或不存在的 id 会返回 404。
状态有四种:
queued:请求已接收,正在排队;processing:正在生成图片;completed:完成,文件链接在result_urls里;failed:没有得到图片,原因在error和failure字段里。
completed 和 failed 是最终状态,到这里就可以停止轮询。状态为 failed 的任务,查询本身返回的仍是 200:这是任务的结果,不是状态查询的错误。
如果任务在 202 之后变成 failed,原来的幂等键不会让它重新开始。想再试一次,就发一个新请求,用新的幂等键;它有自己的扣费,而之前那笔已经自动退回。
查询状态的频率
对同一个任务,用同一个密钥,每 5 秒最多查询一次,更频繁会收到 429。要给总的等待时间设一个上限:超时后保存 id,稍后再查,因为你的客户端超时并不会取消任务。我们没有承诺的等待时间,它取决于模型和负载。
下面的循环会一直等到状态变成最终状态,再保存第一个文件。其中的等待上限是示例自己的设置,不是模型的处理时长。更完整的循环还会处理断线、429 和 5xx 的重试,见“任务与状态”。
CREATION_ID="5b0214ae-9e39-460a-820a-f6ae3e24400b" # 上一步响应里的 id curl --fail-with-body --max-time 20 \ "https://api.rendergrid.io/ api/ public/ v1/ creations/ $CREATION_ID" \ -H "Authorization: Bearer $RENDERGRID_API_KEY" # 每隔 5 秒以上再查一次,直到 status 变成 completed 或 failed。 # 然后用 result_urls 里的第一个链接保存文件: RESULT_URL="https:// …" curl --fail-with-body --max-time 60 -L -o result-1k "$RESULT_URL"
import os import time import requests API = "https://api.rendergrid.io/ api/ public/ v1" KEY = os.environ["RENDERGRID_API_KEY"] HEADERS = {"Authorization": f"Bearer {KEY}"} creation_id = "5b0214ae-9e39-460a-820a-f6ae3e24400b" # 上一步响应里的 id # 等待上限是示例自己的设置,不是模型的处理时长 deadline = time.monotonic() + 180 while True: if time.monotonic() >= deadline: raise SystemExit(f"还没有结果,请保存 id:{creation_id}") time.sleep(5) # 查询频率不能高于每 5 秒一次 r = requests.get(f"{API}/creations/{creation_id}", headers=HEADERS, timeout=(5, 20)) r.raise_for_status() creation = r.json() if creation["status"] in ("completed", "failed"): break if creation["status"] == "failed": raise SystemExit(f"{creation['error']} {creation['failure']}") image = requests.get(creation["result_urls"][0], timeout=(10, 60)) image.raise_for_status() print(image.headers.get("Content-Type")) # 文件格式以它为准 with open("result-1k", "wb") as file: file.write(image.content)
// Node.js 22+,文件扩展名为 .mjs import { writeFile } from "node:fs/promises"; const API = "https://api.rendergrid.io/ api/ public/ v1"; const headers = { Authorization: `Bearer ${process.env.RENDERGRID_API_KEY}` }; const creationId = "5b0214ae-9e39-460a-820a-f6ae3e24400b"; // 上一步响应里的 id // 等待上限是示例自己的设置,不是模型的处理时长 const deadline = Date.now() + 180_000; let creation; do { if (Date.now() >= deadline) { throw new Error(`还没有结果,请保存 id:${creationId}`); } // 查询频率不能高于每 5 秒一次 await new Promise((resolve) => setTimeout(resolve, 5000)); const res = await fetch(`${API}/creations/${creationId}`, { headers, signal: AbortSignal.timeout(20_000), }); if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`); creation = await res.json(); } while (["queued", "processing"].includes(creation.status)); if (creation.status === "failed") { throw new Error(`${creation.error} ${JSON.stringify(creation.failure)}`); } const image = await fetch(creation.result_urls[0], { signal: AbortSignal.timeout(60_000) }); if (!image.ok) throw new Error(`HTTP ${image.status}`); console.log(image.headers.get("content-type")); // 文件格式以它为准 await writeFile("result-1k", Buffer.from(await image.arrayBuffer()));
{
"id": "5b0214ae-9e39-460a-820a-f6ae3e24400b",
"status": "queued",
"task_id": "5b0214ae-9e39-460a-820a-f6ae3e24400b",
"cost": 0.04
}
{
"id": "5b0214ae-9e39-460a-820a-f6ae3e24400b",
"status": "completed",
"result_urls": [
"https:// …"
]
}
保存结果文件
result_urls 里是现成的文件链接,原样使用即可:不要用 id 拼链接。文件格式由 output_format 指定:png 或 jpg;省略该字段时,返回模型的原生格式。文件只保存一天,轮询也不会延长这个期限,所以拿到链接后要马上把图片存到你自己的存储里。
也可以不轮询:在请求里传入 webhook_url(公开的 HTTPS 地址),任务变为 completed 或 failed 时我们会通知你。轮询仍要留作备用:通知可能延迟或没有送达。详见 Webhook。
怎样得到 同一个场景的 1K、2K 和 4K 图片?
分辨率由 resolution 字段设置,可以取 1K、2K 或 4K,不区分大小写。我们把同一条提示词通过 Rendergrid 的公开 API 发送了三次,只改了 resolution:每档分辨率一次尝试,aspect_ratio 都是 1:1。
同一条提示词,三档分辨率
打开的长方形琥珀香水瓶立在折起的炭灰纸上,圆润瓶盖单独放在旁边,瓶口清楚可见。右上方光线形成细金色反射,纸折内有阴影。方形画面中两件物品均完整,留白宽松。不出现文字、标志、人物或水印。
Nano Banana Pro
nano-banana-pro · 1K · 1:1 · $0.04
Nano Banana Pro,1K,1:1,一次尝试。瓶子立在折起的深炭灰纸上,瓶口敞开,颈部的螺纹清楚;圆球形的瓶盖单独侧放在瓶子左边,两件物品都完整入镜。纸面有几处大块的斜折面,折痕里是深色阴影。最亮的金色光线在右上角,瓶肩和瓶底的玻璃上有金色的反光,与提示词要求的右上方光线一致。画面从偏高的角度拍摄:瓶子在中心偏右,瓶盖在中心偏左,两件合在一起大致居中,四周留白宽松。画面中没有文字、标志、人物或水印,也没有看到其他明显的瑕疵。
Nano Banana Pro
nano-banana-pro · 2K · 1:1 · $0.04
Nano Banana Pro,2K,1:1,一次尝试。同一条提示词,构图却不是 1K 的放大:镜头离得更近,瓶子(含瓶颈)约占画面高度的一半多,瓶盖单独侧放在画面左下部,一端是球形盖头,另一端是颜色更浅的塞柄。纸面由几道折痕分成宽条,而不是 1K 里的几处斜折面。右上角的纸面上有几道金色的光带,瓶肩有一道浅色的反光,与提示词要求的右上方光线一致。瓶口敞开,颈部螺纹清楚,瓶子和瓶盖都完整入镜;瓶子在中心偏右,瓶盖在中心偏左下,两件合在一起大致居中,但四周的留白比 1K 窄。瓶身下部靠近底座处的玻璃上有一小段无法辨认的细小痕迹,看不出是不是文字,而提示词要求画面里不出现文字。画面中没有其他文字、标志、人物或水印。
Nano Banana Pro
nano-banana-pro · 4K · 1:1 · $0.07
Nano Banana Pro,4K,1:1,一次尝试。这一张的纸面又不同:纸弯成宽大而柔和的弧形折面,左侧的折面落在很深的阴影里,右上方的纸面有一片金色的渐变光,下方有几道笔直的折线。提示词写的是“折起的炭灰纸”,而这里的纸大多弯成宽大的弧形,只有下方有笔直的折线,不像折叠的纸。瓶子在画面中央附近,瓶口敞开,颈部螺纹和瓶肩的金色反光都很清楚。瓶盖侧放在瓶子的右前方,盖头朝向镜头的一面有圆环状的凹口,上方露出一小段颜色更浅的塞柄;它的左缘在画面中与瓶身的右下角相叠,没有像提示词要求的那样单独放在旁边。瓶子和瓶盖合在一起略偏中心右侧,四周留白宽松,两件物品都完整入镜。瓶身下部靠近底座处的玻璃上有一小段无法辨认的细小痕迹,看不出是不是文字,而提示词要求画面里不出现文字。光线从右上方来,与提示词一致。画面中没有其他文字、标志、人物或水印。
这是三次独立的请求,得到的不是同一个场景的三种尺寸,而是三张构图不同的图:1K 里瓶子在中心偏右,2K 里镜头离得更近,4K 里瓶盖移到了右前方。三张图的金色光线都在右上方,瓶口也都是敞开的。2K 和 4K 的瓶身下部有一小段无法辨认的细小痕迹,而提示词要求画面里不出现文字。不要指望 2K 和 4K 只是把 1K 的构图放大:如果需要某一种构图,请在实际要用的分辨率下检查结果。
这三个文件的尺寸分别是 1,024 × 1,024、2,048 × 2,048 和 4,096 × 4,096 像素。但 resolution 只是尺寸档位,不是精确的像素数:接口不接受 width 和 height,需要精确尺寸时请查看拿到的文件。
如果不传 resolution,图片会按 2K 生成并计费:这是 Nano Banana Pro 的默认分辨率。
画面的宽高比由 aspect_ratio 字段设置。Nano Banana Pro 支持 1:1、16:9、9:16、4:3、3:4、2:3、3:2、5:4、4:5、21:9 和 auto。它没有 1:4、1:8、4:1 和 8:1:这四个值只有 Nano Banana 2 和 Nano Banana 2.1 才有。Nano Banana 2.1 另有一篇快速开始。不传 aspect_ratio 时是 1:1,而不是 auto;传入模型不支持的值,会在扣费前收到 400。
一张图片 多少钱,什么时候扣费?
价格由分辨率决定:1K 下为 $0.04,2K 下为 $0.04,4K 下为 $0.07。你的账号的确切价格由 GET /models 返回。
金额在接收请求时、开始生成之前扣除,并出现在 202 响应的 cost 字段里。如果图片没有生成出来,已扣金额会自动退回:失败的请求免费。可以用 GET /balance 查看余额,但这个查询不会预留任何金额,所以不管检查有没有通过,都要处理 402。
官方价格与 Rendergrid 价格的对比,见 Nano Banana Pro API 的价格。所有模型的价格在价格页。
API 出错时会返回什么?
- 401,
ERR_API_KEY_INVALID。没有传密钥,或者密钥错误、已撤销、已过期。检查Authorization请求头是否到达 API,或创建新密钥。 - 402,
ERR_INSUFFICIENT_BALANCE。余额低于这张图片的价格,或者密钥、账号设置的月度限额已用尽。给余额充值(在控制台的“余额与充值”页面中充值,或通过经理充值),或提高限额。 - 400,
ERR_INVALID_REQUEST。缺少prompt,或者模型不支持你写的resolution或aspect_ratio。这时不会扣费:error.details会说明要改什么,details.supported给出可用的值。 - 404,
ERR_NOT_FOUND。找不到这个id,或者它属于另一个账号。请检查id和密钥所属的账号。 - 429,
ERR_RATE_LIMIT。状态查询得太早,或者超出了账号的限额。等待的时间不能短于details.min_interval_seconds,也不能短于响应头Retry-After。各项限额见限额。 - 200,且
status为failed。任务没有成功。读取failure.type、failure.code和failure.retryable。这类任务的金额会自动退回。
依据 error.code 决定下一步,不要解析 message 的文字:它是给人看的,措辞可能会变。全部错误码见错误码一览。
这份指南 不适合什么情况?
- 三张图不是基准测试。每档分辨率只生成了一次,所以不能据此判断 Nano Banana Pro 平均会画成什么样。
- 三次结果各不相同。我们没有重复生成来衡量差异,另一次尝试可能换一种构图。
- 只讲文字生成图片。参考图和基于它的编辑见“参考图”。
- 一次请求一张图片。需要更多,就发送多个请求,每个用不同的幂等键,并保持在账号的限额之内。
- 没有测量等待时间。结果要等多久取决于模型和负载,三张图说明不了这一点。
常见问题解答
怎样用 API 生成第一张 Nano Banana Pro 图片?
POST /images/generate,model 填 nano-banana-pro。用 202 响应里的 id 查询状态,直到变为 completed,再保存 result_urls 里的文件。不指定 resolution 会怎样?
要等多久才能拿到结果?
id,稍后再查。失败的请求要付费吗?
生成好的图片保存多久?
可以不轮询吗?
webhook_url(公开的 HTTPS 地址),任务变为 completed 或 failed 时我们会通知你。轮询仍要留作备用。来源
- Rendergrid 文档:快速开始密钥、请求、状态和结果。
- Rendergrid 文档:生成图片请求字段、
202响应和扣费前的检查。 - Rendergrid 文档:任务与状态状态、查询频率和结果文件的保存期限。
- Rendergrid 文档:模型与参数分辨率、宽高比和默认值。
- Rendergrid 价格页每个模型和分辨率下每张图片的价格。
- 本文的三张图片提示词和参数在图片所在的区块里:每档分辨率各生成一次。




