Webpage Text Extractor — URL to Clean Text & Markdown
Pricing
from $24.00 / 1,000 text extractions
Webpage Text Extractor — URL to Clean Text & Markdown
Get the readable text off a web page. Pass article or page URLs and get the clean main text back as Markdown or plain text, one result per URL, with ads, navigation and boilerplate stripped. $0.03 per page. Built for RAG and AI agents.
Pricing
from $24.00 / 1,000 text extractions
Rating
0.0
(0)
Developer
Broke to Built
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
2
Monthly active users
4 days ago
Last modified
Categories
Share
Webpage Text Extractor - URL to clean article text and Markdown
Give it an article URL, get back the readable main content as portable Markdown and as plain text, with nav, ads, sidebars, cookie banners and footers stripped out. One record per URL, with the title, byline, publish date, language, word count, reading time and a readability score attached. No API key, no headless browser, no crawl configuration.
This is the "reader" step in front of an LLM: raw HTML is mostly boilerplate, and boilerplate is tokens you pay for and quality you lose. $0.03 per page extracted, flat, no start fee.
What you get, per URL
| Field | Type | What it is |
|---|---|---|
markdown | string | GitHub-flavored Markdown of the main content. Links and images are rewritten to absolute URLs so the document works outside the page. |
text | string | The same content as plain text, whitespace normalized. |
title | string | og:title, else <title>, else the first <h1>. |
byline | string | null | meta[name=author], meta[property=article:author], or a [rel=author] element. null when the page declares none. |
publishedAt | string | null | article:published_time, else the first <time datetime>. |
lang | string | null | The lang attribute on <html>. |
excerpt | string | Meta description if present, else the first 280 characters of the text. |
wordCount | number | Words in the extracted content. |
readingTimeMin | number | wordCount / 225, rounded up, minimum 1. |
fleschReadingEase | number | null | Flesch Reading Ease of the extracted content (English-calibrated). |
url, finalUrl, statusCode | What you asked for, where it landed after redirects, and the HTTP status. |
Set outputFormat to markdown, text, or both (default) to control which of the two big
fields are returned.
How the extraction works
- Noise elements are removed outright:
script,style,nav,header,footer,aside,form,iframe, plus common ad / share / comment / newsletter / cookie / breadcrumb classes and ARIA landmark roles. - A semantic container is preferred -
article,main,[role=main],#content,.post-content,.entry-content,.article-body, and similar - as long as it holds real text. - If none qualifies, every
div/section/tdis scored by text length, paragraph count and link density, and the winner is used. That last term is what keeps a link-farm sidebar from beating the article. - Links and images inside the winner are resolved against the final (post-redirect) URL, then the block is converted to Markdown.
No headless browser is involved, which is why it is fast and cheap - and also why a page that renders its article client-side has nothing for it to read. See the honest limits below.
Input
Single URL:
{ "url": "https://en.wikipedia.org/wiki/Web_scraping", "outputFormat": "both" }
Bulk - one record and one charge per URL:
{"urls": ["https://a.com/post", "https://b.com/post"],"maxUrls": 25,"outputFormat": "markdown"}
| Field | Type | Default | Notes |
|---|---|---|---|
url | string | - | One URL. A bare domain gets https:// prepended. |
urls | array of strings | - | Bulk list; takes precedence over url. |
maxUrls | integer | 25 | Hard cap on pages processed, and therefore on spend. |
outputFormat | string | both | both, markdown, or text. |
Output - two real records
Verbatim from a live run on 2026-08-15 (the long fields are truncated here with ...; the real
records carry them in full).
A normal article
{"url": "https://en.wikipedia.org/wiki/Web_scraping","finalUrl": "https://en.wikipedia.org/wiki/Web_scraping","statusCode": 200,"title": "Web scraping - Wikipedia","byline": null,"publishedAt": null,"lang": "en","excerpt": "From Wikipedia, the free encyclopediaMethod of extracting data from websites For broader coverage of this topic, see Data scraping...","text": "From Wikipedia, the free encyclopedia\nMethod of extracting data from websites\nFor broader coverage of this topic, see Data scraping...","markdown": "From Wikipedia, the free encyclopedia\n\nMethod of extracting data from websites\n\nFor broader coverage of this topic, see [Data scraping](https://en.wikipedia.org/wiki/Data_scraping \"Data scraping\")...","wordCount": 4233,"readingTimeMin": 19,"fleschReadingEase": 50.5}
Note the link in markdown: the page wrote /wiki/Data_scraping, and the output carries the
absolute URL. That is the difference between a Markdown file you can hand to a model or a repo and
one whose links only work in the original tab.
An HTTP error page - and what it costs you
{"url": "https://apify.com/nonexistent-page-xyz-404","finalUrl": "https://apify.com/nonexistent-page-xyz-404","statusCode": 404,"title": "Page not found - Apify","byline": null,"publishedAt": null,"lang": null,"excerpt": "","text": "","markdown": "","wordCount": 0,"readingTimeMin": 1,"fleschReadingEase": null}
Read this one carefully. A server that answers a 404 with an HTML error page has served a
page, so it is extracted and it is charged. statusCode and wordCount tell you exactly what
happened - always check statusCode === 200 before trusting the text. Only two outcomes are never
charged: the fetch failing outright (DNS, timeout, connection refused), and a response that is not
HTML at all. Both return a record with an error field instead.
Call it from code or an agent
curl -X POST "https://api.apify.com/v2/acts/EliAI~webpage-text-extractor/run-sync-get-dataset-items?token=$APIFY_TOKEN" \-H "content-type: application/json" \-d '{"url":"https://example.com/some-article","outputFormat":"markdown"}'
Agents can call it as a tool over Apify MCP. The contract an LLM needs:
- Capability: fetch one or many URLs and return the readable main content as Markdown and text
- Required input:
url(string) orurls(array) - Returns: one JSON record per URL;
markdown/textplus title, byline, date, counts - Bounded:
maxUrlscaps the run and the spend; no crawling, no link following - Side effects: none - one GET per URL, nothing is stored externally
Pricing
$0.03 per page extracted - one page-extracted event per URL that returned an HTML response.
No actor-start fee, so a single URL costs exactly $0.03 and 25 URLs cost exactly $0.75. maxUrls
is your budget cap.
Honest comparison, measured on the store 2026-08-15. Most content extractors on Apify price per dataset item with a tiny start fee, and at volume they are far cheaper than this:
| Actor | Price shape | 1 page | 100 pages |
|---|---|---|---|
| eliai/webpage-text-extractor (this one) | $0.03/page, no start fee | $0.03 | $3.00 |
lukaskrivka/article-extractor-smart | $0.00005 start + $0.00001/item | ~$0.0001 | ~$0.001 |
sync-network/website-main-content-extractor | $0.00005 start + $0.0005/item | ~$0.0006 | ~$0.05 |
hichemdev/website-content-crawler | $0.005 start + $0.001/item | $0.006 | $0.105 |
easyapi/article-content-extractor | $0.09 start + $0.00299/item | $0.093 | $0.389 |
So: if you are extracting hundreds or thousands of pages, one of those is the right tool and you should use it. What you get here instead is a flat, predictable per-URL price with no start fee to amortize and no crawl to configure - which is what suits a handful of URLs you already have, called synchronously from an agent or a workflow step, where the answer is one record per URL and nothing else runs.
When NOT to use this
- Bulk crawling a whole site. There is no link following, no depth, no sitemap seeding. Use Sitemap URL Extractor to enumerate the URLs, or a per-item crawler from the table above for the extraction itself.
- JavaScript-rendered articles. The served HTML is parsed as-is. A page whose article is
assembled client-side returns a low
wordCounthonestly rather than failing - you need a browser-based scraper for those. - PDFs, images, JSON, or feeds. Non-HTML responses are reported as
error: "not an HTML page"and not charged. For PDFs use PDF Text Extractor. - Content behind a login, a paywall, or aggressive bot protection. It fetches anonymously with a plain HTTP client - no cookies, no session, no proxy rotation.
- Non-English readability scoring.
fleschReadingEaseuses English syllable heuristics; the text extraction works on any language, but that one number does not. - You already hold the HTML or the text. Then you are paying for a fetch you do not need - run your own Markdown converter over what you have.
FAQ
How do I convert a webpage URL to Markdown?
Pass the URL with outputFormat: "markdown" (or "both"). The markdown field is
GitHub-flavored Markdown of just the main content, with links and images resolved to absolute URLs
so the document is portable. No install, no library.
How do I get clean article text for RAG or an LLM prompt?
Use outputFormat: "text" and feed text straight into your chunker. Raw HTML for a typical
article is several times the size of its readable content, and every byte of that nav-and-footer
boilerplate is a token you pay for and a distraction the model has to ignore.
Does it work on JavaScript-rendered pages?
It parses the HTML the server sends, without a browser. Most articles, blogs, docs and news sites
ship their content in that HTML and work fine. A single-page app that renders the article
client-side will come back with a low wordCount - that is the honest signal to use a
browser-based scraper for that domain.
What metadata comes back with the text?
title, byline, publishedAt, lang and excerpt, plus wordCount, readingTimeMin and
fleschReadingEase. Fields the page does not declare come back null rather than guessed.
Can I extract many articles in one run?
Yes - pass an array in urls, capped by maxUrls (default 25). One record and one $0.03 charge
per page, and one bad URL never stops the others.
Am I charged if the page 404s or is empty?
Yes, if the server returned an HTML error page - it was fetched and parsed, and you get the record
with statusCode: 404 and wordCount: 0. You are not charged when the fetch fails outright
(DNS, timeout, refused) or when the response is not HTML. Check statusCode in your pipeline.
How is this different from a website content crawler? A crawler discovers URLs by following links and bills per item it discovers. This does not discover anything - you give it the exact URLs, it returns exactly that many records. Different job, different price shape; see the comparison table above.
Does it respect robots.txt or send a custom user agent?
It identifies itself as TextExtractorBot/1.0 and fetches only the URLs you pass. It does not
crawl, so there is nothing to discover or spider; you are responsible for having the right to
fetch the pages you submit.
Related actors
Same account, each doing one job well:
- Webpage Links Extractor - every link on a page, with anchor text
- Webpage Readability Grade - six readability formulas on a URL's main content
- Sitemap URL Extractor - enumerate every URL a site declares
- PDF Text Extractor - the same job for PDFs
- Redirect Chain Checker - trace every 301/302 hop
Built by Broke to Built.
For AI agents
This Actor is built to be called by software, not just by people.
- Mount it directly as an MCP tool — no Store search, no ranking, just this one tool:
https://mcp.apify.com/?actors=eliai/webpage-text-extractor - Or call it over HTTP and get the results in the same request:
POST https://api.apify.com/v2/acts/eliai~webpage-text-extractor/run-sync-get-dataset-items - Pay with x402, without an Apify account. This Actor is whitelisted for agentic payments, so an agent holding USDC on Base can buy a prepaid token and spend it here. The minimum purchase is $1, the token balance is an absolute spending cap, and it expires 14 days after purchase.
- Costs are predictable before you call. Pricing is pay-per-event (see Pricing above), so an agent can budget a run in advance instead of discovering the bill afterwards.
- Send only the field you mean. If you pass the bulk field, it is used on its own; the single-value field is a fallback, never merged into your request. You are charged for the items you sent and nothing else.