Claude's server-side tools work through Router One. On the Anthropic-compatible POST /v1/messages endpoint, the web_search server tool and the code_execution tool (with its bash_code_execution and text_editor_code_execution sub-tools and container reuse across turns) are accepted exactly as Anthropic's API defines them.
Declare the typed tool in tools, send the request to https://api.router.one/v1/messages with a Router One key and a Claude catalog id, and the search or the sandboxed run happens on the model side while Router One forwards the request, meters it at the model's standard token rate, and writes the request trace. Two things do not pass: the web_fetch server tool and MCP (an mcp_toolset tool or the mcp_servers field) are rejected with HTTP 400 invalid_request_error before any model is called.
Client-side tools vs. server-side tools
With ordinary tool calling, the tool runs in your code: you declare a function with an input_schema, the model answers with a tool_use block, you execute it and send a tool_result back. That loop works on every tool-capable model in the catalog and on the OpenAI-compatible endpoint.
Server-side tools invert the loop. You declare a tool by a dated type (web_search_20250305, code_execution_20250825) with no schema, and the model vendor's API runs the search or the sandboxed code inside the request — possibly several rounds — before returning the final text with citations or execution output. Nothing comes back to your code mid-request.
Router One's part is deliberately narrow: it authenticates the key, forwards the request to the Claude model, records the trace, and settles the tokens the model reports. Router One does not execute tools itself — the trust boundary is spelled out on the API compatibility fact sheet.
Sending web_search through Router One
The request is the Anthropic Messages request you already know, with the host swapped:
curl https://api.router.one/v1/messages \
-H "x-api-key: sk-your-api-key" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "anthropic/claude-sonnet-4.6",
"max_tokens": 1024,
"messages": [
{"role": "user", "content": "What changed in the most recent Node.js LTS release? Cite sources."}
],
"tools": [{"type": "web_search_20250305", "name": "web_search", "max_uses": 3}]
}'
Three details worth knowing. The key goes in x-api-key or in Authorization: Bearer — both are accepted, and Bearer is what Claude Code sends through ANTHROPIC_AUTH_TOKEN. The model field takes the catalog id (anthropic/claude-sonnet-4.6); most official bare names such as claude-sonnet-4.6 are accepted as aliases, but the catalog id is the safe form. And max_uses, allowed_domains / blocked_domains (one or the other, not both) and user_location are forwarded as sent, as are the newer tool versions web_search_20260209 (dynamic filtering) and web_search_20260318 (response_inclusion).
With the official Python SDK, set base_url to the host — the SDK appends /v1/messages itself:
import anthropic
client = anthropic.Anthropic(
api_key="sk-your-api-key",
base_url="https://api.router.one", # host only; the SDK adds /v1/messages
)
response = client.messages.create(
model="anthropic/claude-sonnet-4.6",
max_tokens=1024,
messages=[{"role": "user", "content": "What changed in the most recent Node.js LTS release? Cite sources."}],
tools=[{"type": "web_search_20250305", "name": "web_search", "max_uses": 3}],
)
for block in response.content:
if block.type == "text":
print(block.text)
print(response.usage.server_tool_use) # web_search_requests=N
The response content array comes back in the order the turn happened: a text block where Claude decides to search, a server_tool_use block (name: "web_search", the query in input), a web_search_tool_result block whose content is a list of web_search_result entries (url, title, page_age and encrypted_content), and finally text blocks carrying citations of type web_search_result_location. usage.server_tool_use.web_search_requests counts the searches. To continue the conversation, send the assistant's blocks back exactly as received, encrypted_content included — Router One accepts the replayed server_tool_use and web_search_tool_result blocks on the next turn and meters them as input like any other content. With stream: true the same blocks arrive as content_block_start events with a pause while the search runs; the streaming guide covers the event stream.
Sending code_execution
Same shape, different tool type:
curl https://api.router.one/v1/messages \
-H "x-api-key: sk-your-api-key" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "anthropic/claude-sonnet-4.6",
"max_tokens": 4096,
"messages": [
{"role": "user", "content": "Draw 1000 samples from a normal distribution, then report the mean, median and standard deviation."}
],
"tools": [{"type": "code_execution_20250825", "name": "code_execution"}]
}'
Claude answers with server_tool_use blocks named bash_code_execution (shell commands) or text_editor_code_execution (view, create and edit files), each followed by a bash_code_execution_tool_result block with stdout, stderr and return_code, or a text_editor_code_execution_tool_result block carrying the view, create or edit result, and then the final text. The response also carries a top-level container object with an id and expires_at; pass that id back as the top-level container request parameter on the next turn to keep the files Claude created — Router One accepts the container id across turns. code_execution_20260120 and code_execution_20260521 are forwarded the same way if you need REPL state persistence or programmatic tool calling.
No anthropic-beta header is required: none of the three current code execution versions needs one, and the legacy beta headers remain optional opt-ins. If your SDK or an older integration still sends anthropic-beta, Router One forwards the header unchanged.
One boundary to plan around: Router One serves no Files API. Keep code execution to work whose result comes back in the response body — stdout, computed values, text — rather than workflows that upload input files by file_id or download generated files afterwards.
What Router One rejects, and why that is useful
Three things are refused before any model is called, each with HTTP 400 and the Anthropic error envelope {"type":"error","error":{"type":"invalid_request_error","message":...},"request_id":...}:
- the
web_fetchserver tool, in any dated version; - an
mcp_toolsettool entry; - a non-empty
mcp_serversfield (the MCP connector).
The message names the rejected feature, so the fix is obvious, and because the request never reached a model, it cost nothing. Your SDK raises the same BadRequestError it would raise for any Anthropic 400. The error codes page lists the other 400s on this endpoint.
The substitutes are straightforward. For page content, either let web_search bring it in (search results are loaded into the model's context), or fetch the page in your own code and pass the text as a normal content block. For MCP, run the MCP client on your side and expose the tools it discovers as client-side function tools — the loop on the tool calling guide is exactly that.
Billing and the request trace
Server tool activity is metered at the model's standard token rate. The search results Claude reads and the execution output it consumes are input and output tokens in the usage the model reports, settled at the posted rate on the model's page — Claude Sonnet 4.6, for example — with no separate per-search or per-container line item, and container time is not passed through per call. Current rates and the "up to 90% off official prices" qualifier for select models are on the pricing page.
Every request lands in Dashboard → Logs with model, input and output tokens, cost, latency and status, so a search-heavy turn shows up as a bigger token bill in the same trace as any other call; for your own accounting, usage.server_tool_use in the response body counts the searches and executions. If an agent can trigger open-ended searching, put the key behind a hard spend cap (maxSpend) and a rate limit — the same per-key controls described in Claude Code token costs explained.
Which models and which endpoint
Server tools are a Claude-family feature on /v1/messages. Per-tool model support is Anthropic's call and changes over time; the Claude ids in the catalog — anthropic/claude-opus-5, anthropic/claude-sonnet-4.6, anthropic/claude-haiku-4.5 and their siblings — each list POST /v1/messages on their model page. DeepSeek V4 also answers on /v1/messages, but with client-side function tools only: a server tool has no executor there. And the OpenAI-compatible /v1/chat/completions request shape has no slot for Anthropic's typed server tools, so on that endpoint tool calling is client-side for every model.
Claude Code depends on this path: its built-in WebSearch relies on the endpoint accepting the web_search server tool — which is why it breaks on relays that reject server tools — so pointing Claude Code at Router One with ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN (the Claude Code setup shows both) keeps WebSearch working. Claude Code's WebFetch fetches pages itself rather than through the web_fetch server tool, so the rejection above does not touch it. The dated acceptance entries are in the changelog.
FAQ
Do I need an anthropic-beta header to use code_execution through Router One?
No. None of the current code_execution_20250825, code_execution_20260120 and code_execution_20260521 tool versions requires an anthropic-beta header. If your client sends one anyway, Router One forwards it unchanged.
Does web_search cost extra per search through Router One?
Server tool activity is metered at the model's standard token rate: the search content the model reads is billed as input tokens in the usage the model reports, and there is no separate per-search line item. The per-request trace shows the total, and usage.server_tool_use.web_search_requests in the response counts the searches.
Can I use web_fetch or MCP servers through Router One?
No. The web_fetch server tool, mcp_toolset tools and the mcp_servers field are rejected with HTTP 400 invalid_request_error before any model is called, so the request costs nothing. Fetch pages in your own code, or run the MCP client on your side and expose its tools as client-side function tools.
Does Claude Code's WebSearch work through Router One?
Yes. Claude Code's WebSearch depends on the endpoint accepting the web_search server tool this post describes, and that tool is accepted on /v1/messages. Configure ANTHROPIC_BASE_URL=https://api.router.one and ANTHROPIC_AUTH_TOKEN with your Router One key as on the Claude Code setup page.
Can I send server tools to DeepSeek V4 or on /v1/chat/completions?
No. Server tools run on the Claude API side and only do something for Claude-family models on /v1/messages; DeepSeek V4 there, and every model on the OpenAI-compatible /v1/chat/completions endpoint, use client-side function tools.
Next steps
- Read the tool calling guide for the client-side loop that works on every model.
- Point Claude Code at the gateway with the Claude Code setup — WebSearch included.
- The Messages endpoint reference and the API docs document the request and error shapes.
- Pick a Claude model and check its endpoints and live rate on the model catalog; the discount qualifier is on the pricing page.