◉GEMINI LABJP
●VEO 3.1 — The three veo-3.1 preview models shut down Oct 22, 14 days left. Move to gemini-omni-1.1-flash●OMNI — gemini-omni-flash-preview also shuts down Oct 22, not Sep 30. Check the deprecations table●NANO 2.1 — gemini-nano-banana-2.1 is GA. gemini-3.1-flash-image is deprecated with no shutdown date yet●CLI 0.63 — Gemini CLI stable is v0.63.0, with clearer retry progress on reconnect●TTS/LIVE — 2.5-era audio and TTS previews shut down Nov 17, 40 days left●NEW — Veo 3.1 previews stop Oct 22: take inventory before you swap●VEO 3.1 — The three veo-3.1 preview models shut down Oct 22, 14 days left. Move to gemini-omni-1.1-flash●OMNI — gemini-omni-flash-preview also shuts down Oct 22, not Sep 30. Check the deprecations table●NANO 2.1 — gemini-nano-banana-2.1 is GA. gemini-3.1-flash-image is deprecated with no shutdown date yet●CLI 0.63 — Gemini CLI stable is v0.63.0, with clearer retry progress on reconnect●TTS/LIVE — 2.5-era audio and TTS previews shut down Nov 17, 40 days left●NEW — Veo 3.1 previews stop Oct 22: take inventory before you swap
Articles/API / SDK
◈ API / SDK/2026-10-08Intermediate

When a Deprecation Has No Shutdown Date: Re-Picking an Image Model on Three Conditions

gemini-nano-banana-2.1 went GA on October 6, and gemini-3.1-flash-image was deprecated with no shutdown date. Here is a table and a short script for choosing a replacement by resolution, reference images, and search grounding.

Gemini API246Image Generation7Model Migration8Nano Banana3Python50

I was reading through the model IDs in my code when I noticed a new row in the deprecations table. The October 6 changelog says only that gemini-3.1-flash-image is deprecated, with no shutdown date announced. Oddly, a missing date is harder to plan around than a bad one.

My starting point: when a deprecation has no date, decide by which conditions let you move today, not by when the model will stop. Everything below comes from the official docs as I read them on October 8, 2026. The numbers are the docs' numbers, not mine.

Facts first

  • gemini-nano-banana-2.1 became GA on October 6.
  • gemini-3.1-flash-image is deprecated and points to gemini-nano-banana-2.1. The deprecations table says "No shutdown date announced."
  • The same page notes that a listed date is the earliest a model might be retired. When a date appears, treat it as your deadline.
  • An older note of mine said October 29. The current table has no such date, so if you see it elsewhere, check the official table.

Three models, side by side

Aspectgemini-nano-banana-2.1gemini-3.1-flash-lite-imagegemini-3.1-flash-image
StatusGA (recommended)AvailableDeprecated, no date
Resolution1K / 2K / 4K1K only512px / 1K / 2K / 4K
Reference images10 object, 4 character14 object (no character figure listed)10 object, 4 character
Google Search groundingYes (image search too)NoYes (image search too)
Thinking levelsminimal / medium / high (default medium)minimal / highminimal / high (default minimal)

Building the table surprised me: 512px output exists only on the deprecated model. If a thumbnail path asks for 512px, you cannot simply swap the ID. You would generate at 1K and downscale, or find another remedy.

Make the choice reproducible

A table is easy to misread, so I encoded it. This script needs no API key and runs on plain Python.

MODELS = {
    "gemini-nano-banana-2.1":      {"sizes": {"1K", "2K", "4K"}, "objects": 10, "characters": 4, "grounding": True,  "thinking": {"minimal", "medium", "high"}, "status": "GA"},
    "gemini-3.1-flash-lite-image": {"sizes": {"1K"},             "objects": 14, "characters": 0, "grounding": False, "thinking": {"minimal", "high"},           "status": "stable"},
    "gemini-3.1-flash-image":      {"sizes": {"512", "1K", "2K", "4K"}, "objects": 10, "characters": 4, "grounding": True, "thinking": {"minimal", "high"},    "status": "deprecated"},
}
 
