URL to Markdown API
Any URL in, clean Markdown out
The page's content as Markdown, ready for a prompt, a chunker or a vector store — from a real browser, with numbers on how clean it is.
By Roger Campos · Last updated: September 2026
TL;DR
POST a URL to /markdown and get the page's main content back as Markdown — headings, lists, tables, fenced code and absolute links, with navigation, sidebars and cookie banners left out. The page is rendered in real Chrome first, so JavaScript sites come back complete, and the conversion is a deterministic walk of the DOM: no model, 1 credit a page.
Free plan, no credit card. 1,000 credits a month.
Try it now — no signup
Try your own URL
Paste up to 10 URLs
Your result will appear here.
Pick one of the pages above to get started.
The request
One POST, one bearer token
curl -X POST https://urlpipe.dev/markdown \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching", "sync": true, "page_options": {"block_cookie_banners":true}}'import requests
res = requests.post(
"https://urlpipe.dev/markdown",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={"url": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching", "sync": True, "page_options": {"block_cookie_banners": True}},
)const res = await fetch("https://urlpipe.dev/markdown", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ url: "https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching", sync: true, page_options: {"block_cookie_banners":true} }),
})$ch = curl_init("https://urlpipe.dev/markdown");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer YOUR_API_KEY",
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode(["url" => "https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching", "sync" => true, "page_options" => ["block_cookie_banners" => true]]),
]);
$response = curl_exec($ch);# HTTP caching
The HTTP cache stores a response associated with a request and reuses the stored response for subsequent requests.
There are several advantages to reusability. First, since there is no need to deliver the request to the origin server, then the closer the client and cache are, the faster the response will be. …
## [Types of caches](#types_of_caches)
In the [HTTP Caching](https://httpwg.org/specs/rfc9111.html) spec, there are two main types of caches: **private caches** and **shared caches**.Choosing
How should I choose a URL-to-Markdown API for RAG?
Judge it on four things, in this order: whether it renders JavaScript, how much of the visible text it keeps, how much text it adds that no visitor ever saw, and whether the same page always gives the same Markdown. Price per page matters less than any of these — a bad chunk costs you on every query that retrieves it.
- 1It renders the page. A client-rendered site sends an empty
<div id="root">to anything that doesn't run its scripts. An API that fetches with plain HTTP returns that shell as a successful, near-empty document — and your pipeline embeds it. URLpipe loads every page in headless Chrome and converts the rendered DOM. - 2It keeps what a reader sees. Coverage of the visible text: an article that loses its second half to an over-eager boilerplate filter answers half the questions.
- 3It doesn't add what a reader never saw. Collapsed menus, off-screen drawers, "skip to content" links, cookie-policy text: all of it is in the HTML, none of it is the page. In a vector store it becomes chunks that match queries they have no business matching. This is the number most tools never publish.
- 4It is deterministic. The same page gives the same Markdown on every call, so a content hash tells you when a page actually changed, and you re-embed only then. A model-based conversion rewrites the output on every run.
- 5Links survive. Relative links are resolved to absolute URLs, so a chunk can cite its source and an agent can follow a link without the page it came from.
- 6The bill matches the work. Check whether re-fetching an unchanged page, or a page that failed, is charged. On URLpipe neither is: cache hits and failed requests cost nothing.
Measured
What the conversion strategies measure
33 real pages, the same fetched DOM for every strategy, two numbers per strategy.
We measured the strategy /markdown ships against the strategies other tools use, on 33 pages: 14 well-known sites and 19 taken from real production requests. Coverage is the share of the text a visitor sees that made it into the Markdown. Text the reader never sees is the share of the Markdown that was not visible on the page — lower is better.
| Strategy | Coverage of visible text | Text the reader never sees | Cost / page | Time / page |
|---|---|---|---|---|
| URLpipe /markdown (DOM walk) | 87.2% | 11.0% | $0 | ~20 ms |
| Readability (what Jina Reader runs) | 88.2% | 21.3% | — | — |
| Firecrawl's selector blocklist | 85.8% | 17.0% | — | — |
| LLM conversion (URLpipe's earlier pipeline) | 79.0% | 22.0% | $0.0025 | 39 s |
What this is — and isn't
These are the strategies, reimplemented and run on the same rendered DOM — not the vendors' live APIs, which add their own fetching, rendering and post-processing. Readability keeps slightly more of the visible text (88.2% against 87.2%) and roughly doubles the hidden text that comes with it. Treat the table as a comparison of approaches, and test any API on your own pages before you pick it.
The LLM row is the pipeline /markdown replaced. It scored worse on both numbers, cost money and took seconds per page, which is why the conversion uses no model at all.
Tokens
How many tokens does Markdown save?
A page's HTML is mostly markup, inline scripts and styles, so the same content as Markdown is several times smaller. Measured on 24 September 2026 with the o200k_base tokenizer, converting each page's HTML with the converter /markdown runs:
| Page | HTML tokens | Markdown tokens | Smaller by |
|---|---|---|---|
| Wikipedia — Markdown | 99,987 | 10,938 | 9.1× |
| MDN — HTTP caching | 70,132 | 9,202 | 7.6× |
| RFC 9110 (HTTP Semantics) | 372,330 | 150,395 | 2.5× |
The ratio depends on the page, not on the converter: the RFC is almost all prose, so there is little markup to remove. The pages above are rendered on the server; on a client-rendered app the raw HTML is smaller than the Markdown of the rendered page, because the content isn't in it at all. That is the case where rendering first is the difference between an answer and nothing.
Page options
Cookie banners, ads and your own clutter
page_options clean the page before it is read. What they take out is removed from the document, not hidden, so it is gone from the Markdown too:
{
"url": "https://example.com/blog/launch",
"page_options": {
"block_cookie_banners": true,
"block_ads": true,
"remove_selectors": [".newsletter-signup", "#related-posts"],
"wait_for_selector": "article"
}
}block_cookie_bannersremoves the banners of the major consent-management providers, and the cookie-policy text their panels inject. Nothing is clicked, so no consent is given for you.block_adsblocks the major ad networks' requests and removes the slots they would fill.remove_selectorstakes out up to 50 elements of your choosing.wait_for_selector(up to 10 seconds) anddelayhold the capture for content that arrives late. An element that never appears fails the request, and costs nothing.
Limits
What it does not do
- It doesn't read CSS. The conversion walks the DOM, so text hidden only by a stylesheet can leak into the output. That is most of the 11.0% above. Elements marked
hiddenoraria-hiddenare dropped. - It doesn't crawl. One URL in, one document out. There is no link discovery, sitemap walking or whole-site job — send the URLs you already have.
- It doesn't convert PDFs. A URL that serves a PDF, an image or a download is refused as not a web page, and costs nothing.
- It doesn't extract to a schema. You get Markdown, not typed fields. For the page's title, author and date, use /meta.
- It has a size ceiling of 10 MB of HTML per page.
At scale
Built for batches
Requests are async by default: you get a token at once, and the Markdown arrives at your webhook, signed with HMAC-SHA256 and retried if your endpoint is down, or at GET /result/:token for 30 days. Send sync: true to wait for it instead. Results are stored and reused for 7 days by default (max_age sets it, up to 30), and a reused result is free. labels tag each request with your own ids — a tenant, a collection — and come back on the result.
Need the title and date alongside the text? /scrape returns markdown and meta from one page visit, each billed as usual. The HTML to Markdown for LLMs guide covers chunking what comes back.
Pricing
What it costs
| Call | Credits | Free (calls/mo) | Starter (calls/mo) | Pro (calls/mo) | Scale (calls/mo) |
|---|---|---|---|---|---|
| /markdown | 1 credit | 1,000 | 20,000 | 55,000 | 175,000 |
Cache hits and failed requests cost nothing. Paid plans are never cut off: past the allowance, extra credits are $1.50 per 1,000 credits. See every plan.
FAQ
Frequently asked questions
Does the URL to Markdown API use an LLM?
Does it work on JavaScript-heavy sites and single-page apps?
Can it convert a PDF, or crawl a whole site?
How do I remove cookie banners and ads from the Markdown?
Is re-fetching the same page charged again?
Is the Markdown the same on every call?
Make your first request in five minutes.
Free plan, no card. Confirm your email and your API key is live — you'll be making real requests in minutes.