Gemini 图像生成与编辑 API(Nano Banana 2.1)

gemini-nano-banana-2.1 等 Gemini 生图模型的文生图、参考图编辑、Images 与原生接口、cURL / Python 示例及错误排查。

适用范围:涉及账户、Key、模型、计费或接口的内容,请以 GPT88 当前控制台和对应 API 文档为准。

返回教程库

GPT88 使用说明 模型名、API 字段、分辨率、运行限制和价格以 GPT88 模型导航及对应 API 文档为准。

快速开始 · 模型导航 · GPT88 API Key 管理

本文介绍如何在GPT88 调用 Gemini 系列生图模型,包含文生图、参考图编辑与原生 Gemini 请求。普通应用推荐使用 Images 接口;已有 Gemini 原生客户端可使用 generateContent。

接入地址与模型

项目内容
站点根地址https://api.gpt88.cc
OpenAI 兼容 SDK 的 Base URLhttps://api.gpt88.cc/v1
文生图POST /v1/images/generations
参考图编辑POST /v1/images/edits
Gemini 原生生成与编辑POST /v1beta/models/{model}:generateContent

本文示例使用 gemini-nano-banana-2.1。当前平台也配置了 gemini-3-pro-image、gemini-3-pro-image-preview、gemini-3.1-flash-image-preview;具体可用模型以你的 API 密钥所属分组、模型权限及平台当前列表为准。这些名称是调用时填写的平台模型标识,不代表它们彼此等价。

使用用户 API 密钥鉴权:Authorization: Bearer sk-你的密钥。不要使用系统管理令牌。密钥仅保存在服务端或可信本地环境,不要嵌入公开网页。

以下 cURL 使用 Bash 语法,先在本地设置 GPT88_API_KEY 环境变量。完整 URL 已含接口路径,不要再次追加 /v1。

1. 文生图

curl --fail-with-body --max-time 300 \
  'https://api.gpt88.cc/v1/images/generations' \
  -H "Authorization: Bearer $GPT88_API_KEY" \
  -H 'Content-Type: application/json' \
  --data-raw '{
    "model": "gemini-nano-banana-2.1",
    "prompt": "一只坐在窗边的橘猫,水彩插画风格,柔和自然光",
    "n": 1,
    "response_format": "b64_json",
    "aspect_ratio": "1:1"
  }'

如需指定清晰度,可添加 "image_size": "2K"。不填写时由上游选择;平台接受 1K、2K、4K,但具体模型是否支持该档位,以该模型实际能力为准。

2. 参考图编辑

上传本地文件

将 reference.png 换成你的本地图片路径。使用 -F 时让 cURL 自动生成 multipart 请求头,不要手动指定 Content-Type。

curl --fail-with-body --max-time 300 \
  'https://api.gpt88.cc/v1/images/edits' \
  -H "Authorization: Bearer $GPT88_API_KEY" \
  -F 'model=gemini-nano-banana-2.1' \
  -F 'prompt=保留图中主体和构图,将背景改为日落海边' \
  -F 'n=1' \
  -F 'response_format=b64_json' \
  -F 'aspect_ratio=1:1' \
  -F '[email protected]'

多张参考图使用重复的 -F 'image[]=@文件路径',不要同时使用 image 与 image[] 文件字段。模型能处理的参考图数量由上游决定,本文不承诺固定上限或生成效果。

JSON 图片引用

也可将下列 JSON 作为 /v1/images/edits 的请求体,使用与文生图相同的 Bearer 和 JSON 请求头。把示例 URL 替换为服务端可直接下载的实际图片链接:

{
  "model": "gemini-nano-banana-2.1",
  "prompt": "保留图中主体,将背景改为日落海边",
  "image": "https://your-domain.example/reference.png",
  "n": 1,
  "response_format": "b64_json",
  "aspect_ratio": "1:1"
}