def pick(size="1K", objects=0, characters=0, grounding=False, thinking=None):
    out = []
    for name, m in MODELS.items():
        why = []
        if size not in m["sizes"]: why.append(f"{size} unsupported")
        if objects > m["objects"]: why.append(f"max {m['objects']} object refs")
        if characters > m["characters"]: why.append(f"max {m['characters']} character refs")
        if grounding and not m["grounding"]: why.append("no search grounding")
        if thinking and thinking not in m["thinking"]: why.append(f"thinking={thinking} unsupported")
        if m["status"] == "deprecated": why.append("deprecated (no date)")
        out.append((name, why))
    return out

I ran it against four use cases. The output (Japanese labels in my original run, translated here):

[A 1K bulk wallpapers, no references]
   OK   gemini-nano-banana-2.1
   OK   gemini-3.1-flash-lite-image
   NG   gemini-3.1-flash-image - deprecated (no date)
[B 4K, 2 character refs, search on]
   OK   gemini-nano-banana-2.1
   NG   gemini-3.1-flash-lite-image - 4K unsupported / max 0 character refs / no search grounding
   NG   gemini-3.1-flash-image - deprecated (no date)
[C 512px thumbnail]
   NG   gemini-nano-banana-2.1 - 512 unsupported
   NG   gemini-3.1-flash-lite-image - 512 unsupported
   NG   gemini-3.1-flash-image - deprecated (no date)
[D 12 references, 1K]
   NG   gemini-nano-banana-2.1 - max 10 object refs
   OK   gemini-3.1-flash-lite-image
   NG   gemini-3.1-flash-image - max 10 object refs / deprecated (no date)

Case A passes on two models, so it becomes a quality-versus-cost call; the cost design for batch images with Nano Banana 2 Lite covers that side. Case B leaves only nano-banana-2.1. Case C has no valid target at all.

The call shape in the official docs

The current image generation page shows interactions.create, not generate_content:

import base64
from google import genai
 
client = genai.Client()
 
interaction = client.interactions.create(
    model="gemini-nano-banana-2.1",
    input="Create a picture of a nano banana dish in a fancy restaurant with a Gemini theme",
)
 
with open("generated_image.png", "wb") as f:
    f.write(base64.b64decode(interaction.output_image.data))

Output options go in response_format={"type": "image", "aspect_ratio": "16:9", "image_size": "2K"}. The docs say image_size needs an uppercase K and rejects "1k". Thinking goes in generation_config={"thinking_level": "high"}. I copied this block from the docs and did not call the API myself, so try one image with your own key first.

Swapping the ID does not give you the same behavior

One more difference is easy to miss. The docs say Gemini 3 image models always think (it cannot be turned off in the API), may generate up to two interim images, and bill thinking tokens. The default level is medium on nano-banana-2.1 and minimal on 3.1-flash-image.

So if you swap only the ID and leave thinking unset, latency and cost can look different because of the defaults alone. I have more than once swapped a model expecting the same quality for less, then puzzled over the billing breakdown later. On day one of a migration I now set thinking_level explicitly to the old default (minimal) before comparing, so a difference can be traced to either the model or the setting.

The order I would follow

  1. Search your code for gemini-3.1-flash-image and list the resolution, reference count, and search use at each call site.
  2. Feed those conditions to the script and treat every OK as a candidate.
  3. Set aside any call site that fails on every candidate, such as 512px or 12-plus references, and decide whether to change the spec or hold until a date appears.

Even when you hold, keep the model ID in one switchable place so the day a date is announced is a small job. I wrote about moving IDs into configuration in a pipeline that survives image model migrations and deprecations.

While no date exists, the only preparation worth doing is the inventory of conditions. When the table updates next, I will start from my own NG rows.

Share

Thank You for Reading

Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $15 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

◈ API / SDK2026-08-17
After generated_images Disappeared: Three Branches Between the Response and a Saved File
Once you switch to generate_content, the line that breaks is usually the save. Here are the three branches on the response side - zero image parts, a shifting image count, and picking the extension - plus a receiver function you can drop in.
◈ API / SDK2026-08-14
Rewriting generate_images as generate_content, where the arguments actually go
Ahead of the August 17 Imagen shutdown, I checked the SDK directly: of the 17 arguments in GenerateImagesConfig, only 5 move across unchanged. Here is the mapping, a compatibility layer that keeps callers working, and a way to verify the request shape without an API key.
◈ API / SDK2026-09-22
When Function Calls Turn Into Plain Text, Suspect the Property Names in Your Tool Declarations
A record of chasing MALFORMED_FUNCTION_CALL after output limits and schema conflicts were ruled out, bisecting nine tool declarations down to one property name, and adding a linter that stops bad names before they ship.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links