Claude Opus 5.5 is live on SeedRouter

Nano Banana Pro in Claude Code: a skill that generates and edits images

Give Claude Code a Nano Banana Pro skill: a small script that submits the request, polls the task and saves the image, plus a SKILL.md you invoke by name.

Read as Markdown

Claude Code can generate and edit images with Nano Banana Pro through a skill: a folder with a SKILL.md file and a short script that calls the SeedRouter API. You type /nano-banana-pro and describe the image; Claude writes the prompt, runs the script, and the script submits the request, polls the task and saves the image into your project. The same folder also works in OpenAI Codex. SeedRouter does not ship an official skill or MCP server, so this guide gives you one to copy. The skill format follows Anthropic's and OpenAI's documentation, checked on September 29, 2026.

How does Claude Code make images with Nano Banana Pro?

Claude Code does not draw images itself. It runs commands, so it can run a script that calls an image API. With a skill in place, one request goes like this:

  1. You invoke the skill and describe the image, for example /nano-banana-pro a hero image for the pricing page.
  2. Claude turns your description into a Nano Banana Pro prompt and picks the size and aspect ratio.
  3. It runs the script. The script sends the request to POST https://api.seedrouter.ai/v1/images/generations and gets back a task ID.
  4. The script polls GET /v1/tasks/{id} every few seconds until the image is ready, then downloads it into an images/ folder.
  5. Claude reports the file path, and can then use the image in your code.

A script, rather than instructions alone, keeps the API calls exact: the request body, the polling loop and the file saving are the same every time, whatever Claude writes as the prompt.

What do you need before you start?

  • A SeedRouter API key, exported in the terminal you start Claude Code from:
export SEEDROUTER_API_KEY="your-key"
  • Credits. Every run is one paid request. New accounts start with a small free balance, and a failed request is not charged.
  • Python 3. The script uses only the standard library, so there is nothing to install.

The script: submit, poll and save

Save this as scripts/nano_banana_pro.py inside the skill folder:

#!/usr/bin/env python3
import argparse
import json
import os
import pathlib
import sys
import time
import urllib.error
import urllib.request

API = "https://api.seedrouter.ai/v1"
AGENT = "nano-banana-pro-skill/1.0"
MIME = {
    ".jpg": "image/jpeg",
    ".jpeg": "image/jpeg",
    ".png": "image/png",
    ".webp": "image/webp",
    ".heic": "image/heic",
    ".heif": "image/heif",
}
RATIOS = ["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"]


def call(method, path, body=None):
    request = urllib.request.Request(
        API + path,
        data=None if body is None else json.dumps(body).encode(),
        method=method,
        headers={
            "Authorization": "Bearer " + os.environ["SEEDROUTER_API_KEY"],
            "Content-Type": "application/json",
            "User-Agent": AGENT,
        },
    )
    try:
        with urllib.request.urlopen(request, timeout=60) as response:
            return json.load(response)
    except urllib.error.HTTPError as error:
        sys.exit(f"HTTP {error.code}: {error.read().decode()}")


def submit(args):
    parts = [{"text": args.prompt}]
    for url in args.ref:
        suffix = pathlib.PurePosixPath(url.split("?")[0]).suffix.lower()
        if suffix not in MIME:
            sys.exit(f"Cannot tell the image type of {url}; use a .jpg, .png, .webp, .heic or .heif URL.")
        parts.append({"fileData": {"mimeType": MIME[suffix], "fileUri": url}})
    image_config = {}
    if args.ratio:
        image_config["aspectRatio"] = args.ratio
    if args.size:
        image_config["imageSize"] = args.size
    generation_config = {"responseModalities": ["IMAGE"]}
    if image_config:
        generation_config["imageConfig"] = image_config
    body = {
        "model": args.model,
        "contents": [{"role": "user", "parts": parts}],
        "generationConfig": generation_config,
    }
    return call("POST", "/images/generations", body)["id"]


