Docs
Open App

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:

FieldMeaningDefaultMaximum
max_usesSearches in this request33
max_resultsResults returned by each search58
max_total_resultsResults across all searches in this request1515

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:

LimitValue
Model calls5
Search query length400 characters, 50 words
Whole request15 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
  }
}
FieldMeaning
web_search.searchesSearches run for this request
web_search.model_callsModel calls made for this request
web_search.resultsThe result titles, URLs and descriptions the model was given, without duplicates
web_search.costWhat the searches cost, in USD
web_search.model_costWhat 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

  1. 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 models fallback list
  • embeddings, images and speech
  • website sessions and connected apps

Errors

StatustypeMeaning
400invalid_server_toolsThe web search tool is malformed, repeated, or outside its limits
400reserved_tool_nameOne of your functions uses the reserved name anonrouter_web_search
400managed_search_requires_explicit_model/auto or a named router was requested
400managed_search_free_unsupportedA free model or the Free Models Router was requested
400managed_search_privacy_unsupportedThe route is TEE or E2EE
400managed_search_e2ee_unsupportedThe request is encrypted
400managed_search_trial_unsupportedThe request would be paid with trial credit
400managed_search_structured_output_unsupportedThe request sets response_format
400managed_search_models_unsupportedThe request sends a models fallback list
400managed_search_chat_onlyThe ticket is not for chat completions
403managed_search_api_key_requiredThe request does not use an inference key
409ticket_web_search_mismatchThe ticket was issued without web_search, or with a lower max_uses
502mixed_server_client_tools_unsupportedThe model asked for a search and one of your tools in the same turn; retry the request
503managed_search_unavailableWeb search is switched off
504managed_search_timeoutThe 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.

On this page