Summary: OpenRouter’s old web search plugin ran a fixed script: a single search per request, whether or not the task needed it, and the model got no vote on timing or query. With the new server tools, that judgment belongs to the model — a request may trigger anywhere from zero to N searches, and the model picks both the query and the moment. Search frequency shifts from a hardcoded constant to a model decision — that line is what makes this agentic.
A model’s route to the web has long been limited to two awkward options: accept a single provider’s built-in search and stay locked to it, or run search and fetch calls from your own client code, untangling every provider’s different schema. OpenRouter’s agentic web tools carve out a third option — a pair of server tools, openrouter:web_search and openrouter:web_fetch, callable by any model within a single request: it looks things up, opens the pages it finds, and assembles the answer itself.
Two things stand out. Where it runs: OpenRouter executes the tools on its own servers and hands results straight to the model, leaving your client with nothing to build. How it behaves: the tool definition, the calling convention, and the shape of the results are the same on any tool-calling model — change models and your tools stay put, the natural edge of a routing platform.
The Search Decision Moves to the Model
OpenRouter’s old web search plugin ran a fixed script: a single search per request, whether or not the task needed it, and the model got no vote on timing or query. With the new server tools, that judgment belongs to the model — a request may trigger anywhere from zero to N searches, and the model picks both the query and the moment. Give it three cloud GPU vendors to compare and it runs three differently worded queries on its own, then merges the findings. Search frequency shifts from a hardcoded constant to a model decision — that line is what makes this agentic.
Runs on the Server; the Client Ships Nothing
Turning a tool on takes a single declaration: {"type": "openrouter:web_search"}. Before, every provider change forced you to rewrite tool definitions and result parsers, and behavior still might not match — strictly enforced blocked domains, to name one case, are absent from certain native tools altogether. This abstraction lifts tool capability out of provider differences: when behavior must match too, pin Exa or Parallel into the engine slot, and whichever model the request lands on, the results handed back are identical.
Web Search: Four Engines, Four Ways to Pay
| Engine | What it does | Cost |
|---|---|---|
| Auto (default) | Native when the provider offers it, Exa otherwise | Follows whichever engine runs |
| Native | Search supplied by the provider itself | Priced by the provider |
| Exa | Search delegated to Exa, charged against your OpenRouter credits | $0.005 per request (up to 10 results), plus $0.001 for each extra result |
| Parallel | Search delegated to Parallel, likewise charged against credits | $0.005 per request (up to 10 results), plus $0.001 for each extra result |
How the four split up: Auto reaches for native search whenever the provider has it and settles for Exa when it does not; for cross-model consistency and billing you can predict, name Exa or Parallel explicitly — identical pricing, different hands doing the work. search_context_size, which sets how large the result context gets, is honored only by Exa and Parallel; native engines drop it. Most engines also take allowed_domains and excluded_domains for domain filtering. The docs example works as-is: max_results of 5, allowed_domains cut down to arxiv.org and nature.com, search_context_size pushed to high — a research setup that reads academic sources only, with plenty of context.
Web Fetch: Five Engines, One of Them Free
Fetch usually runs right after a search: the model lines up its candidate pages via web_search and then brings their full contents back with web_fetch, the two tools forming one research chain inside the request.
| Engine | What it does | Cost |
|---|---|---|
| Auto (default) | Native when the provider offers it, Exa otherwise | Follows whichever engine runs |
| Native | Fetch supplied by the provider itself | Priced by the provider |
| OpenRouter | A direct HTTP fetch performed by OpenRouter | Free |
| Exa | Extracts content and returns clean markdown | $0.001 per fetch |
| Parallel | Higher-quality extraction through Parallel’s extract API | $0.001 per fetch |
The fetch side adds an engine run by OpenRouter itself: a plain HTTP fetch at no charge, the entry point for cost-sensitive runs. Exa and Parallel charge $0.001 per fetch and bring content extraction plus clean markdown. Pick Exa, Parallel, or OpenRouter by name and allowed_domains with blocked_domains will consistently limit which URLs can be fetched across models — native fetch behavior differs from model to model, so when the parameters must be honored, these three are the choice.
Dials for Cost and Context
Inside an agentic loop, the danger is not the price of one call; it is call count compounded by context. Two parameters deserve permanent spots in your setup. max_total_results draws a ceiling over the combined results of every search in one request — once the ceiling is hit, the model is told it has reached the limit instead of being allowed another search. max_content_tokens (the docs example uses 50000) bounds how many content tokens the model receives, so a single oversized page cannot swallow half a context window. Concrete figures: three GPU vendors, one search per vendor at max_results 5, stack up 15 results; put max_total_results at 15 and marginal search cost for the task is capped.
Leaving the Plugin Behind: One Line Changed, One Line Drawn
"plugins": [{ "id": "web" }] → "tools": [{ "type": "openrouter:web_search" }]
Moving over amounts to swapping plugins for tools inside the request body; OpenRouter’s migration guide documents everything. The line you cannot cross is model support: server tools demand a model with tool calling. Models lacking it remain on the old plugin — and inherit its one search per request, every request.
Trade-offs and Limits
Execution being server-side means it is out of your hands: custom JavaScript cannot run, and redirects cannot be handled from your client. If max_content_tokens cuts a page short, the truncated text is all the model ever sees, and anything important sitting near the bottom goes missing. On cost: a model inside an agentic loop can end up searching again and again; Exa and Parallel cost little per call, but behavior multiplies the total and needs watching, and native engine prices are whatever the provider sets, so cross-provider comparisons stay separate. Billing location is another wrinkle: Exa and Parallel charges flow through OpenRouter credits and stay in a single ledger, while native engine charges show up on the provider’s invoice — visibility into costs splits in two.
Builder Guidance
Three moves to make right away. One: prove the pipeline works using the OpenRouter fetch engine (Free), and step up to Exa’s clean markdown once quality demands it. Two: make max_content_tokens and max_total_results standing defaults, and cost and context usage become predictable. Three: confine both search and fetch to domains you trust via allowed_domains, and run blocked_domains the opposite way to keep internal hosts out — internal.example.com is exactly what the docs example blocks. For an agent facing customers, this doubles as a security boundary. In the chatroom, the tool icon turns both tools on straight away; through the API, take the parameter settings from the two tables above.