image 支持一个引用或引用数组,也可使用完整的 data:image/png;base64,... 数据 URI。示例中的省略号不是有效图片数据。远程链接必须返回实际图片,不能是图片展示网页、登录页或本机路径;下载还需满足平台的媒体访问限制。

平台也识别 images、image_urls、referenceImages,但一次请求只选其中一个引用字段;不能混用别名,也不能把上传文件和 JSON 图片引用混在一起。编辑请求至少提供一张参考图。

3. Images 参数与限制

参数用法
model必填,使用平台对当前密钥开放的准确模型名。
prompt必填,描述生成内容或编辑要求。
n使用 1;不填写时按 1 处理。不支持用较大的值批量生成。
response_format省略或填 b64_json;不支持 url。
aspect_ratio可选,见下方比例列表。
image_size可选,1K、2K、4K,受具体模型能力限制。
resolutionimage_size 的兼容写法,不要同时提交冲突值。
size兼容比例、清晰度档位和部分标准像素组合;建议优先使用 aspect_ratio 与 image_size,不要随意填写任意宽高。
quality省略或填 auto;不要填 hd、high 等其他图像模型参数。
stream省略或填 false;Images 接口不支持流式生图。

支持的比例:1:1、2:3、3:2、3:4、4:3、4:5、5:4、9:16、16:9、21:9。

参考图支持 PNG、JPEG、WebP;图片实际格式须与声明的 MIME 类型一致。Images 接口按解码后的图片字节计算:单张不超过 10 MiB,合计不超过 45 MiB。这是平台接入限制,上游模型或传输链路可能有更严格的限制。

目前不支持 mask、style、background、output_format、output_compression、moderation、partial_images 等参数。不要把其他模型的整套参数直接复制过来。若使用 gemini-2.5-flash-image,不要指定 image_size 或等价分辨率参数;是否可调用该模型仍以你的分组权限为准。

4. 获取并保存 Images 返回的图片

成功响应格式如下,Base64 内容仅为结构示意:

{
  "created": 1791417600,
  "data": [
    { "b64_json": "这里是完整的图片Base64数据" }
  ]
}

逐项解码 data[].b64_json 即可得到图片字节。返回的实际格式可能是 PNG、JPEG 或 WebP,保存时应识别文件头后选择扩展名,不要固定保存为 .png。不要依赖返回公开下载 URL,也不要假定成功响应必定包含 usage。

Python 请求与图片保存

将下面的代码保存为 gemini_image_example.py。使用 Python 3 即可运行,无需安装额外依赖。脚本读取 GPT88_API_KEY 环境变量,一次运行只提交一次请求,不自动重试;它会识别实际图片格式并保存到指定目录。

Windows PowerShell 可这样设置密钥,运行前替换占位值:

$env:GPT88_API_KEY = 'sk-你的完整API密钥'

四种调用方式如下。每次生成建议使用新的 --output 目录,避免覆盖已有图片;编辑命令中的 reference.png 需要指向真实本地文件。

# Images 文生图
python gemini_image_example.py --prompt "一只窗边的橘猫,水彩风格" --output images-generation-01

# Images 参考图编辑
python gemini_image_example.py --prompt "保留主体,将背景改为日落海边" --reference reference.png --output images-edit-01

# Gemini 原生文生图
python gemini_image_example.py --protocol native --prompt "一只窗边的橘猫,水彩风格" --output native-generation-01

# Gemini 原生参考图编辑
python gemini_image_example.py --protocol native --prompt "保留主体,将背景改为日落海边" --reference reference.png --output native-edit-01

默认模型为 gemini-nano-banana-2.1,可通过 --model 指定当前分组开放的其他 Gemini 生图模型。

"""gpt88 Gemini image example: standard library only, one request, no retries.

Set GPT88_API_KEY to your full sk- key before running this script.
"""
import argparse
import base64
import json
import os
from pathlib import Path
import urllib.error
import urllib.parse
import urllib.request