def wait(task_id, out_dir, timeout):
    deadline = time.monotonic() + timeout
    while time.monotonic() < deadline:
        task = call("GET", "/tasks/" + task_id)
        if task["status"] == "completed":
            out_dir.mkdir(parents=True, exist_ok=True)
            extension = "jpg" if task["output"]["output_format"] == "jpeg" else task["output"]["output_format"]
            for index, image in enumerate(task["output"]["data"]):
                path = out_dir / f"{task_id}-{index}.{extension}"
                download = urllib.request.Request(image["url"], headers={"User-Agent": AGENT})
                with urllib.request.urlopen(download, timeout=120) as response:
                    path.write_bytes(response.read())
                print(path)
            return
        if task["status"] == "failed":
            sys.exit(f"Task {task_id} failed: {task['error']['message']}")
        time.sleep(3)
    sys.exit(f"Task {task_id} is still processing. Resume with --resume {task_id}")


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--prompt")
    parser.add_argument("--ref", action="append", default=[])
    parser.add_argument("--ratio", choices=RATIOS)
    parser.add_argument("--size", choices=["1K", "2K", "4K"])
    parser.add_argument("--model", default="gemini-3-pro-image",
                        choices=["gemini-3-pro-image", "gemini-3-pro-image-official"])
    parser.add_argument("--out", default="images")
    parser.add_argument("--timeout", type=int, default=600)
    parser.add_argument("--resume")
    args = parser.parse_args()
    if args.resume:
        task_id = args.resume
    elif args.prompt:
        task_id = submit(args)
        print(f"task {task_id}", file=sys.stderr)
    else:
        parser.error("--prompt or --resume is required")
    wait(task_id, pathlib.Path(args.out), args.timeout)


if __name__ == "__main__":
    main()

What it does:

  • Submits once. --prompt is the text, --ref adds a reference image URL (repeat it for several), and --ratio and --size set imageConfig. The request body is Google's generateContent format with a model field, as the Nano Banana Pro API reference defines it.
  • Prints the task ID as soon as the task is created, before it waits.
  • Polls, then saves. When the task completes, it downloads every image into images/ and prints each path. When the task fails, it prints the reason.
  • Resumes instead of resubmitting. If the wait times out, --resume <task id> checks the same task again. A second submission would be a second paid request.
  • Sends a User-Agent header. Keep that line: some servers reject Python's default one, and without it the download can fail with 403.

You can test it on its own before Claude uses it:

python3 scripts/nano_banana_pro.py --prompt "A ceramic teapot on a linen tablecloth, soft window light" --ratio 16:9 --size 2K

The skill: what goes in SKILL.md?

Claude Code's documentation describes a skill as a directory with a SKILL.md file: "YAML frontmatter between --- markers that tells Claude when to use the skill, and markdown content with the instructions Claude follows when the skill runs." Supporting files, such as a scripts/ folder, sit next to it.

Save this as SKILL.md in the same folder:

---
name: nano-banana-pro
description: Generate or edit an image with Nano Banana Pro (gemini-3-pro-image) through the SeedRouter API and save it into the project. Use when the user asks for an image, illustration, icon, poster or edit of an existing image.
argument-hint: "[what the image should show]"
disable-model-invocation: true
---

Create an image for this request: $ARGUMENTS

1. Write one prompt in full sentences: subject, framing, action, setting and style. Put any text that must appear in the image in double quotes and say where it goes. For an edit, say what changes and what must stay the same.
2. Choose --ratio from 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9 and --size from 1K, 2K, 4K (4K only for print).
3. Reference images must be public HTTPS URLs ending in .jpg, .png, .webp, .heic or .heif, passed with --ref. If the user points to a local file, ask for a public URL instead. Never send base64.
4. Show the user the prompt and options, and wait for approval. Each run is a paid request.
5. Run: python3 ${CLAUDE_SKILL_DIR}/scripts/nano_banana_pro.py --prompt "..." [--ratio R] [--size S] [--ref URL]
6. If it stops with "still processing", run it again with --resume and the same task ID. Never submit the same request twice.
7. Report the saved file paths. If the task failed, explain the reason and suggest a change to the prompt.

Two fields matter here:

  • disable-model-invocation: true means only you can start the skill. Claude Code's documentation recommends it for "workflows with side effects or that you want to control timing". Every run costs money, so Claude should not decide on its own to generate an image.
  • ${CLAUDE_SKILL_DIR} is replaced with the skill's own folder, so the script runs from wherever you installed the skill.

