PDF & Document to Markdown and Text: PDF, DOCX, PPTX, HTML avatar

PDF & Document to Markdown and Text: PDF, DOCX, PPTX, HTML

Pricing

from $1.00 / 1,000 document converteds

Go to Apify Store
PDF & Document to Markdown and Text: PDF, DOCX, PPTX, HTML

PDF & Document to Markdown and Text: PDF, DOCX, PPTX, HTML

Convert PDFs, Word, PowerPoint, Excel, CSV, text files and web pages from URLs into clean Markdown and plain text for LLMs, RAG and AI agents. Headings, lists, tables, metadata, page splits, scanned-PDF detection. $1 per 1,000 documents + $0.10 per 1,000 PDF pages.

Pricing

from $1.00 / 1,000 document converteds

Rating

0.0

(0)

Developer

Naveen Raja

Naveen Raja

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

5 hours ago

Last modified

Categories

Share

Turn PDFs, Word documents, PowerPoint decks, Excel sheets, CSV, text files and web pages into clean Markdown and plain text, ready for LLMs, RAG pipelines and AI agents. Paste links (or pick a key-value store with files), and get one row per document with the content, the title, author and other document metadata, page count, word count and language. The real file type is detected from the file itself, so links without a file extension work too. Headings, lists and tables are kept where the format has them; running headers, footers and page numbers are removed from PDFs; scanned PDFs are flagged instead of returning garbage.

What you pay forPrice
document: one converted document (any format)$0.001 ($1 per 1,000 documents)
page: one PDF page or PowerPoint slide with text, or 1,000 spreadsheet data rows (started; header rows are free)$0.0001 ($0.10 per 1,000 pages)
Actor start (standard Apify event)$0.00005 per GB of run memory, once per run ($0.00005 at the default 512 MB)
ExampleChargedCost
1 web page or Word file1 document$0.001
A spreadsheet or CSV with up to 1,000 data rows (the default maxRowsPerSheet converts at most 1,000 per sheet)1 document + 1 page$0.0011
A CSV with 2,500 data rows and maxRowsPerSheet 2,500 or more1 document + 3 pages$0.0013
A 15-page PDF (an arXiv paper)1 document + 15 pages$0.0025
A 100-page PDF report1 document + 100 pages$0.011
A 492-page PDF (NIST SP 800-53, tested)1 document + 492 pages$0.050
1,000 web pages1,000 documents$1.00
1,000 PDFs of 10 pages1,000 documents + 10,000 pages$2.00
Our mixed test batch: 5 PDFs (743 pages), 3 PowerPoint decks (13 slides), 2 Word files, an Excel workbook, 2 CSV files, a text file, a Markdown file and 4 web pages, plus 3 files that were free (2 scanned PDFs, 1 legacy .ppt)19 documents + 758 pages (755 pages and slides + 1 page each for the 3 spreadsheets)$0.095

Apify platform costs are included. You don't pay for failed downloads (404, access denied, login pages, timeouts), files over your size limit, unsupported formats, empty documents, web pages that need JavaScript, scanned PDFs (also when they carry a stamp or header line), pages without text or with only a page number, the same document given twice (after redirects), or documents skipped at your spending limit. Set Maximum cost per run (maxTotalChargeUsd in the API) and the Actor stops cleanly at that amount; it never converts pages it can't charge for.

