URL to Markdown

URL to Markdown API

Send a URL and get the page back as Markdown: headings, lists, tables, code blocks and links, with the site's navigation, header, footer, forms and consent banners removed. The output goes straight into a prompt, an embedding job or a retrieval index.

One request

Add return_page_markdown=true to any fetch. The response is text/markdown; charset=utf-8.

Request

curl "https://api.webfetch.io/v1/\
?url=https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FWeb%2FHTTP%2FOverview\
&return_page_markdown=true\
&render_js=false" \
  -H "Authorization: Bearer $WEBFETCH_KEY"

This page is 228,645 bytes of HTML. The Markdown is 18,847 bytes, 8.2% of the original, for 1 credit as a plain HTTP fetch.

Response, first lines

# Overview of HTTP

**HTTP** is a [protocol](https://developer.mozilla.org/en-US/docs/Glossary/Protocol) for fetching resources such as HTML documents. It is the foundation of any data exchange on the Web and it is a client-server protocol, which means requests are initiated by the recipient, usually the Web browser. A complete document is typically constructed from resources such as text content, layout instructions, images, videos, scripts, and more.

![A single Web document composed from multiple resources from different servers.](https://mdn.github.io/shared-assets/images/diagrams/http/overview/fetching-a-page.svg)

Clients and servers communicate by exchanging individual messages (as opposed to a stream of data). The messages sent by the client are called *requests* and the messages sent by the server as an answer are called *responses*.

Excerpt of the response from October 20, 2026, from MDN (CC-BY-SA 2.5).

Size on real pages

Across these 8 pages, 1,301,564 bytes of HTML became 198,276 bytes of Markdown, 15.2% of the original. A model reads the same content in fewer tokens, and an index stores no markup.

PageHTML bytesMarkdown bytesShare
WordPress News
wordpress.org/news/
155,679 2,666 1.7%
Kubernetes: Overview
kubernetes.io/docs/concepts/overview/
506,514 10,896 2.2%
MDN: Overview of HTTP
developer.mozilla.org/en-US/docs/Web/HTTP/Overview
228,645 18,847 8.2%
Python blog
blog.python.org/
31,740 2,663 8.4%
python.org: About
www.python.org/about/
41,299 4,239 10.3%
Rust: Learn
www.rust-lang.org/learn
17,594 3,161 18.0%
Python docs: Coroutines and tasks
docs.python.org/3/library/asyncio-task.html
177,177 53,453 30.2%
Effective Go
go.dev/doc/effective_go
142,916 102,351 71.6%

Measured October 20, 2026 through the API without JavaScript rendering. Each row is one URL fetched twice, once as HTML and once with return_page_markdown=true. Index and landing pages shrink the most because most of their bytes are markup. Long documentation keeps more because most of its bytes are text.

How the conversion works

Deterministic

The converter is code that maps HTML elements to Markdown. Every sentence in the output comes from the page, nothing is summarized or reworded, and the same HTML always produces the same Markdown.

Navigation and banners removed

Scripts, styles, navigation, the site header and footer, sidebars, forms, buttons, iframes, dialogs, hidden elements and the common cookie consent banners go first. When the page marks its content with main or article, only that element is converted.

Structure kept

Heading levels, paragraphs, nested lists, tables, blockquotes, emphasis, inline code, and code blocks tagged with their language when the page names it. A splitter that chunks on headings works on the output as it is.

Links that still resolve

Links and images are made absolute against the final URL after redirects. Same-page anchors become plain text, heading permalink markers are dropped, and inline base64 images are left out.

Rendered or plain

By default the page is loaded in a browser and the Markdown is made from what it rendered, for 5 credits. wait_for holds the capture until a selector appears, and js_scenario can click a "Load more" button or open a section first. When the content is already in the HTML, render_js=false fetches it over plain HTTP for 1 credit.

When a site refuses the request, mode=auto retries on progressively more capable routes and bills only the one that worked, up to max_cost. country_code fetches the page from a chosen country. robots.txt is honored on every request.

See the parameters
curl "https://api.webfetch.io/v1/\
?url=https%3A%2F%2Fexample.com%2Fapp\
&return_page_markdown=true\
&wait_for=main%20article" \
  -H "Authorization: Bearer $WEBFETCH_KEY"

Other output shapes

ParameterReturns
return_page_markdown=trueThe page content as Markdown
return_page_text=trueThe same content as plain text, without Markdown syntax
json_response=trueJSON with markdown, resolved-url, initial-status-code, cost and metadata (the page's JSON-LD and microdata), for citing the source
add_html=trueWith json_response, the raw HTML in body as well
extract_rulesWith "output": "markdown_relevant", Markdown of one part of the page, picked with a CSS or XPath selector

From code and from agents

The JavaScript, Python and PHP SDKs each have a markdown() method. The MCP server's fetch_page tool returns Markdown by default, so a connected agent reads pages as Markdown with no extra settings.

JavaScript

import { WebfetchClient } from '@tuxxin/webfetch';

const client = new WebfetchClient(process.env.WEBFETCH_KEY);
const md = await client.markdown('https://example.com/post');

Python

import os
from webfetch import WebfetchClient

client = WebfetchClient(os.environ["WEBFETCH_KEY"])
md = client.markdown("https://example.com/post", render_js=False)

PHP

use Tuxxin\Webfetch\WebfetchClient;

$client = new WebfetchClient(getenv('WEBFETCH_KEY'));
$md = $client->markdown('https://example.com/post');

MCP client configuration

{
  "mcpServers": {
    "webfetch": {
      "url": "https://webfetch.io/mcp",
      "headers": { "Authorization": "Bearer wfk_your_key" }
    }
  }
}

fetch_page takes url, render_js and return_format (markdown, text or html).

Pricing

  • 5 credits a page rendered, 1 with render_js=false.
    The same as fetching the HTML. Proxies and auto mode are priced as on any request.
  • A failed fetch costs nothing.
    Billing happens on success only, so a timeout or a block is never charged.
  • Free to start.
    The free account starts with 1,000 credits and adds 250 every month. Starter is 75,000 credits for $11.99 a month. All plans.

Questions

Does a language model write the Markdown?

No. The conversion is deterministic code that maps HTML elements to Markdown. Every sentence in the output appears on the source page, and the same HTML always gives the same Markdown.

What is removed from the page?

Scripts, styles, navigation, the site header and footer, sidebars, forms, buttons, iframes, dialogs, hidden elements and the banners of the common cookie consent platforms. When the page marks its content with main, article or role="main", only that element is converted.

What happens to links and images?

Both are made absolute against the page's final URL after redirects, so they still resolve once the text is stored somewhere else. Images keep their alt text. Same-page anchors become plain text, and inline base64 images are dropped.

Can it convert pages that build their content with JavaScript?

Yes. Pages are rendered in a browser by default (render_js=true) before they are converted, for 5 credits. wait_for and js_scenario control when the page is captured. When the content is already in the HTML, render_js=false fetches it over plain HTTP for 1 credit.

How is it billed?

5 credits a page with JavaScript rendering, the default, and 1 with render_js=false, the same as fetching the HTML. The conversion adds no cost, and a failed fetch is not billed.

Can I get the source URL and page metadata with the Markdown?

Add json_response=true. The JSON response carries markdown, resolved-url (the final URL after redirects), initial-status-code and metadata with the page's JSON-LD and microdata.

Does it follow robots.txt?

Yes, on every request. A path that robots.txt disallows for WebfetchBot returns a 403 with the code robots_disallowed and is not billed. Paid plans can opt out per key after accepting the acceptable use policy.

How large a page can it convert?

Up to 25 MB transferred, or 5 MB through the stealth pool.

Convert your first page

An email address is the whole signup, and the key works on the first request. The API reference lists every parameter, and the comparison with Firecrawl and Jina Reader covers price and billing.

Get a free key