Where do you put the skill, and how do you run it?

Claude Code loads skills from two main places:

LocationPathAvailable in
Personal~/.claude/skills/nano-banana-pro/All your projects on this machine
Project.claude/skills/nano-banana-pro/This repository; commit it to share with your team

The finished folder looks like this:

nano-banana-pro/
โ”œโ”€โ”€ SKILL.md
โ””โ”€โ”€ scripts/
    โ””โ”€โ”€ nano_banana_pro.py

Start Claude Code in a terminal where SEEDROUTER_API_KEY is set, then type the skill name followed by what you want:

/nano-banana-pro a 16:9 hero image for the pricing page: a calm desk with a laptop, a notebook and a cup of tea, soft morning light, muted green palette, no text

Claude shows the prompt and options, you approve, and the image lands in images/. Unless you have pre-approved the command, Claude Code also asks for permission before it runs the script, which is a second check before anything is charged.

For the prompt itself, the Nano Banana Pro prompt guide covers structure, text in images and references in more depth.

Can Codex use the same Nano Banana Pro skill?

Yes. Both tools follow the open Agent Skills standard. OpenAI's Codex documentation says a skill "is a directory with a SKILL.md file plus optional scripts and references", and that the file "must include name and description", which the skill above has.

Codex looks for skills in other folders:

LocationPath
Personal~/.agents/skills/nano-banana-pro/
Repository.agents/skills/nano-banana-pro/

Copy the folder there, or point a symlink at it; both tools follow symlinked skill folders. In Codex, run /skills or type $nano-banana-pro to call it.

Two differences to know:

  • The script path. ${CLAUDE_SKILL_DIR} is a Claude Code substitution. Codex lists each skill's file path to the model, so it can find scripts/nano_banana_pro.py next to SKILL.md, but if it runs the wrong path, tell it where the script is.
  • Automatic use. disable-model-invocation is a Claude Code field. The Codex equivalent is an agents/openai.yaml file in the skill folder with allow_implicit_invocation: false under policy, so Codex runs the skill only when you call it.

The script needs network access to reach api.seedrouter.ai. If your agent runs in a sandbox without it, allow network access for that session.

How do you edit images that are already in your project?

Nano Banana Pro edits from reference images, and the API takes those only as public URLs. A file on your disk has to be hosted somewhere first, for example in your own storage bucket, and then passed with --ref.

Generated images are already hosted: each finished task returns the image as a URL. To refine a result, pass that URL back as a reference with the change you want:

/nano-banana-pro edit the last image: keep the desk and the laptop, change the tea cup to a glass of water, and make the light warmer

Claude reuses the previous image URL with --ref and writes an edit prompt that says what should stay the same. For multi-turn editing that keeps the model's own context between turns, the API reference explains how to send earlier turns back; the script above sends one turn at a time.

If you already use GPT Image 2 from a coding agent, GPT Image 2 in Codex and Claude Code covers that setup, which is built around a prompt you paste into the agent.

Frequently asked questions

Is there an official Nano Banana Pro skill or MCP server from SeedRouter?

No. SeedRouter does not ship one. The skill in this guide is a plain folder you own and can change, and it calls the public API with your key.

Why does the script poll instead of waiting for the image?

Nano Banana Pro requests on SeedRouter are tasks: submitting one returns a task ID, and the image arrives when the task completes. Polling keeps the connection short, and if the wait is interrupted, the task ID is still valid.

Can I use the same skill for Nano Banana 2?

Yes, with small changes. Change the script's --model default and choices to gemini-3.1-flash-image, and add 512 to the --size choices, a size Nano Banana 2 supports and Pro does not. Nano Banana 2 vs Pro vs 2 Lite compares the models.

Does loading the skill cost anything?

No. Claude reading SKILL.md costs nothing on SeedRouter. Each run of the script is one request, charged at the rate on the model page, and a task that fails is not charged.

Start generating from Claude Code

Create the folder, export your key, and try /nano-banana-pro on a small image first. Current prices are on the Nano Banana Pro page, and every request field is in the API reference.

Related guides