have a look at this commit: https://github.com/mitsuhiko/pi-mono/commit/2b7e67873a88ef1ef76c40c988b1b218f4c55c14
Anthropic server-side web-search tool docs
Web search tool
The web search tool gives Claude direct access to real-time web content, allowing it to answer questions with up-to-date information beyond its knowledge cutoff. Claude automatically cites sources from search results as part of its answer.
Please reach out through our [feedback form](https://forms.gle/sWjBtsrNEY2oKGuE8) to share your experience with the web search tool.
Supported models
Web search is available on:
- Claude Sonnet 4.5 (
claude-sonnet-4-5-20250929)
- Claude Sonnet 4 (
claude-sonnet-4-20250514)
- Claude Sonnet 3.7 (deprecated) (
claude-3-7-sonnet-20250219)
- Claude Haiku 4.5 (
claude-haiku-4-5-20251001)
- Claude Haiku 3.5 (deprecated) (
claude-3-5-haiku-latest)
- Claude Opus 4.5 (
claude-opus-4-5-20251101)
- Claude Opus 4.1 (
claude-opus-4-1-20250805)
- Claude Opus 4 (
claude-opus-4-20250514)
How web search works
When you add the web search tool to your API request:
- Claude decides when to search based on the prompt.
- The API executes the searches and provides Claude with the results. This process may repeat multiple times throughout a single request.
- At the end of its turn, Claude provides a final response with cited sources.
How to use web search
Your organization's administrator must enable web search in [Console](/settings/privacy).
Provide the web search tool in your API request:
```bash Shell
curl https://api.anthropic.com/v1/messages \
--header "x-api-key: $ANTHROPIC_API_KEY" \
--header "anthropic-version: 2023-06-01" \
--header "content-type: application/json" \
--data '{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "What is the weather in NYC?"
}
],
"tools": [{
"type": "web_search_20250305",
"name": "web_search",
"max_uses": 5
}]
}'
```
Tool definition
The web search tool supports the following parameters:
Max uses
The max_uses parameter limits the number of searches performed. If Claude attempts more searches than allowed, the web_search_tool_result will be an error with the max_uses_exceeded error code.
Domain filtering
When using domain filters:
- Domains should not include the HTTP/HTTPS scheme (use
example.com instead of https://example.com)
- Subdomains are automatically included (
example.com covers docs.example.com)
- Specific subdomains restrict results to only that subdomain (
docs.example.com returns only results from that subdomain, not from example.com or api.example.com)
- Subpaths are supported and match anything after the path (
example.com/blog matches example.com/blog/post-1)
- You can use either
allowed_domains or blocked_domains, but not both in the same request.
Wildcard support:
- Only one wildcard (
*) is allowed per domain entry, and it must appear after the domain part (in the path)
- Valid:
example.com/*, example.com/*/articles
- Invalid:
*.example.com, ex*.com, example.com/*/news/*
Invalid domain formats will return an invalid_tool_input tool error.
Request-level domain restrictions must be compatible with organization-level domain restrictions configured in the Console. Request-level domains can only further restrict domains, not override or expand beyond the organization-level list. If your request includes domains that conflict with organization settings, the API will return a validation error.
Localization
The user_location parameter allows you to localize search results based on a user's location.
type: The type of location (must be approximate)
city: The city name
region: The region or state
country: The country
timezone: The IANA timezone ID.
Response
Here's an example response structure:
Search results
Search results include:
url: The URL of the source page
title: The title of the source page
page_age: When the site was last updated
encrypted_content: Encrypted content that must be passed back in multi-turn conversations for citations
Citations
Citations are always enabled for web search, and each web_search_result_location includes:
url: The URL of the cited source
title: The title of the cited source
encrypted_index: A reference that must be passed back for multi-turn conversations.
cited_text: Up to 150 characters of the cited content
The web search citation fields cited_text, title, and url do not count towards input or output token usage.
When displaying API outputs directly to end users, citations must be included to the original source. If you are making modifications to API outputs, including by reprocessing and/or combining them with your own material before displaying them to end users, display citations as appropriate based on consultation with your legal team.
Errors
When the web search tool encounters an error (such as hitting rate limits), the Claude API still returns a 200 (success) response. The error is represented within the response body using the following structure:
These are the possible error codes:
too_many_requests: Rate limit exceeded
invalid_input: Invalid search query parameter
max_uses_exceeded: Maximum web search tool uses exceeded
query_too_long: Query exceeds maximum length
unavailable: An internal error occurred
pause_turn stop reason
The response may include a pause_turn stop reason, which indicates that the API paused a long-running turn. You may provide the response back as-is in a subsequent request to let Claude continue its turn, or modify the content if you wish to interrupt the conversation.
Prompt caching
Web search works with prompt caching. To enable prompt caching, add at least one cache_control breakpoint in your request. The system will automatically cache up until the last web_search_tool_result block when executing the tool.
For multi-turn conversations, set a cache_control breakpoint on or after the last web_search_tool_result block to reuse cached content.
For example, to use prompt caching with web search for a multi-turn conversation:
```python
import anthropic
client = anthropic.Anthropic()
First request with web search and cache breakpoint
messages = [
{
"role": "user",
"content": "What's the current weather in San Francisco today?"
}
]
response1 = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=[REDACTED]
messages=messages,
tools=[{
"type": "web_search_20250305",
"name": "web_search",
"user_location": {
"type": "approximate",
"city": "San Francisco",
"region": "California",
"country": "US",
"timezone": "America/Los_Angeles"
}
}]
)
Add Claude's response to the conversation
messages.append({
"role": "assistant",
"content": response1.content
})
Second request with cache breakpoint after the search results
messages.append({
"role": "user",
"content": "Should I expect rain later this week?",
"cache_control": {"type": "ephemeral"} # Cache up to this point
})
response2 = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=[REDACTED]
messages=messages,
tools=[{
"type": "web_search_20250305",
"name": "web_search",
"user_location": {
"type": "approximate",
"city": "San Francisco",
"region": "California",
"country": "US",
"timezone": "America/Los_Angeles"
}
}]
)
The second response will benefit from cached search results
while still being able to perform new searches if needed
print(f"Cache read tokens: {response2.usage.get('cache_read_input_tokens', 0)}")
Batch requests
You can include the web search tool in the Messages Batches API. Web search tool calls through the Messages Batches API are priced the same as those in regular Messages API requests.
Usage and pricing
Web search usage is charged in addition to token usage:
Web search is available on the Claude API for ** +424 lines0 per 1,000 searches**, plus standard token costs for search-generated content. Web search results retrieved throughout a conversation are counted as input tokens, in search iterations executed during a single turn and in subsequent conversation turns.
Each web search counts as one use, regardless of the number of results returned. If an error occurs during web search, the web search will not be billed.
clone the openai/codex repo and see how they do websearch
i propose we formalize this properly. passing a tool called web_search sucks. this should be encoded via StreamOptions (and hence SimplifiedStreamOptions) in some way. providers are free to ignore it. we might want to add additional server-side tools in the future.
that's for "how to enable this in both pi-ai and pi-coding-agent". the next issue is how we combine this with tools specified by the provider. it appears for anthropic, we add a web_search tool if enabled. this can clash with user provided tools, so user provided tools need to be renamed in that case before being passed to anthropic. we already do some kind of renaming in the anthropic provider (for the anthropic endpoint). when sending to the llm endpoint the server side web search must be called web_search, while any user provided tool with name web_search must be called user_web_search. when persisting to the pi-ai context, we need to rename the server side tool calls (and results if they contain the tool name) from web_search to something that uniquely identifies it as a server side web_search. when the context is later serialized for the provider again, we need to do the name swapping again for old tool calls/results.
finally, we also need to deal with the tool result. from what i can gather in the anthropic docs, they return a content signature along with other shit. i suppose since tool results are free form anyways for the most part, that's fine. however, coding-agent @packages/coding-agent/src/modes/interactive/components/tool-execution.ts may have to accomodate that somehow?