香蕉生图指南
通过 Gemini 兼容接口调用 Nano Banana 系列图像模型。本页介绍文生图、图生图及参数用法;不同模型的宽高比、参考图数量和分辨率支持可能不同,未确认的能力不作统一承诺。
- Base URL —
https://api.llmnex.com - 香港加速端点 —
https://hk.llmnex.com - 协议 — Gemini
generateContent - 可用模型 — 以当前 Key 的模型列表为准
- 分辨率 — 按具体模型确认,不保证所有模型具有相同输出尺寸
概览
请求采用 Gemini 兼容格式,基础地址使用 https://api.llmnex.com,API Key 使用本站签发的 Key;还需按本页配置输出模态与模型支持的参数。香港加速可使用 https://hk.llmnex.com,路径和鉴权方式不变。https://www.llmnex.com 仅用于控制台,不用于 API 接入。
| 项 | 值 |
|---|---|
| Base URL | https://api.llmnex.com |
| 香港加速端点 | https://hk.llmnex.com |
| 生图端点 | POST /v1beta/models/{model}:generateContent |
| 流式端点 | POST /v1beta/models/{model}:streamGenerateContent |
| 返回形态 | 下文 IMAGE 生图示例使用 inlineData 内嵌 base64;按响应的 mimeType 识别图片格式 |
| 单次出图 | 示例按 1 张图片处理,不依赖 candidateCount 请求多图 |
| 用量统计 | 非流式响应带 usageMetadata(token 数),见 响应结构 |
模型名可用于确认调用目标和计费方式,但不能据此推断全部能力。尤其是 gemini-nano-banana-2.1,目前仅确认模型名称及后端 Nano Banana 2.1,其余能力请向服务方确认。
鉴权
两种鉴权方式任选其一。使用 Google 官方 SDK 时,还需按所用 SDK 的配置方式设置基础地址、API Key 和输出模态;本页提供 HTTP 示例,不保证所有 SDK 版本仅替换地址即可使用。
Authorization: Bearer YOUR_API_KEYx-goog-api-key: YOUR_API_KEY密钥保管
Key 形如 sk-xxxxxxxx。请放在服务端调用,不要写进前端代码或客户端 App。
快速开始
生图必须明确指定 IMAGE
为兼容首字快速响应,后端已兼容 TEXT 模式。要生成图片,必须在 generationConfig.responseModalities 中明确包含 "IMAGE",建议使用 "responseModalities": ["IMAGE"]。省略该参数或仅指定 TEXT 时,Pro 模型可能生成文字,而不是图片。
curl -X POST "https://api.llmnex.com/v1beta/models/gemini-3-pro-image-c:generateContent" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
--max-time 300 \
-d '{
"contents": [
{ "parts": [ { "text": "a red ceramic teapot on a wooden table, soft daylight" } ] }
],
"generationConfig": { "responseModalities": ["IMAGE"] }
}'set -o pipefail
curl -fsS -X POST "https://api.llmnex.com/v1beta/models/gemini-3-pro-image-c:generateContent" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" --max-time 300 \
-d '{"contents":[{"parts":[{"text":"a red ceramic teapot"}]}],"generationConfig":{"responseModalities":["IMAGE"]}}' \
| python3 -c "import sys,json,base64;
d=json.load(sys.stdin);
candidates=d.get('candidates') or [];
parts=(candidates[0].get('content',{}).get('parts') or []) if candidates else [];
b=next((p.get('inlineData',{}).get('data') for p in parts if p.get('inlineData',{}).get('data')),None);
if not b: sys.exit('响应中未找到图片,请检查 responseModalities、模型支持情况及响应内容');
image=base64.b64decode(b,validate=True);
open('out.png','wb').write(image);
print('saved out.png')"模型与分辨率
模型名后缀区分计费方式,不再表示固定分辨率。 带 -c 按次计费,不带 -c 按 token 计费。实际可用名称请先 查询模型列表,具体价格与扣费以控制台及账单为准。
| 模型族 | 后端 | 特点 |
|---|---|---|
gemini-3-pro-image | Nano Banana Pro | Pro 系列,效果与耗时依请求而异,不作绝对排名 |
gemini-3.1-flash-image | Nano Banana 2 | Flash 系列,效果与耗时请结合业务验证 |
gemini-nano-banana-2.1 | Nano Banana 2.1 | 新增香蕉模型,具体能力以模型说明为准 |
模型命名示例(实际可用名称以当前 Key 的列表为准):
| 模型名称 | 计费方式 |
|---|---|
gemini-3-pro-image-c | 按次计费 |
gemini-3-pro-image | 按 token 计费 |
旧版固定分辨率后缀模型已下线,请更新调用时的完整模型名,不要在未确认计费方式时自动切换。
后缀 -c:分辨率交给调用方
-c 结尾的模型不锁定分辨率,由你在请求里用 generationConfig.imageConfig.imageSize 指定。不传则为 1K。
{
"contents": [ { "parts": [ { "text": "a plain grey cube" } ] } ],
"generationConfig": {
"responseModalities": ["IMAGE"],
"imageConfig": { "imageSize": "4K" }
}
}本页 -c 示例使用的 imageSize 取值为 1K / 2K / 4K。不要传入 512 等未列出的值,也不要依赖无效值自动回落;具体模型的支持范围请先确认。
不带 -c 的模型
不带 -c 的模型按 token 计费。其分辨率参数支持情况请以具体模型说明为准,不要沿用已下线模型的固定分辨率规则。
关于 4K
4K 表示分辨率档位,不是固定宽高保证。请读取返回图片的实际尺寸,不要硬编码为 4096 × 4096,也不要假设不同模型具有相同尺寸规则。
请求体结构
与 Gemini 官方格式一致。生图请求除 contents 外,还必须明确设置 generationConfig.responseModalities 包含 "IMAGE",不要仅依赖提示词要求模型画图。
| 字段 | 类型 | 说明 |
|---|---|---|
contents[].parts[].text | string | 提示词。多个 text part 会用空格拼接 |
contents[].parts[].inlineData | object | 内嵌参考图,见「图生图」;数量限制需按具体模型确认 |
generationConfig.responseModalities | string[] | 生图必须明确包含 "IMAGE",建议 ["IMAGE"];后端兼容 TEXT 模式,省略或仅传 TEXT 时 Pro 模型可能生成文字 |
generationConfig.imageConfig.aspectRatio | string | 宽高比,如 "16:9"。默认 1:1 |
generationConfig.imageConfig.imageSize | string | -c 模型可选 1K/2K/4K;不带 -c 的模型请确认具体参数支持情况 |
{
"contents": [
{ "parts": [ { "text": "a lighthouse on a cliff at dawn, cinematic" } ] }
],
"generationConfig": {
"responseModalities": ["IMAGE"],
"imageConfig": { "aspectRatio": "16:9" }
}
}宽高比
旧文档列出的比例包括:1:1 16:9 9:16 4:3 3:4 3:2 2:3 5:4 4:5 21:9 1:4 4:1 1:8 8:1。这不是所有当前模型的统一支持列表,使用前请确认目标模型支持的比例。
以下尺寸仅为旧文档的 1K 历史样例,未标明适用模型,不作为当前模型的输出保证;实际尺寸以返回图片为准:
| aspectRatio | 1K 历史样例尺寸 | 典型用途 |
|---|---|---|
1:1 | 1024 × 1024 | 头像、商品图 |
16:9 | 1376 × 768 | 横版封面、视频首帧 |
9:16 | 768 × 1376 | 竖屏短视频、手机壁纸 |
4:3 | 1200 × 896 | 传统照片比例 |
21:9 | 1584 × 672 | 超宽横幅 |
4:5 | 928 × 1152 | 社交媒体竖图 |
提示
不要按上述样例推算 2K / 4K 的固定尺寸,也不要依赖未支持的比例自动回落。参数不受支持时,请修正请求,而不是假设服务一定忽略该值。
超长比例需单独确认
旧文档标注 gemini-3-pro-image 系列不支持 1:4、4:1、1:8、8:1;在重新确认前,Pro 请求请避开这四种比例。其他模型(包括 Nano Banana 2.1)的支持情况及像素规则需向服务方确认,不沿用旧样例的倍数或报错规则。
图生图
在 parts 里加入 inlineData 即为图生图。文字描述你想要的改动,参考图提供内容或风格来源。
{
"contents": [
{
"parts": [
{ "text": "把这张照片改成水彩画风格,保留构图" },
{
"inlineData": {
"mimeType": "image/png",
"data": "iVBORw0KGgoAAAANSUhEUg..."
}
}
]
}
],
"generationConfig": { "responseModalities": ["IMAGE"] }
}| 约束 | 值 |
|---|---|
| 参考图数量 | 示例使用 1 张;多图支持及数量上限需按具体模型确认 |
| 格式 | PNG / JPEG / WebP,由 mimeType 声明 |
| 编码 | 纯 base64,不要带 data:image/png;base64, 前缀 |
| 输出分辨率 | -c 模型由 imageSize 指定,与输入图尺寸无关 |
不生效的参数
以下是旧文档记录的兼容限制,尚未逐模型重新验证。接入时请保守处理,不依赖这些参数;不保证所有模型都会接受参数或返回相同结果:
| 参数 | 实际行为 |
|---|---|
generationConfig.candidateCount | 本页示例按 1 张图处理,不依赖该参数请求多图 |
generationConfig.seed | 接受,但不保证跨请求复现,同 seed 同提示词也可能出不同的图 |
tools 里的 googleSearch | 不联网,响应无 groundingMetadata |
generationConfig.thinkingConfig | 不生效,响应无思考 token 计数 |
contents[].parts[].fileData(URL 参考图) | 不读取,参考图请用 inlineData 内嵌 |
流式返回(不建议使用)
建议用非流式的 :generateContent
以下为旧文档记录的 IMAGE 生图流式行为,未逐模型复测;不适用于 TEXT 首字快速响应,也不承诺新模型行为完全相同。
图像是一次性生成的,没有中间产物可以流式推送。这个端点会先等整张图出完, 再把最终结果包成单个 SSE chunk 发出 —— chunk 里的内容与 generateContent 的响应体一致,只是没有 usageMetadata。
也就是说,你付出了 SSE 解析的成本,却拿不到任何流式的好处:
- 不会更早看到画面,等待时间与非流式完全一致
- 不能保持连接活跃 —— 出图期间服务端不发送任何字节,防不了中间层空闲超时
- 整张图的 base64 挤在一行
data:里,常见 7–11 MB,部分 SSE 客户端和反向代理 对单行长度有限制,反而更容易被截断 - 出错时不走 SSE,直接返回 JSON 错误体,客户端要额外处理两种响应格式
- 不返回
usageMetadata,需要用量统计必须用非流式
建议为客户端及中间层配置足够的超时余量;仅增加客户端超时不能保证避开代理或网关的超时,见下方「超时与耗时」。
保留该端点是为了兼容已有代码。用法是把端点换成 :streamGenerateContent, 返回 SSE 格式,每行以 data: 开头。
curl -N -X POST "https://api.llmnex.com/v1beta/models/gemini-3-pro-image-c:streamGenerateContent" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" --max-time 300 \
-d '{"contents":[{"parts":[{"text":"a plain grey cube"}]}],"generationConfig":{"responseModalities":["IMAGE"]}}'响应结构
以下为明确指定 responseModalities: ["IMAGE"] 后成功返回图片的非流式响应示例(gemini-3-pro-image-c),不是 TEXT 模式或错误响应的结构保证:
{
"candidates": [
{
"content": {
"role": "model",
"parts": [
{
"inlineData": {
"mimeType": "image/png",
"data": "iVBORw0KGgoAAAANSUhEUgAABAAA..."
}
}
]
},
"finishReason": "STOP",
"index": 0
}
],
"responseId": "ee2bcd924c5d",
"upstreamJobId": "d427bc37-0f6c-458b-93c9-d40a0b5412f7",
"usageMetadata": {
"promptTokenCount": 4,
"promptTokensDetails": [ { "modality": "TEXT", "tokenCount": 4 } ],
"candidatesTokenCount": 1120,
"candidatesTokensDetails": [ { "modality": "IMAGE", "tokenCount": 1120 } ],
"totalTokenCount": 1124
}
}成功生图时,从 candidates[0].content.parts 中遍历查找 inlineData.data,不要写死 parts[0]。先检查 HTTP 状态,再检查候选内容及图片是否存在;响应缺少图片时应报告错误,不要直接解码或写文件。
| 字段 | 类型 | 说明 |
|---|---|---|
candidates[] | array | 示例取第一个候选;使用前检查是否存在 |
candidates[].content.role | string | 示例为 model |
candidates[].content.parts[].inlineData.mimeType | string | 示例为 image/png,以实际响应为准 |
candidates[].content.parts[].inlineData.data | string | 图片的 base64 数据,需检查存在后再解码 |
candidates[].finishReason | string | 正常出图为 STOP |
candidates[].index | integer | 示例为 0 |
responseId | string | 本站请求 ID,与响应头 X-Oneapi-Request-Id 相同 |
upstreamJobId | string | 上游任务号,报障时连同 responseId 一起附上 |
usageMetadata | object | 本次消耗的 token 数,见下 |
上述示例只展示 IMAGE 生图结果。后端兼容 TEXT 模式,省略输出模态或仅指定 TEXT 时,Pro 模型可能在 parts[].text 返回文字;因此不能假定每个成功响应都有图片。promptFeedback、modelVersion、safetyRatings 等可选字段是否返回,请以实际响应为准,不作为取图的必需条件。
usageMetadata
usageMetadata 是本次请求消耗的 token 数,结构与官方一致:promptTokenCount(提示词与参考图)、 candidatesTokenCount(生成的图片)、totalTokenCount,以及 *Details 里按模态的拆分。 不同模型的用量字段与计算方式可能不同,不要按固定图片 token 数估算实际扣费。
三点说明
- 旧文档记录的 IMAGE 流式请求(
:streamGenerateContent)不返回usageMetadata,尚未逐一确认所有模型与 TEXT 模式;需要用量统计建议使用非流式,并检查实际返回字段。 - 旧文档将
promptTokenCount标为估算值;各模型的用量精度与计算规则尚未逐一确认,请将其作为参考。 usageMetadata是用量统计,不等于计费方式。带-c的模型按次计费,不带-c的模型按 token 计费,实际扣费以账户账单为准。
4K 图的响应体很大
旧文档中的 4K base64 响应样例约 25–28 MB,实际大小随模型与图片内容变化。请检查 HTTP 客户端与中间层的响应体上限, 并给传输留出时间:下方的耗时表是服务端出图时间,不含下载,弱网下 4K 建议把超时设到 600 秒。
超时与耗时
当前生图速度较此前大幅提升,常见耗时参考如下,不代表时延保证。
为高峰期排队、重试和图片传输留出余量,客户端超时仍建议设到 300 秒。常见耗时不等于最长耗时,较短的超时设置仍可能导致请求中断。
| 档位 | 服务端出图耗时参考 |
|---|---|
| 1K | 约 15–25 秒 |
| 2K | 约 15–35 秒 |
| 4K | 约 20–45 秒 |
以上是服务端出图时间,图片传输另计;响应体大小随模型与图片内容变化。 耗时受上游负载影响,高峰期可能拉长。服务内部已有重试机制,客户端不要叠加激进重试;参数、鉴权、权限及审核问题应先修正请求,临时故障的重试建议见下方错误处理。
错误处理
| HTTP | 含义 | 怎么办 |
|---|---|---|
| 400 | 请求体不合法,如 contents 缺失或为空 | 检查 JSON 结构 |
| 401 | Key 无效或已过期 | 检查 Authorization 头 |
| 429 | 请求受限 | 退避 2–5 秒后可尝试重试一次,不保证成功,不要循环重试 |
| 451 | 内容被安全审核拦截 | 终态,重试无用。 改提示词或参考图 |
| 500 | 上游临时不可用,或网关内部错误 | 稍后重试一次;持续出现请联系我们 |
| 503 | model_not_found —— 模型名写错,或你的 Key 没有该模型的权限 | 见下 |
| 503 | 其他消息(如 upstream session error) | 上游连接临时故障,退避后重试一次 |
请保留响应头 X-Oneapi-Request-Id;存在时在报障信息中附上,便于定位请求。不要公开 API Key。
{
"error": {
"message": "image_unsafe: The generated images appear to be unsafe.",
"type": "invalid_request_error"
}
}不要对 451 自动重试
451 表示内容安全拦截,请修改提示词或参考图,不要对同一请求自动重试。不保证重复请求会得到相同审核结果,也不据此推断是否扣费。
503 No available channel for model ...
先排查模型名拼写、是否已下线及当前 Key 的权限,用 GET /v1/models 确认可用名称,不要直接自动重试。列表里没有目标模型时,联系服务方确认;列表中存在但仍报错时,也应附上请求 ID 排查,不能仅凭这条消息排除服务侧问题。
完整示例
Python 示例需安装 requests,Node.js 示例使用支持内置 fetch 的运行环境。以下示例按 PNG 响应保存为 out.png;如实际 mimeType 不同,请使用对应文件扩展名。成功写入会覆盖当前目录的同名文件,请先备份。
import base64, requests
API_KEY = "YOUR_API_KEY"
BASE = "https://api.llmnex.com"
MODEL = "gemini-3-pro-image-c"
resp = requests.post(
f"{BASE}/v1beta/models/{MODEL}:generateContent",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"contents": [{"parts": [{"text": "a lighthouse on a cliff at dawn"}]}],
"generationConfig": {"responseModalities": ["IMAGE"], "imageConfig": {"aspectRatio": "16:9", "imageSize": "2K"}},
},
timeout=300, # 为排队、重试和传输留出余量
)
resp.raise_for_status()
candidates = resp.json().get("candidates") or []
parts = (candidates[0].get("content", {}).get("parts") or []) if candidates else []
b64 = next((p.get("inlineData", {}).get("data") for p in parts if p.get("inlineData", {}).get("data")), None)
if not b64:
raise RuntimeError("响应中未找到图片,请检查 responseModalities、模型支持情况及响应内容")
image = base64.b64decode(b64, validate=True)
with open("out.png", "wb") as f:
f.write(image)
print("saved out.png")import { writeFile } from "node:fs/promises";
const API_KEY = "YOUR_API_KEY";
const MODEL = "gemini-3-pro-image-c";
const res = await fetch(
`https://api.llmnex.com/v1beta/models/${MODEL}:generateContent`,
{
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
contents: [{ parts: [{ text: "a lighthouse on a cliff at dawn" }] }],
generationConfig: { responseModalities: ["IMAGE"], imageConfig: { aspectRatio: "16:9", imageSize: "2K" } },
}),
signal: AbortSignal.timeout(300_000), // 5 分钟
}
);
if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
const payload = await res.json();
const parts = payload.candidates?.[0]?.content?.parts ?? [];
const b64 = parts.find((p) => p.inlineData?.data)?.inlineData.data;
if (!b64) throw new Error("响应中未找到图片,请检查 responseModalities、模型支持情况及响应内容");
await writeFile("out.png", Buffer.from(b64, "base64"));
console.log("saved out.png");import base64, requests
src = base64.b64encode(open("input.png", "rb").read()).decode()
resp = requests.post(
"https://api.llmnex.com/v1beta/models/gemini-3-pro-image-c:generateContent",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={"contents": [{"parts": [
{"text": "把这张照片改成水彩画风格,保留构图"},
{"inlineData": {"mimeType": "image/png", "data": src}},
]}], "generationConfig": {"responseModalities": ["IMAGE"]}},
timeout=300,
)
resp.raise_for_status()
candidates = resp.json().get("candidates") or []
parts = (candidates[0].get("content", {}).get("parts") or []) if candidates else []
b64 = next((p.get("inlineData", {}).get("data") for p in parts if p.get("inlineData", {}).get("data")), None)
if not b64:
raise RuntimeError("响应中未找到图片,请检查 responseModalities、模型支持情况及响应内容")
image = base64.b64decode(b64, validate=True)
with open("out.png", "wb") as f:
f.write(image)