本指南展示如何在代码中通过文本提示词编辑图像。你通过 OpenRouter API 将源图像和编辑提示词发送给 google/gemini-3.1-flash-image,编辑后的图像会在响应中返回。"Nano Banana" 是 Google Gemini 图像模型的昵称。此 slug 为 Nano Banana 2,是该系列中默认的快速模型。由于你通过 一个 API 访问它,之后只需更改一个字段即可使用不同的编辑模型。
图像编辑是对现有图像进行修改。图像生成是根据文本创建新图像。本指南涵盖的是编辑,因此这里的每个请求都包含一张源图像。如需根据文本创建图像,请参阅 图像生成文档或 图像生成教程。

简而言之
- 编辑只需一次请求。将源图像放入
input_references,将指令放入prompt,然后从data[0].b64_json读取编辑后的图像并将其解码到磁盘。 google/gemini-3.1-flash-image是 Nano Banana 2,默认的快速 Gemini 图像模型。在使用某个模型之前,请确认它接受图像输入,因为编辑支持情况各不相同。- 对于本地或私有文件,将输入作为 base64 数据 URL 发送;对于托管图像,则使用普通的 HTTP(S) URL。
- 以小步进行编辑。将每次返回的图像作为下一次的源图像发回,每次调用只给一条指令,这样修改就会逐步叠加。
- 只需编辑一个字段即可更改编辑模型。
前置条件
你需要三样东西:
- 一个来自密钥页面的 OpenRouter API key,以及基础 URL
https://openrouter.ai/api/v1。 - 一个 HTTP 客户端。示例使用 Python
requests和 TypeScriptfetch。你也可以使用 curl 或 OpenRouter SDK。任何能发送带 Authorization 头的 JSON POST 请求的客户端都可以。 - 一张源图像,可以是本地文件或公开 URL。
使用哪个模型
本指南的默认模型是 google/gemini-3.1-flash-image,即 Nano Banana 2。它以图像作为输入并返回编辑后的图像。Nano Banana 系列目前有四个成员:Nano Banana 2(google/gemini-3.1-flash-image)是本指南的默认模型,Nano Banana 2 Lite(google/gemini-3.1-flash-lite-image)最便宜、最快,Nano Banana Pro(google/gemini-3-pro-image)速度较慢但质量更高,而最初的 Nano Banana(google/gemini-2.5-flash-image)是这个昵称最初来源的较旧模型。
图像目录经常变化。模型会被新增、弃用和重新定价,因此你今天固定的 slug 之后可能会被停用。在基于某个模型进行开发之前,请确认它接受图像输入并支持你所需的编辑功能。你可以在 图像模型合集中浏览支持编辑的模型。如需了解目录的完整介绍,请参阅 图像生成模型。
下面的示例使用每个请求中所示的 slug,因此你可以按原样运行它们,之后再更改模型。请将你的密钥保存在环境变量中,而不是代码里:
export OPENROUTER_API_KEY="sk-or-..." 你的第一次图像编辑
要编辑图像,请在单个请求中发送源图像和文本指令。编辑后的图像会在响应中返回。以下是一个可运行的 Python 请求,它对本地文件进行编码:
import base64, os, requests
api_key = os.environ["OPENROUTER_API_KEY"]
# Encode a local source image as a base64 data URL.
with open("portrait.jpg", "rb") as f:
encoded = base64.b64encode(f.read()).decode()
source = f"data:image/jpeg;base64,{encoded}"
resp = requests.post(
"https://openrouter.ai/api/v1/images",
headers={"Authorization": f"Bearer {api_key}"},
json={
"model": "google/gemini-3.1-flash-image",
"prompt": "Add a red wool scarf around the person's neck. Keep everything else the same.",
"input_references": [
{"type": "image_url", "image_url": {"url": source}}
],
},
)
resp.raise_for_status() TypeScript 中的相同请求:
import { readFileSync } from "node:fs";
const apiKey = process.env.OPENROUTER_API_KEY!;
const encoded = readFileSync("portrait.jpg").toString("base64");
const source = `data:image/jpeg;base64,${encoded}`;
const resp = await fetch("https://openrouter.ai/api/v1/images", {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "google/gemini-3.1-flash-image",
prompt: "Add a red wool scarf around the person's neck. Keep everything else the same.",
input_references: [{ type: "image_url", image_url: { url: source } }],
}),
}); 两种语言的请求体相同。将参考图像放入 input_references,将指令放入 prompt。这就是整个请求。
对输入图像进行编码:base64 或 URL
input_references 字段接受 base64 数据 URL 或 HTTP(S) URL。上面的示例对本地文件进行编码。如果你的图像已经公开托管,直接传入链接并跳过编码即可:
"input_references": [
{"type": "image_url", "image_url": {"url": "https://example.com/portrait.jpg"}}
] 当图片是公开托管时,请使用 URL,因为这样可以保持请求体较小。对于本地或私有文件,请使用 base64。Gemini 接受 image/png、image/jpeg、image/webp、image/heic 和 image/heif 输入。支持的格式因模型而异,因此发送前请查看模型页面。
从响应中获取编辑后的图像
API 会将编辑后的图像以 base64 数据的形式返回在 data 数组中。解码 b64_json 值并将其写入文件:
data = resp.json()["data"][0]
with open("edited.png", "wb") as out:
out.write(base64.b64decode(data["b64_json"])) TypeScript 版本:
import { writeFileSync } from "node:fs";
const { data } = await resp.json();
writeFileSync("edited.png", Buffer.from(data[0].b64_json, "base64")); 打开 edited.png 查看结果。如果你想要类型化客户端而非原始 HTTP,OpenRouter SDK 提供了一个 images 资源,它调用同一个端点:
from openrouter import OpenRouter
client = OpenRouter(api_key=api_key)
result = client.images.generate(
model="google/gemini-3.1-flash-image",
prompt="Add a red wool scarf around the person's neck. Keep everything else the same.",
input_references=[{"type": "image_url", "image_url": {"url": source}}],
) 使用 pip install openrouter 安装 SDK。它复用之前定义的 api_key,因此无需额外设置。
编写编辑提示词
生成提示词描述一整张新图像。编辑提示词则说明要更改什么、保留什么。先说明更改内容,再指明必须保持不变的部分:
- 物体替换:“将咖啡杯替换为一杯橙汁。保持手部位置和背景不变。”
- 背景更换:“将背景更改为夜晚的雪天街道。保持主体完全不变。”
- 风格迁移:“把这张照片渲染成水彩画。保留构图和主体的姿态。”
- 文字修复:“把招牌上的文字改成‘OPEN’。匹配原有的字体和颜色。”
你也可以把提示词写成一小段 JSON 文本:
"prompt": "{\"edit\": \"add sunglasses\", \"preserve\": [\"face\", \"hair\", \"lighting\"], \"style\": \"photorealistic\"}" API 会把它当作纯文本处理,所以这并不是一种特殊模式。这种结构有助于模型区分哪些要改、哪些要保留。可以在你自己的图片上分别试试句子形式和 JSON 形式,哪种效果更好就用哪种。
再次编辑结果
一次编辑并不总能得到你想要的效果。要再跑一遍,就把返回的图片作为下一次的源图传回去。从响应中取出 b64_json 值,把它转成 data URL,然后在下一个 input_references 中传入:
def edit(source_data_url, prompt):
resp = requests.post(
"https://openrouter.ai/api/v1/images",
headers={"Authorization": f"Bearer {api_key}"},
json={
"model": "google/gemini-3.1-flash-image",
"prompt": prompt,
"input_references": [
{"type": "image_url", "image_url": {"url": source_data_url}}
],
},
)
resp.raise_for_status()
item = resp.json()["data"][0]
media_type = item.get("media_type", "image/png")
return f"data:{media_type};base64,{item['b64_json']}"
step1 = edit(source, "Add a red wool scarf. Keep everything else the same.")
step2 = edit(step1, "Now make the scarf navy blue instead of red.")
step3 = edit(step2, "Add soft morning light coming from the left.") 每次调用都会编辑上一次的结果,因此之前的改动会延续下来。每次调用只给一条指令。小改动更容易检查,出错时也更容易重做。模型不会记住你之前的提示词,所以每次新的提示词里都要重复那些应当保持不变的部分。
更换编辑模型
要把同一个编辑请求发给另一个模型,只需更改 model 字段。源图、提示词以及响应处理代码都保持不变:
json={
"model": "openai/gpt-5-image", # was google/gemini-3.1-flash-image
"prompt": "Add a red wool scarf. Keep everything else the same.",
"input_references": [
{"type": "image_url", "image_url": {"url": source}}
],
}, 使用google/gemini-3.1-flash-image作为快速的默认选择。当你想要最低价格时,使用google/gemini-3.1-flash-lite-image。当你想要更高质量并能接受更高延迟时,使用google/gemini-3-pro-image。原有的google/gemini-2.5-flash-image仍可使用相同的请求结构,但上述更新的模型是更好的默认选择。当你想要openai/gpt-5-image时,使用来自其他提供商的模型,例如比较质量、成本或速度在你自己的图像上。这一单字段的更改仅适用于接受图像输入并支持相同input_references结构的模型,因此在切换之前请确认该模型具备编辑能力。
要按环境设置模型及其选项,而不是在代码中设置,请使用 OpenRouter Presets。
错误与成本
这些失败足够常见,值得提前规划应对:
- 不支持的输入。模型可能会拒绝它不支持的图片格式,也可能拒绝它无法访问的 URL。发送前请检查文件类型和 URL。
- 图片过大。大文件可能会超时或失败。请先缩小图片,因为大多数编辑并不需要 4000 万像素的源图。
- 返回文本而非图片。像“这张照片里有什么?”这样的提问可能会让模型用文本作答,而不是生成图片。API 会将其作为
400错误返回,例如Gemini could not generate an image (STOP),而不是空响应。请改写为指令而非提问,并在解码前检查 HTTP 状态码。
当用量数据可用时,响应会以美元报告每个请求的成本。将其记录下来以跟踪支出:
usage = resp.json().get("usage")
if usage:
print(f"This edit cost ${usage['cost']}") 对于批处理任务,请遵守速率限制。对 429 和 5xx 响应进行重试,并在每次尝试之间逐步延长延迟,同时限制同时运行的编辑数量。在开始下一次编辑之前保存每一张返回的图像,这样即使某次失败也不会丢失已完成的工作。
后续步骤
复制第一个请求,换成你自己的图像,然后运行一次编辑。如果想改为根据文本生成图像,请参阅图像生成文档。要查找当前支持编辑的模型,请浏览图像模型合集。
常见问题
我可以用 Gemini API 编辑图像吗?
可以。通过 OpenRouter API 向 google/gemini-3.1-flash-image 发送一个请求,其中包含源图像和文本指令,编辑后的图像会以 base64 形式在响应中返回。该模型是 Nano Banana 2。整个请求一屏就能放下,你可以用 Python、TypeScript 或 curl 运行它。
图像生成和图像编辑有什么区别?
图像编辑是对已有图像进行修改。图像生成则是根据文本创建新图像。每个编辑请求都包含 input_references 中的源图像,以及一条说明要更改什么、保留什么的指令。如果你的请求没有源图像,仅凭文本提示词生成,那就是生成。
如何向 API 发送图像,用 URL 还是 base64?
input_references 字段接受用于本地或私有文件的 base64 数据 URL,或用于公开托管图像的普通 HTTP(S) URL。当图像已经在线时,使用 URL 形式可以保持请求体积较小;当文件在你的机器上时,使用 base64 形式。Gemini 接受 png、jpeg、webp、heic 和 heif 输入(image/png、image/jpeg、image/webp、image/heic、image/heif)。支持的格式因模型而异,因此发送前请查看模型页面。
我可以使用 Gemini 以外的模型来编辑图像吗?
可以。更改 model 字段,其余请求保持不变。请先查看 图像模型集合,因为编辑支持、价格和速度因模型而异。
如何提示 AI 模型编辑图像?
先描述要更改的内容,然后说明要保留的内容,例如“把背景改成夜晚的雪街。主体保持原样。”每个请求只给一条指令效果最好。为了获得精确结果,请以小步骤进行编辑,并将每次返回的图像作为下一个提示词的源图像发回。
参考文献
- OpenRouter API keys:创建并管理每次请求中使用的密钥。
- 图像模型集合:具备编辑能力的完整模型集及其输入支持。
- 图像生成文档:从文本创建图像的配套指南。
- 预设指南:按环境固定模型及其选项,而无需在代码中设置。
This guide shows how to edit an image with a text prompt in code. You send the source image and an edit prompt to google/gemini-3.1-flash-image through the OpenRouter API, and the edited image comes back in the response. “Nano Banana” is the nickname for Google’s Gemini image models. This slug is Nano Banana 2, the default fast model in that family. Because you reach it through one API, you can use a different editing model later by changing one field.
Image editing changes an existing image. Image generation creates a new image from text. This guide covers editing, so every request here includes a source image. For creating images from text, see the image generation docs or the image generation tutorial.

