🎁 New Sign up free, 10 calls on us. Up to $1, no card needed.

Web Fetch

Give the model the full text of a page you already have the URL for. Add one tool entry and Synthorai fetches the page, cleans it, and puts the readable body into the conversation — on any model, through any channel.

Minimal request

Add synthorai:web_fetch to tools on /v1/messages. Nothing else changes:

curl https://synthorai.io/v1/messages \
  -H "x-api-key: $YOUR_KEY" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-sonnet-4-6",
    "max_tokens": 1024,
    "tools": [{"type": "synthorai:web_fetch"}],
    "messages": [
      {"role": "user", "content": "Summarise https://docs.anthropic.com/en/docs/build-with-claude/tool-use"}
    ]
  }'

A page is only fetched when the model decides it needs one and you included synthorai:web_fetch. Requests without the tool are ordinary requests — zero impact on behavior or cost.

Options

Every field on the synthorai:web_fetch entry is optional:

ParameterDescription
max_usesMaximum pages to fetch in one request. Defaults to 3, capped at 10. This is a spend limit, so it is enforced across retries — a request declaring max_uses: 1 is billed for at most one fetch even if it is retried across channels.
{
  "type": "synthorai:web_fetch",
  "max_uses": 2
}

Using it with Web Search

Search finds pages; fetch reads them. Declaring both lets the model search, pick a promising result, and pull the whole page in — which is usually what you want for research-style questions:

"tools": [
  {"type": "synthorai:web_search"},
  {"type": "synthorai:web_fetch"}
]

Response shape

Each fetch appears in the assistant turn as a pair of blocks, followed by the model's answer:

{
  "type": "server_tool_use",
  "id": "srvtoolu_synth_...",
  "name": "web_fetch",
  "input": { "url": "https://example.com/page" }
},
{
  "type": "web_fetch_tool_result",
  "tool_use_id": "srvtoolu_synth_...",
  "content": {
    "type": "web_fetch_result",
    "url": "https://example.com/page",
    "title": "Example page",
    "content": { "type": "text", "text": "…page body…" }
  }
}

The number of fetches is reported in usage, so you can reconcile the charge yourself:

"usage": {
  "input_tokens": 9241,
  "output_tokens": 412,
  "cache_read_input_tokens": 0,
  "cache_creation_input_tokens": 0,
  "server_tool_use": { "web_fetch_requests": 1 }
}

Billing

ItemPrice
Each page fetched$0.01 / fetch
Tokens (prompt + completion)Standard input price for the model in use
!

The per-fetch fee is not the whole cost. A fetched page enters the conversation as input tokens, and a long article can be several thousand of them — on most models that token cost exceeds the $0.01 fee. Budget for both.

Three ways to keep it predictable:

  • Set max_uses to the smallest number your task needs — it caps the fee for the whole request, retries included.
  • Fetch specific pages rather than letting the model explore. A URL the user pasted is one fetch; "read around the topic" can be three.
  • Watch usage.server_tool_use.web_fetch_requests and usage.input_tokens together — the second is where the money usually goes.

Limits and safety

  • Only http and https URLs are fetched. URLs with embedded credentials are rejected.
  • Private, loopback, link-local and cloud-metadata addresses are refused — web_fetch reaches public pages only.
  • Page bodies are truncated to a configured character limit before entering the conversation, so a single huge page cannot inflate one request without bound.
  • A page that cannot be fetched comes back as an error block rather than failing the whole request, and the model can react to it or answer without the page.
  • While you declare synthorai:web_fetch, the bare name web_fetch is reserved — declaring a tool of your own by that name returns a 400 naming it. Rename yours, or drop the synthorai: parameter.
  • A fetch we refuse before it leaves our network — rejected URL, blocked domain, backend not configured — is not charged. A fetch that reached the provider and failed there is charged, because the provider charged us. Both still count against max_uses.

Your responsibility for what you fetch

web_fetch retrieves a page on your instruction. You decide which URL to fetch, so you are responsible for that retrieval and for what you do with what comes back.

  • You must have the right to access the content. That includes the target site's terms of service, its robots.txt, any paywall or login boundary, and applicable copyright and database rights. Fetching through us does not grant you access you would not otherwise have.
  • Personal data stays your obligation. If a fetched page contains personal information, you remain the controller of it under GDPR, PIPL and equivalent regimes — including lawful basis, retention and any request to erase.
  • Claims arising from your fetches are yours. If a rights holder or a site operator brings a claim over content you retrieved, that claim is directed at you and you bear the cost of it. This mirrors the terms of the upstream retrieval providers we route through.
  • Model output is not cleared for reuse. A model answer built on fetched material may reproduce parts of it. Whether you may republish that output is a question about the source's licence, and we make no representation about it.

What is on us: we run the retrieval infrastructure, block internal and private-network addresses, honour the domain blocklist your administrator configures, and do not retain fetched page content beyond serving your request. We do not review the legality of individual URLs, and we cannot — a URL alone does not carry its own licence.

!

We will suspend web_fetch on an API key that draws credible abuse reports or that a retrieval provider flags, because a suspended provider account takes the feature down for every customer.

FAQ

Can the model fetch a page I did not mention?

It can follow a URL it found through synthorai:web_search in the same conversation, which is the point of combining the two. It is instructed not to invent URLs; if it needs to find a page it should search first.

What happens if the page is behind a login or blocks crawlers?

You get an error block for that fetch and the model continues without the page. The fetch still counts toward max_uses because the request was really made on your behalf.

Does declaring the tool change anything when no page is fetched?

No. Billing and behavior are identical to a plain request until the model actually fetches something.