Add live web search to a Gemini app with function calling

Superhighway guides

Gemini's function calling lets you extend the model with external tools: you declare a function, Gemini decides when to call it, and you run it and hand back the result. Superhighway exposes live web search as a plain HTTP API, so it slots into that flow naturally — no extra infrastructure, just an API key.

1. Install

pip install google-generativeai requests

2. Define the search tool and call Gemini

The full function-calling round trip with the google-generativeai SDK: declare a FunctionDeclaration, attach it to a GenerativeModel, send a message, run the function Gemini asks for, and send the result back for the final answer.

import os, requests
import google.generativeai as genai

genai.configure(api_key=os.environ["GOOGLE_API_KEY"])

SUPERHIGHWAY_KEY = os.environ["SUPERHIGHWAY_API_KEY"]

def web_search(query: str, count: int = 5) -> dict:
    """Search the live web and return structured results."""
    r = requests.get(
        "https://superhighway.walls.sh/search",
        params={"q": query, "count": count},
        headers={"Authorization": f"Bearer {SUPERHIGHWAY_KEY}"},
        timeout=15,
    )
    r.raise_for_status()
    return r.json()

search_tool = genai.protos.Tool(function_declarations=[
    genai.protos.FunctionDeclaration(
        name="web_search",
        description="Search the live web for up-to-date information. Use for current events, recent releases, or anything that may not be in the model's training data.",
        parameters=genai.protos.Schema(
            type=genai.protos.Type.OBJECT,
            properties={
                "query": genai.protos.Schema(type=genai.protos.Type.STRING, description="The search query"),
                "count": genai.protos.Schema(type=genai.protos.Type.INTEGER, description="Number of results (default 5, max 10)"),
            },
            required=["query"],
        ),
    )
])

model = genai.GenerativeModel("gemini-1.5-flash", tools=[search_tool])
chat = model.start_chat()

response = chat.send_message("What are the latest developments in AI agent frameworks?")

# Handle function call
if response.candidates[0].content.parts[0].function_call.name == "web_search":
    fc = response.candidates[0].content.parts[0].function_call
    search_result = web_search(**{k: v for k, v in fc.args.items()})

    response = chat.send_message(
        genai.protos.Content(
            parts=[genai.protos.Part(
                function_response=genai.protos.FunctionResponse(
                    name="web_search",
                    response={"result": search_result},
                )
            )]
        )
    )

print(response.text)

Gemini returns a function_call part when it wants a tool. You execute it, wrap the JSON in a FunctionResponse, and send it back on the same chat — the model then writes its grounded answer from the live results.

3. Multi-tool setup (search + news)

Add the /news endpoint with the same pattern — one more FunctionDeclaration and one more local function. Register both declarations on a single Tool:

news_tool_decl = genai.protos.FunctionDeclaration(
    name="news_search",
    description="Search for recent news articles on a topic.",
    parameters=genai.protos.Schema(
        type=genai.protos.Type.OBJECT,
        properties={
            "query": genai.protos.Schema(type=genai.protos.Type.STRING),
        },
        required=["query"],
    ),
)

def news_search(query: str) -> dict:
    r = requests.get(
        "https://superhighway.walls.sh/news",
        params={"q": query, "count": 5},
        headers={"Authorization": f"Bearer {SUPERHIGHWAY_KEY}"},
        timeout=15,
    )
    r.raise_for_status()
    return r.json()

model = genai.GenerativeModel(
    "gemini-1.5-flash",
    tools=[genai.protos.Tool(function_declarations=[
        # search_tool declaration from above
        search_tool.function_declarations[0],
        news_tool_decl,
    ])]
)

When you expose more than one tool, branch on the function name in the response: read fc.name, dispatch to the matching Python function (web_search vs news_search), and send back a FunctionResponse tagged with that same name. The handling loop from section 2 stays identical otherwise.

4. MCP path (for the full 5-tool suite)

If you need all five tools — web_search, news_search, image_search, scrape_page, and research — and want fully autonomous x402 payments instead of an API key, run the MCP server: npx -y superhighway-mcp.

As of writing, Gemini does not natively speak MCP, so you'd connect it through an MCP bridge, or use Google ADK, whose MCPToolset loads the whole suite directly. See the Google ADK guide for that path.

5. Function calling vs ADK vs MCP

ApproachBest forWhat you need
Function calling (this guide)Direct control, custom app logic, 1–2 toolsgoogle-generativeai + API key
Google ADK + MCPToolsetAgent framework, all 5 tools, x402 autonomous paymentsgoogle-adk + funded wallet
MCP bridgeFull tool suite in an MCP client (Claude, Cursor)npx

Get your API key at /pricing (free tier: 1,000 calls/month). For the full tool suite with autonomous x402 payments, see the Google ADK guide.