Add live web search to any OpenRouter model
OpenRouter gives you one API key and one OpenAI-compatible endpoint in front of 300+ models — GPT-4o, Claude, Gemini, Llama, Mistral, DeepSeek, and a whole free tier. What it doesn't give any of those models is access to the live web. Wire in Superhighway and you close that gap once: every model OpenRouter routes to can now search real-time web results. No separate account per provider, no separate search vendor — one setup, any LLM, fresh web content.
Why OpenRouter + Superhighway
OpenRouter handles model routing; Superhighway handles live web search. The division of labour is clean: you keep a single LLM key and a single search key, and you can swap which model answers a query — Claude today, a free Llama tomorrow — without touching the search plumbing. Two paths below: function calling with a free key (one tool, full control, works against every model), or the MCP server (all five tools, less code) for any OpenRouter-powered agent that speaks MCP.
Two paths
| Path | Best for | What you need |
|---|---|---|
| Function calling + REST | One tool (web search), explicit control, switch models with one line | A free Superhighway key + an OpenRouter key |
| MCP server | All five tools auto-loaded; any MCP-aware agent; optional x402 wallet | npx + a free key (or a funded Base wallet) |
Get your keys
Grab a free-tier Superhighway key at superhighway.walls.sh (no credit card) and an OpenRouter key at openrouter.ai. Export both:
export OPENROUTER_API_KEY=sk-or-...
export SUPERHIGHWAY_API_KEY=sk_... # from /pricing
Path 1 — function calling (REST API + free key)
OpenRouter is OpenAI-compatible, so the standard openai Python SDK works — just point its base_url at OpenRouter. Define the web-search tool once in OpenAI's tool schema; every model on OpenRouter understands it.
pip install openai requests
import json
import os
import requests
from openai import OpenAI
# Point the OpenAI SDK at OpenRouter
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"],
)
def web_search(query: str, limit: int = 5) -> dict:
"""Call Superhighway search."""
resp = requests.get(
"https://superhighway.walls.sh/search",
params={"q": query, "limit": limit},
headers={"Authorization": f"Bearer {os.environ['SUPERHIGHWAY_API_KEY']}"},
)
resp.raise_for_status()
return resp.json()
# Define the tool in OpenAI format — works with ANY OpenRouter model
tools = [{
"type": "function",
"function": {
"name": "web_search",
"description": "Search the live web for real-time information. "
"Returns ranked results with title, URL, and description.",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "The search query"},
"limit": {"type": "integer", "description": "Max results (default 5)"},
},
"required": ["query"],
},
},
}]
Now run the standard two-call tool loop: ask the model with tools=[...], run any tool call it returns, append the result as a tool message, and call again so it can answer from live data.
PROMPT = "What are the latest developments in AI agents?"
MODEL = "anthropic/claude-opus-4-8" # one line away from any other model
messages = [{"role": "user", "content": PROMPT}]
response = client.chat.completions.create(
model=MODEL,
messages=messages,
tools=tools,
tool_choice="auto",
)
msg = response.choices[0].message
if msg.tool_calls:
messages.append(msg) # the assistant turn that requested the tool
for call in msg.tool_calls:
args = json.loads(call.function.arguments)
result = web_search(**args)
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result),
})
# Second call: the model reads the results and writes the answer
final = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
print(final.choices[0].message.content)
else:
print(msg.content)
Switch models by changing one line
This is the OpenRouter payoff: the tool definition, the search call, and the loop never change. Only the MODEL string moves — across frontier models, a free-tier Llama, or a cheap DeepSeek — and your web search works against all of them.
MODELS = [
"anthropic/claude-opus-4-8",
"openai/gpt-4o",
"google/gemini-2.0-flash",
"meta-llama/llama-3.3-70b-instruct:free", # free tier
"deepseek/deepseek-chat",
]
for model in MODELS:
print(f"=== {model} ===")
# ...run the same tool loop above with MODEL = model...
Pick a frontier model for hard reasoning, a :free model for cheap high-volume runs — the live-web capability rides along regardless.
Path 2 — MCP server (all five tools, less code)
Superhighway also ships an MCP server that exposes all five tools — web_search, news_search, image_search, scrape, and research — in one command. Any OpenRouter-powered agent that speaks MCP (a framework MCP client, Claude Code, Cursor, or a custom loop) can load them with no per-tool schema to write:
npx -y superhighway-mcp
Add it to your MCP client's config and pass the same free key:
{
"mcpServers": {
"superhighway": {
"command": "npx",
"args": ["-y", "superhighway-mcp"],
"env": { "SUPERHIGHWAY_API_KEY": "sk_..." }
}
}
}
The agent — running on whatever model you've pointed OpenRouter at — now discovers all five tools and picks the right one per turn.
x402 pay-per-call. To let an autonomous agent pay per call with no API key, swap the env for a funded Base wallet:
"env": { "AGENT_PRIVATE_KEY": "0x...", "X402_NETWORK": "base" }
Each call then settles automatically in USDC on Base — $0.001 a search — via the x402 protocol. No signup, no key, no human in the loop.
Which tools are available
| Tool / endpoint | What it returns | Price |
|---|---|---|
web_search / /search | Ranked web results — title, URL, description | $0.001 |
news_search / /news | Recent news with published dates | $0.001 |
image_search / /images | Image URLs, thumbnails, source pages | $0.001 |
scrape / /scrape | Any URL → clean markdown text | $0.002 |
research / /research | Search + read top pages in one call | $0.005 |
Full API reference: /openapi.json. Get a free API key or learn the x402 wallet flow.