Web search
web_search is an MCP tool your agent calls on the hosted MCP server.
It runs a web query through a configured search provider and returns a
normalized result set, identical in shape across providers.
Every search is billed against your organization’s plan — a sponsored monthly allowance covers normal use, and nothing beyond it is charged unless an owner explicitly opts in. All the controls live on one dashboard page, Web Search under your organization (owners only).
Calling it
Section titled “Calling it”{ "query": "rust async cancellation patterns", "provider": "brave", "count": 5}| Argument | Required | Notes |
|---|---|---|
query |
yes | 1–400 characters. |
provider |
no | One of the provider keys below. Omit to use your organization’s default provider. |
count |
no | Result count, clamped to the provider’s supported range. |
The result is the same shape no matter which provider served it:
{ "query": "rust async cancellation patterns", "provider": "brave", "results": [ { "title": "…", "url": "…", "snippet": "…", "published_date": "2026-08-01", "score": 0.87 } ]}An empty results array is a valid answer, not an error.
Providers
Section titled “Providers”| Provider key | Provider |
|---|---|
brave |
Brave Search |
exa |
Exa |
kagi |
Kagi |
Provider resolution, in order:
- Explicit
provider— must be a known, enabled provider, or the call fails (provider_not_allowed). It is never silently swapped for another. - Your organization’s default provider — set on the Web Search dashboard
page. If it was disabled after you chose it, calls fail
(
provider_unconfigured) rather than quietly switching — a different provider can carry a different price. - The platform default.
Keys are resolved the same way regardless of who configured them: if your
organization registered its own key for the provider (see
BYOK), it is used first; otherwise the
platform key. If neither exists, the call fails (provider_unconfigured).
Access and availability
Section titled “Access and availability”- Organization policy. Admins can allow or deny
web_searchper user or org-wide from the dashboard. A denied call returnsmcp_tool_disabled. See MCP access policy. - Platform kill switch. The tool stays listed when web search is
disabled platform-wide, and calls answer
web_search_disabled.
Managing it in the dashboard
Section titled “Managing it in the dashboard”Open Web Search under your organization (org owners only). The page shows:
- Usage — sponsored searches used against your free allowance this period, total searches (provisional, includes BYOK), and accrued paid overage cost.
- Paid overage — off by default. Turning it on requires confirming a dialog that restates your free allowance, each usable provider’s per-search price, and your chosen monthly limit. Disabling it is immediate.
- Monthly limit — a search count. It caps all searches, platform-key and BYOK alike.
- Default provider — chosen from the providers your organization can actually use.
- Per-member controls (Teams plans) — for each member, an on/off switch and an individual search cap. An empty cap inherits the organization limit.
What happens at the limit
Section titled “What happens at the limit”- An organization that has not opted in to paid overage is hard-stopped at
its free allowance: further platform-key searches fail with
quota_exceeded. - An opted-in organization keeps searching past the allowance up to its monthly limit; the platform-key searches beyond the allowance accrue to the usage ledger and are charged next cycle.
- The monthly limit is enforced per period, not per key: BYOK searches count toward it even though they are never charged.
- A member with their own cap is stopped by the smaller of their cap and the organization’s limit.
Bringing your own provider key
Section titled “Bringing your own provider key”You can register your own search-provider key (Brave, Exa, or Kagi) so your searches run on your account. BYOK searches are metered for abuse limits but never charged — they don’t touch your allowance or accrue overage, though they still count toward your monthly limit.
From the dashboard, open Provider keys under your organization and add a search key. Or from the CLI:
lunaroute search-keys listlunaroute search-keys set --provider kagi # prompts for the keylunaroute search-keys remove kagiset also accepts --api-key or the LUNAROUTE_SEARCH_API_KEY environment
variable, but prefer the prompt — a key on the command line lands in your
shell history.
Native harness search
Section titled “Native harness search”Claude Code and Codex can use their own built-in web search — no MCP server required — and LunaRoute serves it from the same web_search rails as the MCP tool.
- Claude Code. Point
ANTHROPIC_BASE_URLat LunaRoute (the Connect tab in your dashboard writes the environment block). Claude Code’s nativeweb_search_*tool is intercepted on the Messages ingress and executed on your organization’s web search allowance. WebFetch is not intercepted on current Claude Code: it fetches the page client-side and sends the extracted text in a prompt, so it never reaches LunaRoute as aweb_fetch_20250910call. (A raw API caller that does sendweb_fetch_20250910is still intercepted and metered — see Behavior changes.) - Codex. With a LunaRoute provider block, Codex’s hosted
web_search*tools (whatweb_search = "live"sends) are intercepted on the Responses ingress — this is the path that works, and whatlunaroute setup codexandlunaroute run codexset up. Codex’s standalone search is not supported through LunaRoute today: the provider block’ssupports_standalone_web_search = trueonly declares thePOST /v1/alpha/searchendpoint, and Codex needs its own under-developmentstandalone_web_searchfeature flag to use it. With that flag on, Codex drives searches through aweb.runnamespace tool LunaRoute does not handle, so no search happens — leave the flag off and use the hosted path above.
Native search is gated by exactly the same entitlement as the MCP tool — one gate governs search and fetch, so the allowance, paid-overage setting, monthly limit, per-member caps, and org/user policy all described under Access and availability apply unchanged. A native search bills on your web_search rails like any MCP search; a fetched page is priced like a search through its own price row and counts toward the same limits.
When a call is not entitled — the tool is disabled by policy, the platform kill switch is off, no provider is configured, or the allowance/limit is exhausted — it degrades in-band rather than failing the request: the model receives a tool result carrying the same error code the MCP tool returns (see Errors) and answers that it could not search. Nothing hard-fails.
Codex: cross-turn recall
Section titled “Codex: cross-turn recall”On current Codex (verified against rust-v0.162.0-alpha.12),
ResponseItem::WebSearchCall has no field for the opaque results blob and
Codex drops unknown fields when it echoes history back. LunaRoute therefore
cannot recall a previous turn’s search results: a follow-up turn that would
have reused them re-runs the search, billed normally. Search within a turn
is unaffected. The restore path stays in place so recall returns automatically
on a future Codex that preserves the field.
Behavior changes for raw API users
Section titled “Behavior changes for raw API users”If you call the Anthropic Messages or Responses API directly, native web search now behaves differently:
- Server-tool declarations are intercepted and executed on your rails. A
request that declares
web_search_*,web_fetch_20250910, or a hostedweb_search*tool type is now intercepted on the Messages and Responses ingresses and run through your web_search rails. Previously these were dropped on non-Anthropic upstreams (and the model invented results), or executed unmetered on Anthropic-dialect upstreams. The Chat Completions ingress is untouched. - No entitlement, no native search. An Anthropic-dialect caller without web_search entitlement no longer gets the previously-working native search — that path was invisible to our billing. It now receives the in-band “search unavailable” result, gated exactly like every other search. Enable web search on the Web Search dashboard page to restore it.
- New endpoint. The standalone
POST /v1/alpha/searchroute family exists for Codex’s client-side search; see the native harness notes for why Codex does not currently reach it through LunaRoute. - New error code.
fetch_not_allowed— a native fetch URL rejected by policy (see Errors).
Errors
Section titled “Errors”| Code | Meaning |
|---|---|
quota_exceeded |
The organization’s (or your personal) search limit for this period is exhausted, or the org has no allowance and hasn’t opted in. |
provider_not_allowed |
The named provider is unknown or disabled. |
provider_unconfigured |
The resolved default provider is unavailable, or no key exists for the provider (neither yours nor a platform key). |
mcp_tool_disabled |
The tool was denied for you or your organization by policy. |
web_search_disabled |
Web search is switched off platform-wide. |
fetch_not_allowed |
A native web_fetch request was rejected by policy: the URL’s scheme is not https, it fails the request’s domain allow/block filter, or it resolves to a private or metadata address. |