Skip to the page

Gemini Live API

Give a Gemini Live voice session a web search it calls as a function. The SpecStream Search API answers with the fact, the page it was read from and the ranked results. The model speaks the answer, and your code keeps every source URL.

SpecStream for Gemini Live APIAgent frameworks & builders

What you need

  • A Gemini API key, Python with the Google Gen AI SDK and httpx (pip install google-genai httpx).
  • A SpecStream API key. New accounts start with $10 of credit, no card. Create an account.
  1. 1. Create a SpecStream API key

    In SpecStream, open Agents & API, choose API keys, then Create API key. Give it execute permission and a spending limit above zero, so it can search. Set it as SPECSTREAM_API_KEY on the server that runs your session. Keep the key on the server and out of the browser.

  2. 2. Declare the search function

    The Live API takes your functions as function_declarations in the session config. Declare one that takes the question. Add each block of this guide to one file, live_search.py, in order:

    live_search.py
    web_search = {
    "name": "web_search",
    "description": "Search the live web. Use it for news, prices, schedules and any fact that may have changed since training.",
    "parameters": {
    "type": "object",
    "properties": {
    "q": {"type": "string", "description": "The question, in everyday words"}
    },
    "required": ["q"],
    },
    }
    tools = [{"function_declarations": [web_search]}]
    config = {"response_modalities": ["AUDIO"], "tools": tools}
  3. 3. Call the Search API

    The function calls GET /v1/web-search with the question in q. num sets how many results come back. Return only what the model needs to speak, so the reply stays short:

    live_search.py
    import os
    import httpx
    specstream = httpx.AsyncClient(
    base_url="https://api.specstream.ai",
    headers={"Authorization": f"Bearer {os.environ['SPECSTREAM_API_KEY']}"},
    timeout=30,
    )
    async def search(q: str) -> dict:
    r = await specstream.get("/v1/web-search", params={"q": q, "num": 5})
    r.raise_for_status()
    data = r.json()["data"]
    answer = data.get("answer") or {}
    return {
    "answer": answer.get("value"),
    "answer_page": answer.get("page"),
    "sources": [
    {"title": s.get("title"), "url": s["url"], "snippet": s.get("snippet")}
    for s in data["sources"]
    ],
    }
  4. 4. Answer each tool call

    The Live API does not answer tool calls for you. When a message carries tool_call, run the search for each function call and send a FunctionResponse with the call's id and name through session.send_tool_response. The model then speaks the answer:

    live_search.py
    import asyncio
    import wave
    from google import genai
    from google.genai import types
    client = genai.Client(http_options={"api_version": "v1beta"})
    model = "gemini-3.8-live"
    async def main():
    async with client.aio.live.connect(model=model, config=config) as session:
    await session.send_client_content(
    turns={"parts": [{"text": "What did the European Central Bank decide at its last meeting?"}]}
    )
    wf = wave.open("audio.wav", "wb")
    wf.setnchannels(1)
    wf.setsampwidth(2)
    wf.setframerate(24000) # Output is 24kHz
    async for response in session.receive():
    if response.data is not None:
    wf.writeframes(response.data)
    elif response.tool_call:
    function_responses = []
    for fc in response.tool_call.function_calls:
    try:
    result = await search(fc.args["q"])
    except httpx.HTTPError as e:
    result = {"error": str(e)}
    function_responses.append(
    types.FunctionResponse(id=fc.id, name=fc.name, response={"result": result})
    )
    await session.send_tool_response(function_responses=function_responses)
    wf.close()
    asyncio.run(main())
  5. 5. Run it

    Set GEMINI_API_KEY and SPECSTREAM_API_KEY, run python live_search.py, and play audio.wav. Print result inside the loop to see the pages the answer came from.

Why a function and not the Google Search tool

The Live API also takes google_search as a built in tool. A SpecStream function differs in three ways that matter in a voice app:

  • Your code holds the results. Each call returns the answer, the page it was read from and the ranked results with their URLs, so you can log them, show them on screen next to the voice reply, or drop a site you do not trust.
  • It sits next to your other functions. On Vertex AI, Google's documentation says search tools cannot be combined with function calling in the same setup request. web_search is a function, so it goes in the same function_declarations list as the rest of your tools.
  • The price is per call, from your balance. Grounding with Google Search on the Live models is 5,000 free search requests a month, shared across Gemini 3 and newer models, then $14 per 1,000 requests, with a cap of 1,500 requests a day. The Search API is $5 per 1,000 searches of up to 10 results, and it is the most accurate web search API on SimpleQA Verified and WideSearch (scores and method).

Keep talking while it searches

On gemini-3.8-live, the conversation can go on while the search runs. Add "behavior": "NON_BLOCKING" to the declaration, and a scheduling value to each response: INTERRUPT speaks the result at once, WHEN_IDLE waits until the model finishes what it is saying, and SILENT keeps it for later in the conversation. Run each search in its own task with asyncio.create_task, so the loop keeps reading audio while it waits. gemini-3.1-flash-live-preview supports synchronous calls only, and gemini-3.8-live-extended-thinking supports asynchronous calls only.

Text from the top pages

Add "passages": "true" to params when the model needs more than snippets. Each of the first num sources then carries an excerpt, the text of its page closest to the question, up to about 3,500 characters. The call takes up to about a second more.

Read a page the caller names

To read a page by its address, declare a second function and answer it with the Extract API. It returns the main text of the page, and of PDF, Word and Excel files:

Cost

Each web_search call is one Search API call at $5 per 1,000 searches of up to 10 results. Each page read with text is $0.80 per 1,000 pages, and a page with no text is not charged. Both come out of your balance with no quote and no approval, and the key's spending limit caps what it can spend. New accounts start with $10 of credit, no card. The Search API reference lists every parameter, and balance and billing has the details.

Disconnect

Remove the functions from the session config. Then open Agents & API in your workspace, choose API keys, and revoke the key. Questions go to [email protected].

More

SpecStream for Gemini Live API covers what it does in Gemini Live API on one page. Every other tool has its guide in Connect your tools. You see the most a request can cost before it starts, and the matches are paid from your balance. Billing has the details. For help, email [email protected].