GEMINI LABJP
API — The deprecation of temperature, top_p, and top_k is a silent no-op rather than an error: requests still return 200 and the values are simply ignoredAUDIT — There is no runtime signal to catch it, so auditing means searching your codebase statically for the parameters, a gap developers are actively discussingCHECK — Send the same prompt several times at temperature 0 and again at 1, then compare the spread of the outputs, and you can confirm for yourself that nothing changesMODELS — Gemini 3.7 Flash scores 65.3% on DeepSWE v1.1 and 43.6% on FrontierCode 1.1 Main, with introductory pricing available through December 31, 2026SEARCH — File Search now supports multimodal search through gemini-embedding-2, so images can be embedded and searched directly without a transcription stepDEPRECATION — gemini-robotics-er-1.6-preview shuts down on August 31, four days from now, with the ER 2 line in public preview since July 30 as the migration pathAPI — The deprecation of temperature, top_p, and top_k is a silent no-op rather than an error: requests still return 200 and the values are simply ignoredAUDIT — There is no runtime signal to catch it, so auditing means searching your codebase statically for the parameters, a gap developers are actively discussingCHECK — Send the same prompt several times at temperature 0 and again at 1, then compare the spread of the outputs, and you can confirm for yourself that nothing changesMODELS — Gemini 3.7 Flash scores 65.3% on DeepSWE v1.1 and 43.6% on FrontierCode 1.1 Main, with introductory pricing available through December 31, 2026SEARCH — File Search now supports multimodal search through gemini-embedding-2, so images can be embedded and searched directly without a transcription stepDEPRECATION — gemini-robotics-er-1.6-preview shuts down on August 31, four days from now, with the ER 2 line in public preview since July 30 as the migration path
Articles/API / SDK
API / SDK/2026-07-13Advanced

When responseSchema Can't Do $ref: Handling Recursive Schemas in Production with responseJsonSchema

Gemini's responseSchema is an OpenAPI subset with no $ref or $defs, so it can't express shared definitions or recursion. Here's how I moved to responseJsonSchema to reuse localized fields and handle a recursive category tree in production.

gemini109gemini-api283structured-output28json-schema2python104

Premium Article

The first wall I hit trying to have Gemini classify wallpaper categories was that I couldn't express a category tree in the schema at all. Nature to Mountain to Snowy Mountain: a parent holds children, which hold their own children, and the depth varies by image. When I tried to write a self-reference in responseSchema, it simply wouldn't accept $ref. So I hand-inlined three levels and gave up on anything deeper.

What dissolved that compromise was responseJsonSchema. It's a separate field from responseSchema, and it understands JSON Schema's $ref and $defs. You can define something once and reuse it, and you can declare recursion through self-reference. As an indie developer dealing with multilingual, nested structured output, that difference maps directly onto how maintainable the whole pipeline is. This piece walks through the differences, the migration, and the traps I actually stepped on.

What responseSchema can't express

responseSchema accepts a subset of the OpenAPI 3.0 Schema object. You get type, properties, items, enum, nullable, propertyOrdering, and the later-added anyOf. But it does not interpret $ref (a reference to another definition) or $defs (a place for reusable definitions), which are at the heart of JSON Schema.

That gap bites in two situations.

First, when you want to reuse the same shape in several places. In my case, a LocalizedText object representing a Japanese/English pair is needed in three spots: the category name, the description, and the tags. With responseSchema you inline it three times, the schema grows vertically, and every time you tweak the required fields you have to fix all three without missing one.

Second, recursion. A structure like a category tree, where a node contains itself as a child, can't be expressed without references. Expanding to a fixed depth is a workaround, but with variable-depth data it breaks somewhere eventually.

AspectresponseSchemaresponseJsonSchema
Schema dialectSubset of OpenAPI 3.0JSON Schema ($ref/$defs)
Reusing shared definitionsNo (inline only)Yes, via $defs + $ref
Recursion / self-referenceNoYes
propertyOrderingSupportedNo such concept
Using bothMutually exclusive (one at a time)

Define the localized object once with $defs

Start with reuse. Using google-genai (the newer Python SDK), define LocalizedText once in $defs and reference it from several places with $ref.

from google import genai
from google.genai import types
 
client = genai.Client(api_key="YOUR_API_KEY")
 
# Define LocalizedText once, reference it from multiple places
schema = {
    "type": "object",
    "$defs": {
        "LocalizedText": {
            "type": "object",
            "properties": {
                "ja": {"type": "string"},
                "en": {"type": "string"},
            },
            "required": ["ja", "en"],
        }
    },
    "properties": {
        "name": {"$ref": "#/$defs/LocalizedText"},
        "description": {"$ref": "#/$defs/LocalizedText"},
        "tags": {
            "type": "array",
            "items": {"$ref": "#/$defs/LocalizedText"},
        },
    },
    "required": ["name", "description", "tags"],
}
 
resp = client.models.generate_content(
    model="gemini-2.5-flash",
    contents="Give a snowy mountain wallpaper a category name, description, and tags in Japanese and English.",
    config=types.GenerateContentConfig(
        response_mime_type="application/json",
        response_json_schema=schema,
    ),
)
print(resp.text)

The formatted output looks like this:

{
  "name": {"ja": "雪山", "en": "Snowy Mountain"},
  "description": {"ja": "雪をかぶった山の風景", "en": "A mountain landscape covered in snow"},
  "tags": [
    {"ja": "自然", "en": "Nature"},
    {"ja": "冬", "en": "Winter"}
  ]
}

There's a single definition of LocalizedText. If I later decide to require zh alongside ja/en, I edit exactly one block inside $defs. Back when I inlined it under responseSchema, I applied the same change by hand in three places. The more definitions you accumulate, the more that difference compounds.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
If you've been inlining the same localized object over and over because responseSchema can't do $ref, you'll learn to define it once in $defs and reuse it everywhere
You'll declare a category tree of arbitrary depth using a self-referencing $ref instead of the fixed-depth workaround, and receive it with working code
You'll be able to decide between responseSchema and responseJsonSchema per task, knowing the feature gaps and the traps in each
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
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 →

Related Articles

API / SDK2026-03-20
Build an AI Data Analysis Agent with Gemini API — Combining Code Execution, Function Calling, and Structured Output
Learn how to build a production-ready AI data analysis agent in Python that combines Gemini API's Code Execution, Function Calling, and Structured Output to automatically analyze CSV/Excel data, generate visualizations, and produce structured reports.
API / SDK2026-08-22
Once You Pass Twenty Mediation Groups, How Do You Find the Setting That Went Missing?
As ad mediation groups multiply, missing sources and type drift accumulate quietly. Here is the split I settled on: normalize the settings into one matrix, let code confirm the gaps, and send Gemini only the cells that need judgment.
API / SDK2026-07-14
When Gemini's executed result and its prose disagree on a number — a gate that trusts only code_execution_result
Gemini Code Execution returns the value it actually computed and the sentence describing it as separate parts. Trust the prose and you can inherit a hallucinated number. Here is a verification gate, in working code, that extracts the executed result as the single source of truth and rejects prose that disagrees.
📚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
See all →