One HTTP request. Clean, structured Markdown back. web‑fetch fetches any public page and strips it down to what machines actually need — no scripts, no noise, no CSS soup.
Models are trained to read Markdown fluently. Give them the same document as clean headings, lists and links — not 2,000 lines of tangled HTML.
HTML carries a huge overhead of tags, scripts and inline CSS that models pay for token-by-token. Conversion removes the dead weight before it ever reaches the API.
Clean content means accurate retrieval. No hidden text, no ad injections, no scripts — just the real article, chunked and embedded the way you want it.
Your agent asks for a URL, gets an answer it can actually use: title, final URL, status, and structured content — in one deterministic JSON envelope.
SSRF-hardened so it survives being open to the internet: every resolved IP, port, redirect and scheme is validated before a single byte is fetched.
Plain HTTP, JSON in and out. Works from Python, Node, curl, n8n, Make, Zapier — or straight from a prompt-tool like a glorified curl.
This box is calling the real API, right now. Paste any public http(s) URL and watch the web become Markdown.
// GET /fetch?url=<url> · try it with any public page — or an RSS feed, a JSON API, a robots.txt…
// output will appear here…
$ curl -s 'https://web-fetch.xuss.us/fetch?url=https://example.com'
{
"ok": true,
"url": "https://example.com",
"final_url": "https://example.com/",
"status": 200,
"content_type": "text/html",
"mode": "html",
"title": "Example Domain",
"redirects": [],
"truncated": false,
"markdown": "# Example Domain\\n\\nThis domain is for use in illustrative examples…"
}
const res = await fetch(
'https://web-fetch.xuss.us/fetch?url=' +
encodeURIComponent('https://example.com')
);
const { markdown, title, status } = await res.json();
// or raw markdown mode:
const md = await fetch(
'https://web-fetch.xuss.us/fetch?url=https://example.com',
{ headers: { accept: 'text/markdown' } }
).then(r => r.text());
import requests
r = requests.get(
"https://web-fetch.xuss.us/fetch",
params={"url": "https://example.com"},
timeout=30,
)
data = r.json()
print(data["title"])
print(data["markdown"])
# POST works too:
r = requests.post(
"https://web-fetch.xuss.us/fetch",
json={"url": "https://example.com"},
timeout=30,
)
<?php
$url = "https://web-fetch.xuss.us/fetch?url=" . urlencode("https://example.com");
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$body = curl_exec($ch);
curl_close($ch);
$data = json_decode($body, true);
echo $data["title"] . "\n";
echo $data["markdown"];
// raw markdown mode:
$md = file_get_contents($url . "&raw=1");
// POST with limits works too:
$ch = curl_init("https://web-fetch.xuss.us/fetch");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
"url" => "https://example.com",
"max_chars" => 2000,
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/json"]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$res = curl_exec($ch);
curl_close($ch);
?>
| FIELD | TYPE | MEANING |
|---|---|---|
ok | bool | true on success, false on error |
url | string | the URL you submitted |
final_url | string | URL after redirects (useful for citations) |
status | int | HTTP status of the fetched page |
content_type | string | MIME type actually returned |
mode | string | html (converted) or text (plain/JSON) |
title | string|null | page <title>, when present |
redirects | int[] | status codes of each redirect hop |
truncated | bool | true if content hit a size cap |
markdown | string | the converted content |
| PARAM | WHERE | NOTES |
|---|---|---|
url required | query or JSON body | http/https only, max 4096 chars |
raw | query | raw=1 returns the markdown body directly (text/markdown) |
Accept: text/markdown | header | same as raw=1 |
max_bytes | query or JSON body | cap downloaded bytes (min 1024, service max) — stops early, saves bandwidth |
max_chars | query or JSON body | cap markdown output length (min 256, up to the service cap) |
max_html | query or JSON body | cap HTML fed to the converter (min 1024, up to the service cap) |
| HTTP | CODE | WHEN |
|---|---|---|
| 400 | invalid | malformed URL, missing url, bad JSON body |
| 403 | blocked | blocked host, port or credentials in URL |
| 404 | dns | hostname does not resolve |
| 413 | too-large | request body over 16 KB |
| 415 | unsupported | content type can't become markdown (images, PDFs…) |
| 422 | redirect | too many redirects / bad redirect location |
| 429 | rate-limited / busy | too many requests, or server at capacity |
| 502 | unreachable / tls / reset / upstream | upstream refused, bad certificate, reset, other failure |
| 504 | timeout | upstream did not respond in time |
A public service with a simple rule: your requests leave no trace.
Add “fetch the URL as markdown” to your AI workflow — RAG, summarization, research, chat-with-web. It costs one request and makes every downstream call cheaper and sharper.