Web page to Markdown
Markdown is what a language model actually wants to read: headings, lists and links without the ad slots, nav bars and tracking scripts wrapped around them. This API takes a URL and hands back that page as tidy Markdown, so you can drop real content into a prompt instead of a wall of div soup.
Run it online
Run this on our servers with your account. Free tools run in your browser; this one bills your KIT balance per the price above.
The problem with feeding raw HTML to a model
Paste a page's raw HTML into a prompt and most of your token budget goes to markup, not meaning — nested divs, inline styles, script tags, cookie banners, and navigation menus repeated on every page of a site. A model has to work through all of that to find the three paragraphs that actually mattered, and often gets distracted or truncated before reaching them.
How the conversion works
The page is rendered first, so JavaScript-built content is included, then the meaningful structure — headings, paragraphs, lists, tables, links, code blocks — is identified and translated into standard Markdown syntax. Boilerplate like navigation, footers and ad containers is left out by design, because the goal is the content a reader came for, not a pixel-perfect reproduction of the layout.
Markdown's quiet rise as a machine format
Markdown was invented for people writing plain-text emails who wanted output that still looked reasonable as HTML. Its second life, two decades later, is as the preferred format for feeding text to language models: compact, unambiguous about structure, and far cheaper in tokens than HTML or a heavily formatted PDF. Converting the web to Markdown on demand is really converting it into the shape models were built to read.
Where the output goes next
The Markdown result drops straight into a retrieval-augmented generation pipeline, a documentation mirror, or a personal notes system — anywhere plain, structured text is easier to store and search than a rendered page. Because delivery is async by webhook or a signed 24-hour link, converting a batch of URLs doesn't mean holding open connections or polling a job list.
Who reaches for this vs. a full scrape
If you need every attribute and script on a page, this isn't that tool — reach for the rendered HTML API instead. This one is for the far more common case: you want the substance of a page, readable, structured and small, without writing a parser for every site's markup quirks.
What you can do with it
LLM context and RAG pipelines
Convert reference pages to Markdown before embedding them, so retrieval returns clean text instead of markup noise.
Documentation mirroring
Pull an external API's docs page into Markdown to keep a local, searchable copy in your own knowledge base.
Research and note-taking tools
Save articles and pages as Markdown files that drop straight into a notes app or a static site's content folder.
Content migration
Convert a legacy site's pages to Markdown as a first step toward moving them into a modern static-site generator.
FAQ
Does it handle JavaScript-rendered pages?
Yes, the page is rendered first so content built by client-side frameworks is captured before conversion, not just the raw server response.
What structures does it preserve?
Headings, paragraphs, lists, tables, links, images and code blocks are translated into standard Markdown syntax.
Will it strip ads and navigation?
Boilerplate like menus, footers and ad containers is left out by design, since the goal is readable content, not a full layout copy.
Is there a free tier?
There's no free tier — free tiers get abused and slow everyone down. Access runs on a prepaid ForHosting KIT balance: top up from $10.00 (it never expires) and each request is charged at its published price, so a call with no balance returns HTTP 402. No subscription, no tokens, no invented credits, and a failed task is never charged.
How much does it cost?
$0.040 per request plus $0.001 per URL, so converting a batch of pages has a predictable, published cost.
Can I convert many URLs in one call?
Yes, submit multiple URLs in a single request; the per-URL rate applies to each one processed.
What happens if a page fails to load?
The task retries automatically up to three times; if it still fails you get a clear error and that page is not charged.
How do I get the Markdown back?
Via a signed webhook when conversion finishes, or a signed link valid for 24 hours if you'd rather pull the result yourself.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, and soon from our app, email and Telegram.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/web/to-markdown \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://ejemplo.com"}'const res = await fetch("https://api.kit.forhosting.com/web/to-markdown", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"url": "https://ejemplo.com"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/web/to-markdown",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"url": "https://ejemplo.com"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/web/to-markdown", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"url":"https://ejemplo.com"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"url":"https://ejemplo.com"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/web/to-markdown", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"url": "https://ejemplo.com"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "web.to_markdown",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Limits
timeout_sec | 30 |
max_crawl_pages | 25 |
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |
422 | task_failed | The task failed after 3 retries. You are never charged for it. |