def image_type(raw):
    if raw.startswith(b'\x89PNG\r\n\x1a\n'):
        return 'image/png', 'png'
    if raw.startswith(b'\xff\xd8\xff'):
        return 'image/jpeg', 'jpg'
    if raw[:4] == b'RIFF' and raw[8:12] == b'WEBP':
        return 'image/webp', 'webp'
    raise ValueError('Expected a PNG, JPEG or WebP image')


def make_request(model, prompt, reference=None, protocol='images'):
    encoded = mime = None
    if reference is not None:
        if len(reference) > 10 * 1024 * 1024:
            raise ValueError('Reference image exceeds 10 MiB')
        mime, _ = image_type(reference)
        encoded = base64.b64encode(reference).decode('ascii')
    if protocol == 'native':
        parts = [{'text': prompt}]
        if encoded:
            parts.append({'inlineData': {'mimeType': mime, 'data': encoded}})
        return '/v1beta/models/' + urllib.parse.quote(model, safe='') + ':generateContent', {
            'contents': [{'role': 'user', 'parts': parts}],
            'generationConfig': {'responseModalities': ['TEXT', 'IMAGE']},
        }
    body = {'model': model, 'prompt': prompt, 'n': 1, 'response_format': 'b64_json'}
    if encoded:
        body['image'] = 'data:' + mime + ';base64,' + encoded
    return ('/v1/images/edits' if encoded else '/v1/images/generations'), body


def extract_images(payload, protocol):
    if not isinstance(payload, dict) or payload.get('error'):
        raise ValueError('API returned an error or an invalid response')
    if protocol == 'native':
        if payload.get('promptFeedback', {}).get('blockReason') not in (None, '', 'BLOCK_REASON_UNSPECIFIED'):
            raise ValueError('Native request was blocked; inspect promptFeedback')
        if any(candidate.get('finishReason') not in (None, '', 'STOP')
               for candidate in payload.get('candidates', [])):
            raise ValueError('Native generation did not finish normally; inspect finishReason')
        items = [part['inlineData']['data']
                 for candidate in payload.get('candidates', [])
                 for part in candidate.get('content', {}).get('parts', [])
                 if not part.get('thought') and part.get('inlineData', {}).get('data')]
    else:
        items = [item['b64_json'] for item in payload.get('data', []) if item.get('b64_json')]
    if not items:
        raise ValueError('Response contains no image; inspect the error or finishReason')
    images = []
    for item in items:
        raw = base64.b64decode(item, validate=True)
        _, extension = image_type(raw)
        images.append((raw, extension))
    return images


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--model', default='gemini-nano-banana-2.1')
    parser.add_argument('--prompt', required=True)
    parser.add_argument('--reference', type=Path)
    parser.add_argument('--protocol', choices=['images', 'native'], default='images')
    parser.add_argument('--output', type=Path, default=Path('generated'))
    args = parser.parse_args()
    if args.output.exists() and (not args.output.is_dir() or any(args.output.glob('result-*'))):
        raise ValueError('Choose a new output directory before submitting another request')
    key = os.environ['GPT88_API_KEY'].strip()
    if not key.startswith('sk-'):
        raise ValueError('GPT88_API_KEY must contain your complete sk- API key')
    reference = None
    if args.reference:
        with args.reference.open('rb') as stream:
            reference = stream.read(10 * 1024 * 1024 + 1)
    endpoint, body = make_request(args.model, args.prompt, reference, args.protocol)
    request = urllib.request.Request(
        'https://api.gpt88.cc' + endpoint,
        data=json.dumps(body, ensure_ascii=False).encode('utf-8'),
        headers={'Authorization': 'Bearer ' + key, 'Content-Type': 'application/json'},
        method='POST',
    )
    try:
        with urllib.request.urlopen(request, timeout=180) as response:
            payload = json.load(response)
    except urllib.error.HTTPError as error:
        message = error.read(4096).decode('utf-8', errors='replace').replace(key, '<redacted>')
        raise SystemExit(f'HTTP {error.code}: {message}') from None
    images = extract_images(payload, args.protocol)
    args.output.mkdir(parents=True, exist_ok=True)
    for index, (raw, extension) in enumerate(images, start=1):
        destination = args.output / f'result-{index}.{extension}'
        # Exclusive creation protects earlier generated images from overwrite.
        with destination.open('xb') as stream:
            stream.write(raw)
        print(destination)


