Web search
Let a model search the web inside one API request with the managed anonrouter:web_search tool.
Managed web search lets the model look things up while it answers. Add the
anonrouter:web_search tool to a chat completion. The model decides when to
search and what to search for, AnonRouter runs each search and hands the model
short excerpts from the results, and you get back one ordinary chat completion.
You do not implement a search loop.
Web search is an API feature. It works with an inference key, in the private ticket flow and in compatibility mode. It is not available in the website chat.
Check that web search is on
Web search can be switched off. While it is off, a request that includes the
tool, or a ticket request with web_search, fails with
503 managed_search_unavailable and nothing is charged.
Request
{
"model": "creator/model",
"messages": [{ "role": "user", "content": "What changed in the news this week?" }],
"tools": [
{
"type": "anonrouter:web_search",
"parameters": { "max_uses": 3, "max_results": 5, "max_total_results": 15 }
}
]
}parameters is optional. Every field has a default and a maximum:
| Field | Meaning | Default | Maximum |
|---|---|---|---|
max_uses | Searches in this request | 3 | 3 |
max_results | Results returned by each search | 5 | 8 |
max_total_results | Results across all searches in this request | 15 | 15 |
Send at most one web search tool. Unknown fields or values outside these ranges
fail with 400 invalid_server_tools.
Other limits on one request:
| Limit | Value |
|---|---|
| Model calls | 5 |
| Search query length | 400 characters, 50 words |
| Whole request | 15 minutes |
Pick a model whose route supports tool calling, and name it explicitly. /auto
and the named routers are not supported with web search yet.
Ticket flow
In the private ticket flow, ask for the search allowance when you issue the ticket, then send the tool in the completion:
{
"model": "creator/model",
"max_completion_tokens": 1024,
"web_search": { "max_uses": 3 }
}web_search.max_uses is an integer from 1 to 3. The completion's max_uses may
not be higher than the ticket's, or the request fails with
409 ticket_web_search_mismatch. In compatibility mode AnonRouter issues the
ticket for you from the tool's max_uses.
Your own tools
You can send your own function tools next to the web search tool. When the
model calls one of yours, the response ends with finish_reason: "tool_calls"
as usual. Run the tool and send its result in your next request. The function
name anonrouter_web_search is reserved.
AnonRouter never opens result pages. If you want the model to read a page, give it your own tool that fetches one.
Response
The response is a standard chat completion with two additions: usage.cost
covers every model call and every charged search, and a web_search object
summarizes the searches.
{
"object": "chat.completion",
"model": "creator/model",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "..." },
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 3120,
"completion_tokens": 410,
"total_tokens": 3530,
"cost": 0.0151
},
"web_search": {
"searches": 2,
"model_calls": 3,
"results": [
{ "title": "...", "url": "https://example.com/...", "description": "..." }
],
"cost": 0.012,
"model_cost": 0.0031
}
}| Field | Meaning |
|---|---|
web_search.searches | Searches run for this request |
web_search.model_calls | Model calls made for this request |
web_search.results | The result titles, URLs and descriptions the model was given, without duplicates |
web_search.cost | What the searches cost, in USD |
web_search.model_cost | What the model calls cost, in USD |
usage token counts are totals across all model calls. Values above are
illustrative.
Streaming
With stream: true you receive the answer text as normal chunks. While a search
runs, AnonRouter also sends progress frames with the SSE event name
anonrouter.web_search:
event: anonrouter.web_search
data: {"id":"chatcmpl-...","status":"started","index":1}status is started, succeeded or failed, and index counts searches from
- Progress frames never contain the query. They have no
choices, so skip them if your client only reads chunks.
The last chunk carries finish_reason and the web_search object. With
stream_options.include_usage, a usage chunk follows. data: [DONE] is sent
only after every model call and search has been billed. If something fails
after streaming has started, you get one error frame and the stream closes
without [DONE].
Pricing
Each successful search costs $0.006, on every account, including accounts with a subscription. A search is charged only when the search provider returned a successful response. A search that failed, timed out without an answer, or was never sent is not charged.
The model calls in the request are billed like any other chat request, at the model's listed price. A request can make up to five model calls, so its model cost can be higher than a single completion.
A per-key credit limit and a daily usage budget count searches too. If a search cannot be paid for, the model is told the search is unavailable and continues without it; model calls already made are still billed.
For rate limits, each model call counts as one request.
Privacy
A search sends the model's query to a third-party web search provider that AnonRouter classifies as Private. Searches run on the confidential content plane: the query goes only to the search provider, and the result excerpts go only to the model provider, as part of the conversation. AnonRouter's account side records that a search was authorized, whether it succeeded, and its price, never the query or the results.
Because the query leaves for a Private provider, web search is refused for TEE and E2EE routes and for encrypted requests. Search results are untrusted text from the web. The model is told to treat them as information, not instructions.
Usage records
GET /v1/usage with a management key includes a web_search field: up to 100
recent searches and the totals for the last 30 days.
{
"web_search": {
"totals_30d": { "charged_searches": 42, "cost_usd": 0.252 },
"data": [
{
"id": "...",
"request_id": "req_...",
"status": "charged",
"outcome": "succeeded",
"unit_price_usd": 0.006,
"cost_usd": 0.006,
"pricing_version": "web-search-2026-10-26-v1",
"created_at": "2026-10-28T12:00:00.000Z"
}
]
}
}status is charged, not_charged or pending. request_id is the API
request the search belonged to. Records never contain the query or the results.
Not supported
Web search is refused before anything is charged for:
/auto, the named routers, free models and the Free Models Router- TEE and E2EE routes, and encrypted requests
- trial credit
response_format(structured output)- a
modelsfallback list - embeddings, images and speech
- website sessions and connected apps
Errors
| Status | type | Meaning |
|---|---|---|
400 | invalid_server_tools | The web search tool is malformed, repeated, or outside its limits |
400 | reserved_tool_name | One of your functions uses the reserved name anonrouter_web_search |
400 | managed_search_requires_explicit_model | /auto or a named router was requested |
400 | managed_search_free_unsupported | A free model or the Free Models Router was requested |
400 | managed_search_privacy_unsupported | The route is TEE or E2EE |
400 | managed_search_e2ee_unsupported | The request is encrypted |
400 | managed_search_trial_unsupported | The request would be paid with trial credit |
400 | managed_search_structured_output_unsupported | The request sets response_format |
400 | managed_search_models_unsupported | The request sends a models fallback list |
400 | managed_search_chat_only | The ticket is not for chat completions |
403 | managed_search_api_key_required | The request does not use an inference key |
409 | ticket_web_search_mismatch | The ticket was issued without web_search, or with a lower max_uses |
502 | mixed_server_client_tools_unsupported | The model asked for a search and one of your tools in the same turn; retry the request |
503 | managed_search_unavailable | Web search is switched off |
504 | managed_search_timeout | The request ran longer than 15 minutes |
Or hand the whole thing to your agent
One prompt carries the entire setup. Give it to your agent, review what it configures, and you are done.