HTML to Markdown Converter - URL or HTML to GFM
Pricing
from $1.20 / 1,000 converted pages
HTML to Markdown Converter - URL or HTML to GFM
Convert a page (by URL) or raw HTML to clean GitHub-Flavored Markdown - headings, links, fenced code, tables, strikethrough. Single, bulk, or inline HTML. $0.0015 per page no start fee, cheaper than paid converters; failed fetches free.
Pricing
from $1.20 / 1,000 converted pages
Rating
0.0
(0)
Developer
Broke to Built
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
2
Monthly active users
8 days ago
Last modified
Categories
Share
HTML to Markdown Converter - URL or Raw HTML to Clean GitHub-Flavored Markdown
Send a page URL (or raw HTML), get it back as clean Markdown with links made absolute, plus the page title.
| You give it | You get back | You pay (Free plan) |
|---|---|---|
| One page URL, a list of up to 50, or raw HTML with a base URL | Per page: title and the Markdown | $0.0015 per page |
Pay per result only: no subscription, no start fee. A failed fetch returns an error instead of Markdown and is not charged. Paid Apify plans get Store discounts on the price above.
Try it in one click
The input form is already filled in with this example. Press Start and you get the output below.
{"url": "https://info.cern.ch/hypertext/WWW/TheProject.html"}
Real output from that exact input (run on 2026-09-24; long arrays shortened to a few entries and long text cut for display):
[{"ok": true,"url": "https://info.cern.ch/hypertext/WWW/TheProject.html","title": "The World Wide Web project","markdown": "The World Wide Web project\n\n# World Wide Web\n\nThe WorldWideWeb (W3) is a wide-area [hypermedia](https://info.cern.ch/hyp…","length": 2435}]
Give it a URL or an HTML string, get clean GitHub-Flavored Markdown back - real code fences with language hints, real GFM tables, strikethrough, and links, with the scripts, styles and hidden elements stripped out. Single page, bulk list, or inline HTML. $0.0015 per page, no start fee. Failed fetches are recorded free.
Feeding pages to an LLM is the common case: Markdown carries the same structure as HTML at a fraction of the tokens. Docs migration, turning a knowledge base into a RAG corpus, and archiving articles as text all want the same thing.
What you get
Per page or HTML string, one record with these exact fields:
ok- true when the conversion produced Markdownurl- the final URL after redirects (null when you passed raw HTML with nobaseUrl)title- the page's<title>, when it has onemarkdown- the converted documentlength- character length of the Markdownerror- present instead ofmarkdownwhen the fetch failed. Never charged.
Conversion specifics that matter in practice:
- ATX headings (
#),-bullets, inlined links,**bold**,*italic*,~~strikethrough~~ - Fenced code blocks, with the language taken from
class="language-js",lang-jsorhighlight-js <table>rebuilt as a GFM pipe table, with|inside cells escaped<script>,<style>,<noscript>,<iframe>,<svg>,<link>,<meta>and elements hidden viadisplay:noneorhiddenare removed before conversion- Only
<body>is converted, so head metadata never leaks into the text - Relative links and images are rewritten to absolute URLs using the page URL (or
baseUrlfor raw HTML)
Example 1: raw HTML, showing what survives conversion
Input:
{"html": "<html><head><title>Release notes</title></head><body><h1>v2.1</h1><p>Ships <del>beta</del> <strong>stable</strong> support.</p><pre><code class=\"language-js\">const x = 1;</code></pre><table><tr><th>Flag</th><th>Default</th></tr><tr><td>retry</td><td>3</td></tr></table><script>alert(1)</script></body></html>"}
Output (real run, 2026-08-15):
{"ok": true,"url": null,"title": "Release notes","markdown": "# v2.1\n\nShips ~~beta~~ **stable** support.\n\n```js\nconst x = 1;\n```\n\n| Flag | Default |\n| --- | --- |\n| retry | 3 |","length": 114}
The <script> is gone, the <del> became ~~beta~~, the code block kept its js language hint,
the table is valid GFM, and the <title> was captured separately instead of being dumped into the
text.
Example 2: one live page
Input:
{ "url": "https://example.com" }
Output (real run, 2026-08-15):
{"ok": true,"url": "https://example.com/","title": "Example Domain","markdown": "# Example Domain\n\nThis domain is for use in documentation examples without needing permission. Avoid use in operations.\n\n[Learn more](https://iana.org/domains/example)","length": 167}
Note url is the URL after redirects. Any relative link on a page (/docs/install) is rewritten
against that final URL, so the Markdown still resolves when you store it somewhere else.
Example 3: bulk, for a docs migration
Input:
{"urls": ["https://example.com/docs/intro","https://example.com/docs/install","https://example.com/docs/api"],"maxUrls": 200}
One record per URL, each charged once. A URL that 404s becomes
{"ok": false, "error": "HTTP 404"} and is not charged. When urls is filled, the single url
field is ignored - you are charged for the pages you listed and nothing else.
Pricing
$0.0015 per page converted. No start fee. One event covers one page fetched and converted, or one raw HTML string converted, including the title extraction. A failed fetch (DNS failure, timeout, 4xx, 5xx) is recorded and never charged.
Honest comparison, prices read from the Apify Store API on 2026-08-07:
| Actor | Pricing | Cost of one page |
|---|---|---|
| This actor | $0.0015 per page, no start fee | $0.0015 |
| shoebill-dev27/html-to-markdown | $0.005 start + $0.002 per item | $0.007 |
| scrapeworks/pandoc-document-converter | $0.00005 start + $0.005 per result | $0.005 |
Those are single-page costs. On a 500-page docs migration the start fee amortizes, so the honest comparison there is per-item: $0.0015 against $0.002 and $0.005.
When NOT to use this
- JavaScript-rendered pages. This fetches server HTML. If the content only exists after the page
runs its scripts (most single-page apps, infinite-scroll feeds), you will get the shell, not the
article. Render it yourself with a browser actor and pass the result in as
html. - Pages behind a login or a bot wall. Requests are plain unauthenticated GETs with a normal User-Agent. There is no proxy rotation, no cookie jar, no CAPTCHA handling.
- PDFs, Word documents, or images. This is an HTML converter. Use a PDF text extractor for those.
- Pixel-faithful layout. Deeply nested tables and CSS grid layouts are flattened into valid Markdown, which is a lossy target by design.
- A single page, once, by hand. Browser extensions do that for free. This is worth paying for in a pipeline, across many pages, or as a tool an agent can call.
Honest limits
- Server-side HTML only, as above. 20 second fetch timeout per page.
- Up to 200 pages per run (
maxUrls, default 50). - Nested tables are simplified; a table inside a table cell will not round-trip.
- Raw HTML mode takes priority: if you pass
html, no URL is fetched in that run. PassbaseUrlwhen you want relative links inside that HTML resolved to absolute ones.
FAQ
How do I convert a web page to Markdown for an LLM or RAG pipeline?
Send {"url": "https://..."} and use the markdown field. It typically costs a fraction of the
tokens the raw HTML would, and it keeps the headings, lists and tables that chunking relies on.
Can I pass HTML I already fetched or rendered myself?
Yes, that is the html field, and it is the recommended path for JavaScript-heavy sites: render the
page with your own browser or a browser actor, then send the DOM here. Add baseUrl so relative
links resolve.
Does it keep code blocks and their language?
Yes. <pre><code class="language-js"> becomes a fenced block tagged js. Common class conventions
(language-, lang-, highlight-) are all recognized.
Are HTML tables converted to Markdown tables?
Yes, as GFM pipe tables, with a header separator row and | characters inside cells escaped so the
table does not break. Ragged rows are padded to the widest row.
How many pages can I convert in one run?
Up to 200. Put them in urls; each is one record and one charge, and one bad URL does not stop the
rest.
Do relative links break when I move the Markdown elsewhere? No. Links and image sources are resolved against the page's final URL (after redirects), so they come out absolute.
Why did a page come back with almost no Markdown?
Almost always JavaScript rendering: the server HTML really was near-empty. Check length - if it is
tiny for a page you know is long, render it first and pass html.
Use from code or AI agents
curl -s "https://api.apify.com/v2/acts/EliAI~html-to-markdown/run-sync-get-dataset-items?token=$APIFY_TOKEN" \-X POST -H 'Content-Type: application/json' \-d '{"url":"https://github.com/apify/apify-sdk-js"}'
Agents: connect Apify MCP and call the EliAI/html-to-markdown tool.
- Capability: convert a page URL, a bulk list of URLs, or a raw HTML string into GitHub-Flavored Markdown
- Required input:
url,urls, orhtml - Returns: one record per page;
markdownandtitleare the payload - Bounded: 200 pages per run; failures isolate per page
- Side effects: none beyond fetching the pages you named
Related actors
- Markdown to HTML (
EliAI/markdown-to-html) - the inverse trip, with a table of contents and sanitized output.
Who uses it
- RAG developers converting documentation pages to Markdown to cut tokens before embedding
- Docs teams migrating a legacy HTML knowledge base into a Markdown docs repo
- Researchers archiving articles as readable text files with links kept
- Newsletter writers pulling a web article into Markdown to quote and edit it
- AI agent builders giving an agent a tool that reads a web page as Markdown instead of raw HTML
Changelog
- 2026-09-24: New summary at the top of this page: what you send, what you get, the price, and a real output from the prefilled example.