Document to Markdown: PDF, DOCX & HTML for RAG avatar

Document to Markdown: PDF, DOCX & HTML for RAG

Pricing

from $1.00 / 1,000 pages

Go to Apify Store
Document to Markdown: PDF, DOCX & HTML for RAG

Document to Markdown: PDF, DOCX & HTML for RAG

Convert PDF, Word (DOCX), HTML pages, CSV and text into clean Markdown + JSON for LLMs and RAG: headings, tables, links, metadata and ready-to-embed chunks with heading paths and page numbers. URLs, Drive/Dropbox links or base64. No OCR, no AI cost.

Pricing

from $1.00 / 1,000 pages

Rating

0.0

(0)

Developer

Dodi Abak

Dodi Abak

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

5 days ago

Last modified

Categories

Share

Turn PDFs, Word documents, web pages, CSV and text files into clean Markdown and structured JSON that LLMs, AI agents and RAG pipelines can use right away. You get headings, paragraphs, tables (as Markdown and as JSON rows), links, metadata, word and token counts, and optional ready-to-embed chunks with section paths and page numbers.

It's fast and predictable: no OCR, no LLM calls, no browser. A 300-page PDF converts in about 5 seconds. You pay $0.001 per page.

What does it do?

InputWhat you get
PDF (URL, Drive/Dropbox link or base64)Markdown with # headings detected from font size and boldness, paragraphs re-flowed across line breaks, hyphenated words re-joined, bullet and numbered lists, tables rebuilt from column-aligned text, <!-- page N --> markers, title/author/dates, and a needsOcr flag for scanned PDFs. Password-protected PDFs work if you pass password.
Word DOCXHeadings, bold/italic, bullet and numbered lists, tables, links, plus title and author from the document properties. Google Docs links are exported as DOCX automatically.
HTML / web pageMain content only (Mozilla Readability, the engine behind Firefox Reader View): navigation, footers, cookie banners and ads removed. GFM tables, code blocks, lists, absolute links and images. Page metadata: title, author, description, publish date, language, canonical URL.
CSV / TSVA Markdown table plus JSON rows and row/column counts. The delimiter is detected automatically.
Markdown / plain textNormalized and chunked.

Every document also gets a heading outline, stats (pages, characters, words, approximate tokens, tables) and clear warnings (scanned pages, truncated files, skipped rotated text).

Why use it?

  • Better RAG answers. Chunks follow the document structure: they break at headings and paragraphs, never in the middle of a table or code block, and each chunk carries its headingPath (for example ["Annual Report", "Risk Factors"]) and page so answers can cite their source.
  • Made for AI agents. One call turns a link into text the model can read. Output fields are stable and documented, and failures come back as error codes (HTTP_ERROR, PASSWORD_REQUIRED, FILE_TOO_LARGE, UNSUPPORTED_TYPE…) instead of crashing the run.
  • Cheap and fast. Text extraction runs locally with pdf.js and mammoth, so there's no GPU, OCR or AI bill.
  • Bulk. Up to 1,000 documents per run, 3 in parallel by default.

How to use it

  1. Add your documents to Documents. Each one is a URL, or an object with base64 + fileName, html, text or markdown.
  2. Choose an Output mode: One item per document for Markdown files, or One item per chunk to load straight into a vector database (Pinecone, Qdrant, Weaviate, pgvector, Chroma…).
  3. Click Start. Download the results as JSON, CSV or Excel, use the API, or turn on Save .md files to get a download link for each Markdown file.

Input example

{
"sources": [
{ "url": "https://arxiv.org/pdf/1706.03762v7" },
{ "url": "https://drive.google.com/file/d/FILE_ID/view?usp=sharing", "id": "contract-17" },
{ "base64": "UEsDBBQABgAIAAAAIQ…", "fileName": "policy.docx" },
{ "url": "https://example.com/locked.pdf", "password": "1234" },
{ "html": "<h1>Price list</h1><table>…</table>" }
],
"outputMode": "document",
"includeChunks": true,
"chunkSize": 2000,
"chunkOverlap": 200
}

Output example (document mode, shortened)

{
"sourceIndex": 0,
"status": "ok",
"url": "https://arxiv.org/pdf/1706.03762v7",
"fileName": "1706.03762v7.pdf",
"type": "pdf",
"title": "Attention Is All You Need",
"markdown": "<!-- page 1 -->\n\n# Attention Is All You Need\n\n…\n\n### Abstract\n\nThe dominant sequence transduction models…",
"headings": [{ "level": 1, "text": "Attention Is All You Need" }, { "level": 3, "text": "1 Introduction" }],
"tables": [{ "page": 6, "method": "heuristic", "rows": [["Self-Attention", "O(n2 · d)", "O(1)", "O(1)"], ["Recurrent", "O(n · d2)", "O(n)", "O(n)"]] }],
"links": [{ "text": "https://github.com/tensorflow/tensor2tensor", "url": "https://github.com/tensorflow/tensor2tensor" }],
"metadata": { "title": null, "author": null, "producer": "pdfTeX-1.40.25", "creationDate": "2024-04-10T21:11:43Z" },
"stats": { "pages": 15, "pagesProcessed": 15, "billedPages": 15, "characters": 38203, "words": 6034, "approxTokens": 9747, "tables": 5, "headings": 15 },
"needsOcr": false,
"warnings": ["283 rotated/vertical text fragment(s) (e.g. margin stamps) were skipped."]
}

