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:
- 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.
- Docs say it should be there: the Live API reference lists
groundingMetadataonBidiGenerateContentServerContentandtoolUsePromptTokenCountinUsageMetadata, 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:
- Is this a known gap or a bug for the Gemini 3.x Live models?
- Is there a setup option I am missing that turns grounding metadata on for 3.x Live?
- 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!