Skip to content
LUNAROUTEDocs

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).

{
"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.

Provider key Provider
brave Brave Search
exa Exa
kagi Kagi

Provider resolution, in order:

  1. Explicit provider — must be a known, enabled provider, or the call fails (provider_not_allowed). It is never silently swapped for another.
  2. 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.
  3. 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).

  • Organization policy. Admins can allow or deny web_search per user or org-wide from the dashboard. A denied call returns mcp_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.

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.
  • 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.

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:

Terminal window
lunaroute search-keys list
lunaroute search-keys set --provider kagi # prompts for the key
lunaroute search-keys remove kagi

set 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.

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_URL at LunaRoute (the Connect tab in your dashboard writes the environment block). Claude Code’s native web_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 a web_fetch_20250910 call. (A raw API caller that does send web_fetch_20250910 is still intercepted and metered — see Behavior changes.)
  • Codex. With a LunaRoute provider block, Codex’s hosted web_search* tools (what web_search = "live" sends) are intercepted on the Responses ingress — this is the path that works, and what lunaroute setup codex and lunaroute run codex set up. Codex’s standalone search is not supported through LunaRoute today: the provider block’s supports_standalone_web_search = true only declares the POST /v1/alpha/search endpoint, and Codex needs its own under-development standalone_web_search feature flag to use it. With that flag on, Codex drives searches through a web.run namespace 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.

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.

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 hosted web_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/search route 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).
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.