Gemini 3.x Live never returns groundingMetadata for Google Search

Hi all,

I run a voice agent on gemini-3.8-live with the Google Search tool enabled. Search clearly works: the model answers questions about current weather and last night’s scores correctly. But the Live API never tells the client that a search happened. No groundingMetadata, no webSearchQueries, no searchEntryPoint, no toolUsePromptTokenCount. Older Live models do send all of this.

Since Gemini 3 bills Search grounding per executed query, this leaves me with no way to see, attribute, or cap search spend from inside a session.

What I tested (Gemini API key, not Vertex; google-genai 2.20.0; AUDIO output; same prompt each time: “Search the web: what is the current weather in Chicago right now?”). I read the raw server JSON before the SDK converter, so nothing was dropped by the SDK.

Model API version Search ran? Search info in server messages
gemini-2.5-flash-native-audio-preview-12-2025 v1beta Yes serverContent.groundingMetadata with webSearchQueries: ["current weather in Chicago"], groundingChunks, groundingSupports, searchEntryPoint, plus a codeExecutionResult part (“Looking up information on Google Search.”)
gemini-3.1-flash-live-preview v1beta Yes (live weather, humidity, cloud cover) Nothing
gemini-3.8-live v1beta Yes Nothing
gemini-3.8-live v1alpha Yes Nothing
gemini-3.8-live v1beta, temperature: 1.0 Yes Nothing

Control: the same gemini-3.8-live session with the Google Search tool removed answers “I’m sorry, I cannot look up the weather information for you right now” and “I am unable to access real time information”. So the 3.x answers above really did come from search.

On 3.x, every server message only ever contains modelTurn (audio), outputTranscription, generationComplete, turnComplete, sessionResumptionUpdate, and usageMetadata with promptTokenCount, responseTokenCount, thoughtsTokenCount, totalTokenCount and the modality details. The field set is identical with and without search enabled.

Why this matters:

  1. Cost: on Gemini 3, each search query the model runs is billed (5,000 free per month, then $14 per 1,000). With no signal per session, the only view is the daily billing SKU, which can’t be tied to a user or conversation. Given the over counting incident earlier this year (thread), I would also like to reconcile my own counts against billing.
  2. Docs say it should be there: the Live API reference lists groundingMetadata on BidiGenerateContentServerContent and toolUsePromptTokenCount in UsageMetadata, and staff confirmed in this 2025 thread that Live returns grounding metadata. That is still true for 2.5, but not for 3.1 Flash Live or 3.8 Live.

Questions:

  1. Is this a known gap or a bug for the Gemini 3.x Live models?
  2. Is there a setup option I am missing that turns grounding metadata on for 3.x Live?
  3. If it is not planned, is there any other way to count billed search queries for Live, per session or per request?
Repro (google-genai 2.20.0, reads the raw server JSON)
import asyncio, os
from google import genai
from google.genai import _live_converters as lc, live, types

wire, orig = [], lc._LiveServerMessage_from_mldev
def tap(resp, *a, **k):  # raw JSON before the SDK drops unknown fields
    wire.append(resp); return orig(resp, *a, **k)
live.live_converters._LiveServerMessage_from_mldev = tap

async def probe(client, model):
    wire.clear()
    cfg = types.LiveConnectConfig(response_modalities=["AUDIO"],
        tools=[types.Tool(google_search=types.GoogleSearch())],
        output_audio_transcription=types.AudioTranscriptionConfig())
    async with client.aio.live.connect(model=model, config=cfg) as s:
        await s.send_client_content(turns=types.Content(role="user", parts=[types.Part(
            text="Search the web: what is the current weather in Chicago right now?")]), turn_complete=True)
        async for m in s.receive():
            if m.server_content and m.server_content.turn_complete: break
    g = [m["serverContent"]["groundingMetadata"] for m in wire if "groundingMetadata" in m.get("serverContent", {})]
    said = "".join(m.get("serverContent", {}).get("outputTranscription", {}).get("text", "") for m in wire)
    print(model, "| webSearchQueries:", [q for x in g for q in x.get("webSearchQueries", [])], "|", said[:100])

async def main():
    client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
    for model in ["gemini-2.5-flash-native-audio-preview-12-2025", "gemini-3.1-flash-live-preview", "gemini-3.8-live"]:
        await probe(client, model)
asyncio.run(main())

Output (Oct 2, 2026):

gemini-2.5-flash-native-audio-preview-12-2025 | webSearchQueries: ['current weather in Chicago'] | Right now it is light rain in Chicago, with a temperature of 62°F...
gemini-3.1-flash-live-preview | webSearchQueries: [] | It's currently raining in Chicago with a temperature of around 64 degrees Fahrenheit...
gemini-3.8-live | webSearchQueries: [] | Right now in Chicago, it is currently raining with a temperature of 64° Fahrenheit.

Raw captures (audio removed) available on request. Happy to run more tests.

Thanks!

Hello @Alex_Yu1 ,

Thanks for the detailed report. Gemini 3.x Live models (gemini-3.8-live and gemini-3.1-flash-live-preview) now return groundingMetadata when Grounding with Google Search runs. See Understanding the grounding response for what each field contains.

  • If you send input with send_realtime_input (text or audio), the full groundingMetadata is returned, including groundingChunks and groundingSupports.
  • If you send turns with send_client_content (as in your repro), you currently get webSearchQueries and searchEntryPoint, but not groundingChunks or groundingSupports. We’re looking into this.
  • Each search query the model runs is listed in webSearchQueries (see Understanding the grounding response), and each search query is billed, with empty queries ignored.