Tl;dr
- Editing takes one request. Put the source image in
input_referencesand the instruction inprompt, then read the edited image fromdata[0].b64_jsonand decode it to disk. google/gemini-3.1-flash-imageis Nano Banana 2, the default fast Gemini image model. Check that a model accepts image input before you use it, because editing support varies.- Send the input as a base64 data URL for local or private files, or a plain HTTP(S) URL for a hosted image.
- Edit in small steps. Send each returned image back in as the next source, one instruction per call, so changes stack.
- Change the editing model by editing one field.
Prerequisites
You need three things:
- An OpenRouter API key from the keys page and the base URL
https://openrouter.ai/api/v1. - An HTTP client. The samples use Python
requestsand TypeScriptfetch. You can also use curl or the OpenRouter SDK. Any client that sends a JSON POST with an Authorization header works. - A source image, either a local file or a public URL.
Which model to use
The default in this guide is google/gemini-3.1-flash-image, Nano Banana 2. It takes an image as input and returns an edited image. The Nano Banana family has four current members: Nano Banana 2 (google/gemini-3.1-flash-image) is the default in this guide, Nano Banana 2 Lite (google/gemini-3.1-flash-lite-image) is the cheapest and fastest, Nano Banana Pro (google/gemini-3-pro-image) is slower and higher quality, and the original Nano Banana (google/gemini-2.5-flash-image) is the older model the nickname started with.
The image catalog changes often. Models are added, deprecated, and repriced, so a slug you pin today may be retired later. Before you build on a model, check that it accepts image input and supports the editing features you need. You can browse the editing-capable models in the image model collection. For a walkthrough of the catalog, see image generation models.
The samples below use the slug shown in each request, so you can run them as written and change the model later. Keep your key in an environment variable, not in your code:
export OPENROUTER_API_KEY="sk-or-..." Your first image edit
To edit an image, send the source image and a text instruction in a single request. The edited image comes back in the response. Here is a working request in Python that encodes a local file:
import base64, os, requests
api_key = os.environ["OPENROUTER_API_KEY"]
# Encode a local source image as a base64 data URL.
with open("portrait.jpg", "rb") as f:
encoded = base64.b64encode(f.read()).decode()
source = f"data:image/jpeg;base64,{encoded}"
resp = requests.post(
"https://openrouter.ai/api/v1/images",
headers={"Authorization": f"Bearer {api_key}"},
json={
"model": "google/gemini-3.1-flash-image",
"prompt": "Add a red wool scarf around the person's neck. Keep everything else the same.",
"input_references": [
{"type": "image_url", "image_url": {"url": source}}
],
},
)
resp.raise_for_status() The same request in TypeScript:
import { readFileSync } from "node:fs";
const apiKey = process.env.OPENROUTER_API_KEY!;
const encoded = readFileSync("portrait.jpg").toString("base64");
const source = `data:image/jpeg;base64,${encoded}`;
const resp = await fetch("https://openrouter.ai/api/v1/images", {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "google/gemini-3.1-flash-image",
prompt: "Add a red wool scarf around the person's neck. Keep everything else the same.",
input_references: [{ type: "image_url", image_url: { url: source } }],
}),
}); The request body is the same in both languages. Put the reference image in input_references and the instruction in prompt. That is the whole request.
Encoding the input image: base64 or URL
The input_references field takes either a base64 data URL or an HTTP(S) URL. The examples above encode a local file. If your image is already hosted publicly, pass the link directly and skip the encoding:
"input_references": [
{"type": "image_url", "image_url": {"url": "https://example.com/portrait.jpg"}}
] Use a URL when the image is public and hosted, because it keeps the request body small. Use base64 for local or private files. Gemini accepts image/png, image/jpeg, image/webp, image/heic, and image/heif inputs. Supported formats vary by model, so check the model page before you send.
Retrieving the edited image from the response
The API returns the edited image as base64 data in the data array. Decode the b64_json value and write it to a file:
data = resp.json()["data"][0]
with open("edited.png", "wb") as out:
out.write(base64.b64decode(data["b64_json"])) The TypeScript version:
import { writeFileSync } from "node:fs";
const { data } = await resp.json();
writeFileSync("edited.png", Buffer.from(data[0].b64_json, "base64")); Open edited.png to see the result. If you want a typed client instead of raw HTTP, the OpenRouter SDK has an images resource that calls the same endpoint:
from openrouter import OpenRouter
client = OpenRouter(api_key=api_key)
result = client.images.generate(
model="google/gemini-3.1-flash-image",
prompt="Add a red wool scarf around the person's neck. Keep everything else the same.",
input_references=[{"type": "image_url", "image_url": {"url": source}}],
) Install the SDK with pip install openrouter. It reuses the api_key defined earlier, so no extra setup is needed.
Writing edit prompts
A generation prompt describes a whole new image. An edit prompt says what to change and what to leave alone. State the change first, then name what must stay the same:
- Object swap: “Replace the coffee mug with a glass of orange juice. Keep the hand position and background unchanged.”
- Background change: “Change the background to a snowy street at night. Keep the subject exactly as is.”
- Style transfer: “Render this photo as a watercolor painting. Preserve the composition and the subject’s pose.”
- Text fix: “Change the sign text to read ‘OPEN’. Match the original font and color.”
You can also write the prompt as a small block of JSON text:
"prompt": "{\"edit\": \"add sunglasses\", \"preserve\": [\"face\", \"hair\", \"lighting\"], \"style\": \"photorealistic\"}" The API treats this as plain text, so it is not a special mode. The structure can help the model separate what changes from what stays. Try both the sentence form and the JSON form on your own images and keep whichever works better.
Editing the result again
One edit will not always give you what you want. To run another pass, send the returned image back in as the next source. Take the b64_json value from the response, turn it into a data URL, and pass it in the next input_references:
def edit(source_data_url, prompt):
resp = requests.post(
"https://openrouter.ai/api/v1/images",
headers={"Authorization": f"Bearer {api_key}"},
json={
"model": "google/gemini-3.1-flash-image",
"prompt": prompt,
"input_references": [
{"type": "image_url", "image_url": {"url": source_data_url}}
],
},
)
resp.raise_for_status()
item = resp.json()["data"][0]
media_type = item.get("media_type", "image/png")
return f"data:{media_type};base64,{item['b64_json']}"
step1 = edit(source, "Add a red wool scarf. Keep everything else the same.")
step2 = edit(step1, "Now make the scarf navy blue instead of red.")
step3 = edit(step2, "Add soft morning light coming from the left.") Each call edits the last result, so earlier changes carry forward. Give one instruction per call. Small edits are easier to check and easier to redo when they come back wrong. The model does not remember your earlier prompts, so repeat the parts it should leave alone in each new prompt.
Changing the editing model
To send the same edit request to a different model, change the model field. The source image, the prompt, and the response-handling code stay the same:
json={
"model": "openai/gpt-5-image", # was google/gemini-3.1-flash-image
"prompt": "Add a red wool scarf. Keep everything else the same.",
"input_references": [
{"type": "image_url", "image_url": {"url": source}}
],
}, Use google/gemini-3.1-flash-image as a fast default. Use google/gemini-3.1-flash-lite-image when you want the lowest price. Use google/gemini-3-pro-image when you want higher quality and can accept more latency. The original google/gemini-2.5-flash-image still works with the same request shape, but the newer models above are the better default. Use a model from another provider, such as openai/gpt-5-image, when you want to compare quality, cost, or speed on your own images. This one-field change only works for models that accept image input and support the same input_references shape, so check that a model is editing-capable before you switch to it.
To set a model and its options per environment instead of in code, use OpenRouter Presets.
Errors and cost
These failures are common enough to plan for:
- Unsupported input. A model may reject an image format it does not support, and it may reject a URL it cannot reach. Check the file type and the URL before you send.
- Oversized images. Large files can time out or fail. Shrink the image first, because most edits do not need a 40-megapixel source.
- Text instead of an image. A question like “what’s in this photo?” can make the model answer in text instead of producing an image. The API returns this as a
400error, such asGemini could not generate an image (STOP), not as an empty response. Write an instruction instead of a question, and check the HTTP status before you decode.
The response reports the cost of each request in USD when usage data is available. Log it to track spend:
usage = resp.json().get("usage")
if usage:
print(f"This edit cost ${usage['cost']}") For batch jobs, stay within rate limits. Retry 429 and 5xx responses with growing delays between tries, and limit how many edits run at once. Save each returned image before starting its next edit, so one failure does not lose finished work.
Next steps
Copy the first request, use your own image, and run an edit. To create images from text instead, see the image generation docs. To find current editing-capable models, browse the image model collection.
Frequently asked questions
Can I edit an image with the Gemini API?
Yes. Send the source image and a text instruction in one request to google/gemini-3.1-flash-image through the OpenRouter API, and the edited image comes back as base64 in the response. That model is Nano Banana 2. The full request fits on one screen, and you can run it in Python, TypeScript, or curl.
What’s the difference between image generation and image editing?
Image editing changes an existing image. Image generation creates a new image from text. Every editing request includes a source image in input_references and an instruction that says what to change and what to keep. If your request has no source image and works from a text prompt alone, that is generation.
How do I send an image to the API, URL or base64?
The input_references field takes a base64 data URL for a local or private file, or a plain HTTP(S) URL for a public hosted image. Use the URL form to keep the request small when the image is already online, and the base64 form when the file is on your machine. Gemini accepts png, jpeg, webp, heic, and heif inputs (image/png, image/jpeg, image/webp, image/heic, image/heif). Supported formats vary by model, so check the model page before you send.
Can I use a model other than Gemini to edit images?
Yes. Change the model field and keep the rest of the request the same. Check the image model collection first, because editing support, price, and speed vary by model.
How do I prompt an AI model to edit an image?
Describe the change first, then name what to preserve, for example “Change the background to a snowy street at night. Keep the subject exactly as is.” One instruction per request works best. For precise results, edit in small steps and send each returned image back in as the source for the next prompt.
References
- OpenRouter API keys: Create and manage the key used in every request.
- Image model collection: The full set of editing-capable models and their input support.
- Image generation docs: The sibling guide for creating images from text.
- Presets guide: Pin a model and its options per environment instead of setting them in code.