if __name__ == '__main__':
    main()

5. Gemini 原生接口

原生接口使用站点根地址下的 /v1beta,不要拼接成 /v1/v1beta。请求使用 contents;生成图片需设置 generationConfig.responseModalities。

curl --fail-with-body --max-time 300 \
  'https://api.gpt88.cc/v1beta/models/gemini-nano-banana-2.1:generateContent' \
  -H "Authorization: Bearer $GPT88_API_KEY" \
  -H 'Content-Type: application/json' \
  --data-raw '{
    "contents": [{
      "role": "user",
      "parts": [{"text": "一只坐在窗边的橘猫,水彩插画风格"}]
    }],
    "generationConfig": {
      "responseModalities": ["TEXT", "IMAGE"],
      "imageConfig": {"aspectRatio": "1:1"}
    }
  }'

支持的模型可在 imageConfig 中按需添加 "imageSize": "2K"。原生 imageSize 和 Images 接口的 image_size 写法不同,不能直接互换层级。

原生编辑仍调用同一个 generateContent 接口,在 parts 中同时提供编辑文字与图片。下面仅展示结构,必须把占位内容替换为图片的完整 Base64:

{
  "contents": [{
    "role": "user",
    "parts": [
      {"text": "保留主体,将背景改为日落海边"},
      {"inlineData": {
        "mimeType": "image/png",
        "data": "替换为真实PNG文件的完整Base64,不带data:前缀"
      }}
    ]
  }],
  "generationConfig": {
    "responseModalities": ["TEXT", "IMAGE"]
  }
}

原生响应从 candidates[].content.parts[] 中读取:跳过 thought: true 的部分,找到 inlineData.data 后解码图片,并根据 inlineData.mimeType 保存。文字说明位于 text,不应把它当作图片。还应检查 promptFeedback、finishReason 及是否实际存在图片,不能仅凭 HTTP 200 判断出图成功。

不要用 responseMimeType: "image/png" 强制指定输出格式。它不是本接口保证图片编码格式的开关;本示例省略该字段,以实际返回图片类型为准。

6. 常见报错与排查

状态或错误含义与处理
401检查是否使用有效用户 API 密钥,且 Bearer 请求头格式正确。
403根据错误正文检查账户状态、令牌与模型权限、入口访问策略;余额不足也可能返回此状态,需按正文判断。
429可能是请求频率限制,也可能是余额不足等限制;先看错误正文,频控按提示等待,余额不足则检查账户或令牌额度。
400 model does not support Gemini image generation当前请求未进入正确的生图适配。若使用本文列出的模型仍出现此错误,请将模型名、接口路径、请求时间和请求 ID 提交平台排查;不要自行改成另一个模型名。
400 参数错误检查 n、response_format、流式选项、参考图字段和比例/清晰度是否冲突。修改对应参数后再请求。
400 安全策略拒绝上游拒绝了本次内容,调整提示词或参考图;同一请求连续重试通常无效。
502 no_image_generated上游完成响应但未返回可用图片;记录错误并联系平台排查。
502 incomplete_image_response上游图片生成未完整结束。
502 missing_image_usage上游未提供当前计费方式所需的用量信息,需由平台核查。
其他 500 / 502查看完整错误正文和请求 ID,可能是上游故障、响应损坏或服务处理异常,不能单凭状态码认定原因。

遇到超时、无图或响应中断,不要立即无限自动重试:客户端停止等待时,上游生成可能仍在继续,再发请求可能产生重复任务和费用。先保存错误正文、时间和请求 ID,再判断是否重新提交。排查资料中请隐藏 API 密钥。