Web Fetch
Anthropic's web_fetch tool lets Claude read the full text of a web page or PDF that the conversation points to. Amazon Bedrock doesn't support the tool, so a request that sends it to Bedrock directly is rejected. On LLM API, a /v1/messages request served by aws-bedrock can include the tool exactly as you would send it to Anthropic:
// POST /v1/messages
{
"model": "aws-bedrock/claude-sonnet-4-6",
"max_tokens": 1024,
"messages": [{ "role": "user", "content": "Summarize https://go.dev/doc/go1.26" }],
"tools": [{ "type": "web_fetch_20250910", "name": "web_fetch" }],
}
You don't need a scraping account or any setup. When the model asks for a page, LLM API fetches it, gives the content to the model, and returns the model's answer.
INFO
Bedrock only. LLM API runs web_fetch itself for models served by aws-bedrock, which have
no native fetch. Requests served by other providers pass the tool to the provider unchanged:
Anthropic, for example, runs its own web fetch.
Activation
| What |
Value |
| Endpoint |
/v1/messages (Anthropic format) |
| Model |
A model served by aws-bedrock, for example aws-bedrock/claude-sonnet-4-6 |
| Tool |
One of web_fetch_20250910, web_fetch_20260209, web_fetch_20260309, web_fetch_20260318, or bare web_fetch, in tools |
Every version fetches the same way here: the versions with dynamic filtering behave like the basic one.
Prerequisites
- An LLM API key, passed as a
Bearer token in the Authorization header, or in x-api-key as Anthropic SDKs send it.
- Requests go to
https://api.llmapi.ai/v1/messages. To use an Anthropic SDK, set its base URL to https://api.llmapi.ai.
How It Works
- The model asks for a page by its URL.
- LLM API checks the URL (see Which URLs Can Be Fetched), fetches the page, and gives the model its main content as markdown, with the page title and the URL it landed on after redirects. A PDF is converted to text, up to its first 50 pages.
- The model reads the page, fetches more if it needs to, and answers.
You get back a standard Anthropic Messages response with the final answer only: no server_tool_use or web_fetch_tool_result blocks and no citations. All fetching happens on LLM API's side, so you never return a tool_result for it.
Basic Usage
Put the URL in the user message and add the tool:
curl -X POST "https://api.llmapi.ai/v1/messages" \
-H "Authorization: Bearer $LLM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "aws-bedrock/claude-sonnet-4-6",
"max_tokens": 1024,
"messages": [
{ "role": "user", "content": "Summarize https://go.dev/doc/go1.26" }
],
"tools": [ { "type": "web_fetch_20250910", "name": "web_fetch", "max_uses": 3 } ]
}'
Example Response
{
"id": "msg_01AbCdEfGhIjKlMnOpQrStUv",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "Go 1.26 adds ... (a summary of the fetched page)."
}
],
"stop_reason": "end_turn",
"usage": {
"input_tokens": 9120,
"output_tokens": 410,
"cost_usd_input": 0.0274,
"cost_usd_output": 0.0062,
"cost_usd_web_fetch": 0.005
}
}
usage sums every model call the request made. Each round of fetching is a model call of its own, and the fetched text counts as input tokens. cost_usd_web_fetch appears only when the model fetched something.
Which URLs Can Be Fetched
So that a model can't be steered into fetching arbitrary addresses, LLM API fetches a URL only when all of these hold:
- It already appeared in the conversation: in a user message, a result of your own tool, an earlier assistant turn, or a page or search result fetched earlier in the same request, including the links on a fetched page. The system prompt doesn't count; to make a URL from it fetchable, include it in a user message.
- It is
http or https and at most 250 characters long.
- Its host is public. Loopback, private, link-local and similar addresses are refused, and so is any name outside a public top-level domain (
localhost, redis, *.local, *.internal and the like). The URL a redirect lands on must pass the same checks.
A refused URL doesn't fail the request: the model is told why and carries on.
Tool Settings
| Setting |
Behavior |
max_uses |
Lowers the per-request fetch limit. Every fetch attempt counts, failed ones included; a URL refused by the checks above doesn't. |
allowed_domains, blocked_domains |
Limit the sites the model can fetch. Unlike on Anthropic, you can set both: a blocked match wins. A rule may include a path, such as docs.example.com/api. A redirect must pass the rules too. |
max_content_tokens |
Lowers the per-page cap of 50,000 tokens; it can't raise it. |
use_cache |
false fetches a fresh page instead of a cached copy, which can be up to two days old. |
citations, url_sources, response_inclusion |
Accepted and ignored. |
{
"type": "web_fetch_20260309",
"name": "web_fetch",
"max_uses": 3,
"allowed_domains": ["go.dev", "pkg.go.dev"],
"max_content_tokens": 20000,
"use_cache": false,
}
Streaming
Web fetch works with stream: true, with one difference: the stream starts only once the fetching is done. LLM API runs the whole fetch loop first, then sends the answer as the usual Anthropic event stream. The costs, cost_usd_web_fetch included, arrive in the final cost_usd event.
curl -N -X POST "https://api.llmapi.ai/v1/messages" \
-H "Authorization: Bearer $LLM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "aws-bedrock/claude-sonnet-4-6",
"max_tokens": 1024,
"stream": true,
"messages": [
{ "role": "user", "content": "What changed in https://go.dev/doc/go1.26?" }
],
"tools": [ { "type": "web_fetch_20250910", "name": "web_fetch" } ]
}'
Using with SDKs
Use the Anthropic SDK pointed at LLM API, and pass the tool as you would to Anthropic:
Python
import anthropic
client = anthropic.Anthropic(base_url="https://api.llmapi.ai", api_key="your-api-key")
message = client.messages.create(
model="aws-bedrock/claude-sonnet-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": "Summarize https://go.dev/doc/go1.26"}],
tools=[{"type": "web_fetch_20250910", "name": "web_fetch", "max_uses": 3}],
)
for block in message.content:
if block.type == "text":
print(block.text)
TypeScript
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({
baseURL: "https://api.llmapi.ai",
apiKey: "your-api-key",
});
const message = await client.messages.create({
model: "aws-bedrock/claude-sonnet-4-6",
max_tokens: 1024,
messages: [{ role: "user", content: "Summarize https://go.dev/doc/go1.26" }],
tools: [{ type: "web_fetch_20250910", name: "web_fetch", max_uses: 3 }],
});
for (const block of message.content) {
if (block.type === "text") console.log(block.text);
}
Your Own Fetch Tool
To fetch pages yourself, declare your own tool whose name contains web_fetch or web-fetch. LLM API then leaves fetching to you: it drops the native web_fetch from the request, and the model calls your tool as a regular tool_use that you answer with a tool_result.
{
"tools": [
{
"name": "my_web_fetch",
"description": "Fetch a web page and return its text",
"input_schema": {
"type": "object",
"properties": { "url": { "type": "string" } },
"required": ["url"],
},
},
],
}
- LLM API tells the two apart by the tool's own
type: a tool with a native type (web_fetch_20250910 and the others above) is LLM API's fetch, whatever its name. A tool without one is yours.
- The name match is case-sensitive: a tool named
WebFetch is not treated as a fetch tool.
- Fetch and search are decided separately: your own fetch tool leaves the native
web_search tool to LLM API.
Combining with Your Own Tools
web_fetch works alongside your other tools. If the model calls one of them in the same turn as web_fetch, the response comes back to you with only your tool call. Once you send its result, the model asks for the page again and LLM API fetches it.
Cost Tracking
- Tokens. Every model call of the request is billed, one per round of fetching, and
usage sums them.
- Pages. Each page LLM API fetches costs $0.005: one per fetch, including a page that answered with an HTTP error or was too large to read, and one per parsed page of a PDF, up to 50. A refused URL and a fetch that couldn't be performed cost nothing. A request that ends in an error pays nothing.
- The page fee is reported as
cost_usd_web_fetch in usage, and in the cost_usd event when streaming. Your dashboard request logs include it in the request's total cost, and your discounts apply to it as to every other cost line.
Limits & Reliability
- Fetches. Up to 10 per request;
max_uses can lower this.
- Content. Up to 50,000 tokens per page and 100,000 for all pages of a request. A page cut to fit is marked
truncated; once the request's budget is spent, further fetches are refused.
- Time. The fetch loop, web search included, has 3 minutes. After that the model answers with what it has.
- Failed fetches don't fail requests. For a page that can't be fetched (an HTTP error, a page too large, a spent budget, or fetching temporarily unavailable), the model gets the reason and still answers.
Differences from Anthropic's web_fetch
- The response holds the final answer only: no
server_tool_use or web_fetch_tool_result blocks and no citations.
- URLs in earlier assistant turns count as seen.
- A URL is not refused for appearing to carry a credential.
allowed_domains and blocked_domains can be combined, and rules with a path apply.
- The limits above are fixed: 10 fetches, 50,000 tokens per page and 100,000 per request, and 3 minutes.
- A PDF reaches the model as text, so images in it aren't read.
- Each fetched page has a fee; Anthropic charges for its web fetch through tokens only.
Best Practices
- Put the URL in the user message. The model can fetch only URLs it has already seen in the conversation.
- Bound long documents. Use
max_uses and max_content_tokens to keep fetches and token cost in check.
- Restrict the sources. Use
allowed_domains when the model should read only your own docs or a few known sites.
- Expect a late first token when streaming. The stream starts after the pages are fetched, so show progress in your UI.
- Monitor cost. Watch
cost_usd_web_fetch in responses and in your dashboard; pages are billed in addition to tokens.