What you get

  • One tool for the common formats: PDF, Word (.docx, .docm, .dotx), PowerPoint (.pptx, .pptm, .ppsx), Excel (.xlsx, .xlsm), CSV / TSV, plain text, Markdown and any web page.
  • Viewer links work: GitHub / GitLab file pages, Dropbox share links and Google Drive / Docs links are rewritten to the file itself (public files only), with a note in the row.
  • Markdown that keeps the structure: headings (#, ##...), bullet and numbered lists with their levels, tables as Markdown tables, bold and italic (Word), footnotes and endnotes ([^1]), text boxes, and optionally links. Or plain text, or both.
  • Clean PDF text: paragraphs re-joined across line breaks, words split by end-of-line hyphens repaired, headings detected from font sizes, bold numbered titles ("3.1 Methods") and the PDF's bookmarks, bullets turned into lists, superscripts kept on their line, and repeated page headers, footers and page numbers removed.
  • Web pages: only the main content (navigation, menus, cookie banners, footers and ads dropped), with title, author, publish date, site name and description.
  • PowerPoint: one section per slide (## Slide 3: Title) in reading order, bullet levels, tables, chart titles and the speaker notes.
  • Excel and CSV: every sheet as a Markdown table (header row first, formulas as their saved values), with a row limit per sheet so huge sheets stay usable. Charged per document plus one page per started 1,000 data rows (the header row is free); each row shows its metadata.rows and pageEvents.
  • Metadata: title, author, subject, keywords, created / modified dates, creating application, page count, word count, character count, table count and language (ISO 639-1).
  • One row per page if you want it (splitPages): page-level rows with pageNumber for citations and RAG chunking.
  • Page selection: pageRange ("1-5, 8, 10-") and maxPages to convert only what you need (and pay only for that).
  • Scanned PDFs are detected: a page that is an image with no text, or only a stamp / header line (court headers, Bates numbers), is reported with needsOcr: true and listed in pagesWithoutText; a document made only of such pages gets status: "needs_ocr". Not charged.
  • Clear errors per document: every input gets a row with status (ok, needs_ocr, empty, failed, skipped) and, if it wasn't converted, an errorKind (not_found, access_denied, login_required, too_large, unsupported_type, encrypted, timeout, duplicate, charge_limit...) and a readable error. One bad link never stops the others, and a run whose links are all bad still succeeds with the error rows.
  • Safe on long runs: progress is saved in the run's key-value store, so if Apify moves the run to another server it continues where it stopped, without returning or charging a document twice.
  • Built for automation and AI agents: small, predictable rows, pay-per-event pricing without usage fees, limited permissions, and a SUMMARY record per run.

Use cases

  • RAG and knowledge bases: convert manuals, policies, reports, papers and help-center pages into Markdown chunks (one row per page with splitPages) for your vector database.
  • AI agents (MCP): give an agent one tool for "read this PDF / this deck / this page" that returns clean Markdown and says clearly when a file can't be read.
  • LLM pipelines: summarise, classify or extract data from documents without writing a parser per format.
  • Document search and analytics: word counts, languages, page counts and metadata for a list of document links.
  • Content migration: Word and PowerPoint to Markdown for docs sites, wikis and static site generators.

How to use

  1. Add Document URLs: direct links to files or web pages, one per line.
  2. Or select a Key-value store with files (optionally only some Record keys) to convert files you uploaded to Apify.
  3. Choose the Output format (Markdown, plain text or both) and, if you want, One row per page, a Page range or Max pages per document.
  4. Run it and download the results as JSON, CSV or Excel, or read them through the API. The dataset has the views Documents, Content and Status & errors; the run's key-value store has a SUMMARY record.

Input examples

The prefilled input (the IRS Form W-9 PDF and a W3C page):

{
"urls": [
"https://www.irs.gov/pub/irs-pdf/fw9.pdf",
"https://www.w3.org/WAI/fundamentals/accessibility-intro/"
]
}

Page-level rows of the first 20 pages of a long PDF, as Markdown and plain text:

{
"urls": ["https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-53r5.pdf"],
"splitPages": true,
"pageRange": "1-20",
"outputFormat": "both"
}

Files you uploaded to one of your key-value stores:

{
"keyValueStore": "<your store ID or name>",
"keyValueStoreKeys": ["contract.docx", "slides.pptx"]
}

Output example

A real row from the prefilled run (Markdown shortened):

{
"url": "https://www.w3.org/WAI/fundamentals/accessibility-intro/",
"finalUrl": "https://www.w3.org/WAI/fundamentals/accessibility-intro/",
"status": "ok",
"error": null,
"errorKind": null,
"fileType": "html",
"contentType": "text/html",
"fileName": null,
"fileSizeBytes": 57249,
"title": "Introduction to Web Accessibility",
"author": null,
"language": "en",
"pageCount": null,
"pagesProcessed": null,
"pageNumber": null,
"wordCount": 996,
"charCount": 6750,
"tableCount": 0,
"needsOcr": false,
"pagesWithoutText": [],
"truncated": false,
"notes": [],
"metadata": {
"description": "Accessibility resources free online from the international standards organization: W3C Web Accessibility Initiative (WAI).",
"siteName": "Web Accessibility Initiative (WAI)",
"publishedAt": "2026-02-03",
"canonicalUrl": "https://www.w3.org/WAI/fundamentals/accessibility-intro/"
},
"markdown": "# Introduction to Web Accessibility\n\n## Accessibility in Context\n\nThe power of the Web is in its universality.\n\nAccess by everyone regardless of disability is an essential aspect.\n\nThe Web is fundamentally designed to work for all people, whatever their hardware, software, language, location, or ability. When the Web meets this goal, it is accessible to people with a diverse range of hearing, movement, sight, and cognitive ability.\n\nThus the impact of disability is radically changed on the Web because the Web removes barriers to communication and interaction that many people face in the physical world. However, when websites, applications, technologies, or tools are badly designed, they can create barriers that exclude people from using the Web.\n\n**Accessibility is essential for developers and organizations that want to create high-quality websites and web tools, and not exclude people from using their products and services.**\n\n## What is Web Accessibility\n\nWeb accessibility means that websites, tools, and technologies are designed and developed so that people with disabilities can use them. More specifically, people can:\n\n- perceive, understand, navigate, and interact with the Web\n- contribute to the Web\n\n...",
"text": null,
"markdownUrl": null,
"textUrl": null,
"processedAt": "2026-10-18T16:03:34Z"
}

The PDF of the same run (Markdown shortened):

{
"url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf",
"finalUrl": "https://www.irs.gov/pub/irs-pdf/fw9.pdf",
"status": "ok",
"error": null,
"errorKind": null,
"fileType": "pdf",
"contentType": "application/pdf",
"fileName": "fw9.pdf",
"fileSizeBytes": 140815,
"title": "Form W-9 (Rev. March 2024)",
"author": "SE:W:CAR:MP",
"language": "en",
"pageCount": 6,
"pagesProcessed": 6,
"pageNumber": null,
"wordCount": 6391,
"charCount": 37712,
"tableCount": 0,
"needsOcr": false,
"pagesWithoutText": [],
"truncated": false,
"notes": [],
"metadata": {
"subject": "Request for Taxpayer Identification Number and Certification",
"keywords": "Fillable",
"createdAt": "2024-03-06T13:18:13Z",
"modifiedAt": "2024-03-06T13:18:13Z",
"creatorTool": "Designer 6.5",
"producer": "Designer 6.5"
},
"markdown": "Form W-9 (Rev. March 2024)\n\nRequest for Taxpayer Identification Number and Certification Department of the Treasury Internal Revenue Service Go to www.irs.gov/FormW9 for instructions and the latest information.\n\nGive form to the requester. Do not send to the IRS.\n\n...",
"text": null,
"markdownUrl": null,
"textUrl": null,
"processedAt": "2026-10-18T16:03:34Z"
}

A link that couldn't be converted (not charged):

{
"url": "https://github.com/settings/profile",
"finalUrl": "https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fsettings%2Fprofile",
"status": "failed",
"error": "The URL returned a login page (password form), not a document. Only public pages can be converted.",
"errorKind": "login_required",
"contentType": "text/html; charset=utf-8",
"markdown": null
}

Output fields

FieldMeaning
url, finalUrlThe input URL and the URL after redirects
statusok (converted, charged), needs_ocr (scanned, no usable text layer), empty (no text), failed, skipped (spending limit reached, or the same document as another input)
error, errorKindWhy a document failed, in words and as a stable code
fileTypepdf, docx, pptx, xlsx, csv, html, markdown or txt (detected from the content)
contentType, fileName, fileSizeBytesAs sent by the server (file name from the URL or Content-Disposition)
title, author, languageFrom the document metadata (or the first heading); language detected from the text
pageCount, pagesProcessed, pageNumberPages / slides / sheets in the document, how many were converted, and the page of a page row
wordCount, charCount, tableCountSize of the converted content
needsOcr, pagesWithoutTextScanned pages that have no text layer
pageEventspage events for this row as planned when it was stored: PDF pages / slides with text, or one per started 1,000 spreadsheet data rows; 0 for free rows. In page-row mode (splitPages) PDF / PowerPoint page rows show 1 or 0 and a workbook's events are on its first sheet row. Rows are stored before their page events are charged, so a failed row never costs anything; the run's SUMMARY (chargedEvents, perDocument.billedPages, pageChargesCut) is the source of truth for what was charged. It only differs if a charge is cut at the limit (the status message then says so).
truncated, notesWhether something was left out (page range, limits) and why
metadataSubject, keywords, description, created / modified / published dates, creating application, producer, site name
markdown, textThe content (null for the format you didn't ask for)
markdownUrl, textUrlLinks to the full content in the key-value store when it is too large for one dataset row (over 5 MB)

Use it from the API, an app or an AI agent

Run synchronously and get the rows in one call (keep your API token in the APIFY_TOKEN environment variable):

curl -X POST "https://api.apify.com/v2/acts/fairfetch~document-to-markdown/run-sync-get-dataset-items?maxTotalChargeUsd=0.10" \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"urls": ["https://www.irs.gov/pub/irs-pdf/fw9.pdf"], "outputFormat": "markdown"}'

Python (pip install "apify-client>=3"):

import os
from decimal import Decimal
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("fairfetch/document-to-markdown").call(
run_input={"urls": ["https://www.irs.gov/pub/irs-pdf/p17.pdf"], "splitPages": True, "maxPages": 30},
max_total_charge_usd=Decimal("0.05"),
)
for row in client.dataset(run.default_dataset_id).iterate_items():
print(row["pageNumber"], row["wordCount"], row["markdown"][:80])

The Actor runs with limited permissions and pay-per-event pricing without usage fees, so AI agents can call it through the Apify MCP server and pay per run. Rows have a fixed set of fields, failures come back as rows with a clear errorKind, and maxPages / pageRange keep long documents from flooding an agent's context.

Performance and limits

  • From our test runs on Apify: the prefilled input (a 6-page PDF and a web page) takes 5-9 seconds at the default 512 MB; a single web page about 3 seconds. A batch of 22 mixed files with 764 pages took 18 seconds at 1,024 MB and 41-93 seconds at 512 MB. A 492-page PDF converts in about 3 seconds of CPU; memory stayed around 100-225 MB.
  • For big batches of large PDFs or long web pages, give the run 1,024-2,048 MB: Apify gives more CPU to more memory, so it runs several times faster for about the same platform cost.
  • Downloads run in parallel (maxConcurrency, default 4); documents are converted one at a time in a separate process, so a damaged file that crashes a parser or a document that takes longer than about 5 minutes fails alone and the run continues.
  • Files are limited to maxFileSizeMb (default 50 MB, up to 200 MB), checked from Content-Length before downloading and again while downloading; requestTimeoutSecs limits each download, retries included. Plain text and web pages are converted up to about 30 MB per GB of run memory (about 15 MB at the default 512 MB); longer files are cut there and marked truncated with a note. Spreadsheets and CSV files are cut at maxRowsPerSheet (default 1,000) rows per sheet and 100 columns; if you raise it to tens of thousands of rows, give the run 2 GB or more (a 45,000-row x 65-column workbook peaked at about 760 MB).
  • No OCR: scanned PDFs and images are reported (needs_ocr / unsupported_type), not read. A page counts as scanned when it has no text of its own and images cover at least 30% of it, or when images cover at least 90% of it and its few characters (under 100, after running headers and page numbers are removed) are stamp-like: in the top or bottom 12% of the page, or vertical, or, only when the covering image looks like a scanner's output (1-bit or grayscale, JBIG2 or CCITT compression), in small type (12 pt or less) anywhere on the page, like a mid-page "RECEIVED" stamp. Born-digital pages are not scans: photo slides with a caption, and full-page colour photos with a small caption (portfolios, brochure covers), are converted and billed as text pages. A colour scan with a small mid-page stamp and no other text is therefore billed as a one-line text page.
  • PDF tables come out as text lines in reading order; there is no table reconstruction for PDFs (Word, PowerPoint, Excel, CSV and HTML tables are real Markdown tables). Multi-column PDFs follow the order of the text in the file, which is the reading order for most generated PDFs.
  • Not supported: legacy .doc / .xls / .ppt, OpenDocument, RTF, EPUB, ZIP archives, images, audio and video (reported with a clear error, not charged). Password-protected PDFs are reported as encrypted.
  • Web pages are downloaded, not rendered: pages that build their content with JavaScript come back empty with a note (not charged). Pages behind a login are reported as login_required. Links to local or private network addresses (localhost, 10.x, 169.254.x...) are refused, also after redirects (blocked_host).
  • The Actor identifies itself with the User-Agent fairfetch-document-to-markdown/1.0; some sites refuse automated downloads (access_denied).
  • Content over 5 MB per row (whole document or one page row) is stored in the run's key-value store and linked from the row (markdownUrl, textUrl); the row keeps the first 1.5 MB of each.
  • The run ends SUCCEEDED when documents were converted or when every failure was the input's own problem (404, unsupported, too large, invalid link, empty, scanned); the status message and the error rows say what happened. It FAILS only when nothing could be converted because every download was blocked by the sources or because of an internal error.

FAQ

Which file types are supported? PDF, DOCX / DOCM / DOTX, PPTX / PPTM / PPSX, XLSX / XLSM, CSV / TSV, TXT, Markdown and HTML web pages. The type is detected from the file content (magic bytes and the parts inside Office files), then from the Content-Type header and the file name, so https://example.com/download?id=123 works.

Can it read scanned PDFs? It detects them (needsOcr: true), including scans with a stamped header line, but doesn't run OCR, and you don't pay for them. PDFs that already have a text layer (including scans that were OCR'd before) are converted normally.

How are pages counted? Each PDF page and PowerPoint slide that has text is one page event, the same with or without splitPages. Pages without text, with only a page number or running header, or scanned pages are free. Excel and CSV files count one page per started 1,000 converted data rows, all sheets together, header rows not counted (1-1,000 data rows = 1 page; 2,500 data rows = 3 pages if you raise maxRowsPerSheet to 2,500, since the default converts at most 1,000 rows per sheet). Every row shows the page events it was charged in pageEvents. Word, text and web pages are charged per document only.

How do I convert my own files? Upload them to a key-value store in Apify Console (Storage → Key-value stores) and select that store in Key-value store with files (Apify then lets the run read it), or link them from any public URL, including GitHub, Dropbox and Google Drive / Docs share links of public files.

Why is my Markdown different from what I see in the PDF? PDFs store positioned text, not structure. The Actor rebuilds paragraphs, headings and lists from fonts and positions; complex layouts (tables, sidebars, forms) can come out in a different order. Word, PowerPoint and HTML keep their real structure.

Does it follow robots.txt? It downloads only the URLs you give it (no crawling), follows redirects, and sends a clear User-Agent. Use it only for documents you are allowed to access.

Can I limit the cost? Yes: maxPages, pageRange and maxFileSizeMb per document, and Maximum cost per run for the whole run.

This Actor downloads only the documents and pages whose links you provide and converts them; it does not log in, bypass paywalls or access controls, or crawl other pages. You are responsible for having the right to access and process the documents, and for complying with their licenses, the sites' terms and the laws that apply to you, including data protection laws if documents contain personal data. The open-source libraries used (PDFium via pypdfium2, python-docx, python-pptx, openpyxl, trafilatura) are under permissive licenses (BSD, MIT, Apache-2.0). This Actor is not affiliated with Adobe, Microsoft or any website whose documents you convert; product names are used only to describe file formats.