Output example (chunks mode: one item per chunk)

{
"sourceIndex": 0,
"title": "Attention Is All You Need",
"chunkIndex": 5,
"chunkCount": 27,
"headingPath": ["Attention Is All You Need", "3 Model Architecture"],
"page": 2,
"text": "### 3 Model Architecture\n\nMost competitive neural sequence transduction models have an encoder-decoder structure…",
"chars": 1437,
"approxTokens": 360
}

A document that can't be converted returns {"status": "error", "errorCode": "HTTP_ERROR", "error": "Download failed: HTTP 404 …"} and isn't charged.

Input options

FieldDefaultDescription
sources(required)URLs or objects (url, base64 + fileName, html, text, markdown). Optional per source: type, password, id.
outputModedocumentdocument (one item per file) or chunks (one item per chunk).
includeChunksfalseAdd a chunks array to document items.
chunkSize / chunkOverlap2000 / 200In characters (about 4 per token).
mainContentOnlytrueWeb pages: keep only the main article.
linkStyleinlinetext drops URLs from the Markdown to save tokens (they stay in links).
detectTablestruePDF table reconstruction.
pageMarkerstrue<!-- page N --> between PDF pages.
maxPages300PDF pages converted (and billed) per document.
includeTables / includeLinks / includeTexttrue / true / falseExtra JSON fields.
saveMarkdownFilesfalseSave doc-0001.md… to the key-value store and return markdownUrl.
maxFileSizeMb25Download size limit (up to 100).
requestTimeoutSecs60Download timeout per URL.
concurrency3Documents processed in parallel.

Pricing

$0.001 per page ($1 per 1,000 pages). There's no subscription and no charge for failed documents.

  • PDF: one page = one PDF page converted (capped by maxPages).
  • DOCX, HTML, CSV, text: one page = every 3,000 characters of extracted text (minimum 1). A typical web article is 1–4 pages.

Example: 100 ten-page PDFs cost $1.00. The Actor respects your run's maximum charge and stops cleanly when it's reached.

Use with AI agents (MCP) and the API

MCP: add this Actor to Claude Desktop, Cursor, VS Code or any MCP client through the Apify MCP server:

https://mcp.apify.com?tools=dodiabak/document-to-markdown

The agent can then call it with {"sources": ["https://…/file.pdf"]} and read the markdown field.

API (synchronous, returns the items):

curl -X POST "https://api.apify.com/v2/acts/dodiabak~document-to-markdown/run-sync-get-dataset-items?token=YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"sources": ["https://arxiv.org/pdf/1706.03762v7"], "outputMode": "chunks"}'

Python:

from apify_client import ApifyClient
client = ApifyClient("YOUR_API_TOKEN")
run = client.actor("dodiabak/document-to-markdown").call(run_input={"sources": ["https://example.com/report.pdf"]})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item["title"], item["stats"]["pages"], item["markdown"][:200])

It also works with LangChain, LlamaIndex, n8n, Make and Zapier through the Apify integrations.

FAQ

Does it do OCR on scanned PDFs or images? No. It reads the PDF text layer, which most PDFs from Word, LaTeX, browsers and reporting tools have. Scanned PDFs return needsOcr: true with a warning so your pipeline can send them to an OCR service. Image files are rejected with UNSUPPORTED_TYPE.

How good is table extraction? Tables in DOCX and HTML are exact. PDF tables are rebuilt from column positions ("method": "heuristic"). Simple tables with one line per row work well. Tables with merged cells or wrapped multi-line cells may come out partly as plain text.

Which files are supported? PDF, DOCX, HTML, CSV/TSV, Markdown and TXT. Legacy .doc, .xls, .pptx and .xlsx aren't supported yet; save them as PDF or DOCX.

Can I use private files? Yes. Send the file as base64, or share a Google Drive, Docs or Dropbox link as "Anyone with the link". Files are processed in memory, stored only in your own run storage, and never sent to a third party.

Does it crawl websites? No. It fetches exactly the URLs you give it, one request per document. To convert a whole site, run a crawler first and pass its URLs or HTML here.

What about multi-column layouts? Text is read line by line from top to bottom, so two-column academic papers can have columns interleaved on some lines. Single-column documents (most reports, contracts, invoices, manuals) convert cleanly.

Limitations

  • No OCR and no image descriptions. Formulas come out as plain text.
  • PDF heading levels are inferred from font size, so very uniformly styled PDFs may have few or no headings.
  • Max 1,000 documents per run and 100 MB per file.

Tech and sources

pdf.js (Mozilla, Apache-2.0), mammoth (BSD-2), Readability (Mozilla, Apache-2.0), Turndown + GFM plugin (MIT), linkedom (ISC).

Questions or a document that converts badly? Open an issue on the Actor's Issues tab and include a sample file if you can.