Document to Markdown for LLM & RAG (PDF, DOCX, PPTX, XLSX, OCR) avatar

Document to Markdown for LLM & RAG (PDF, DOCX, PPTX, XLSX, OCR)

Pricing

Pay per event

Go to Apify Store
Document to Markdown for LLM & RAG (PDF, DOCX, PPTX, XLSX, OCR)

Document to Markdown for LLM & RAG (PDF, DOCX, PPTX, XLSX, OCR)

Convert PDF, Word, PowerPoint, Excel, HTML and scanned images to clean Markdown or token-sized chunks for RAG, LangChain and LlamaIndex. Headings, tables, images and Tesseract OCR included. Pay per page.

Pricing

Pay per event

Rating

0.0

(0)

Developer

Tidewell Software

Tidewell Software

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

an hour ago

Last modified

Security

Limited permissions

Share

Document to Markdown for LLM & RAG pipelines

Turn PDF, Word (DOCX), PowerPoint (PPTX), Excel (XLSX/XLS), HTML, EPUB, CSV and scanned images into clean, structured Markdown, or into token-sized chunks with page and heading metadata, ready for embeddings, RAG, LangChain, LlamaIndex, n8n and Make.

  • Real structure. Headings come from font sizes and weights, so #, ## and ### follow the document. Lists stay lists, and tables become Markdown tables. Reading order works across columns, and running headers, footers and page numbers are removed.
  • OCR built in. Tesseract OCR runs only on scanned pages and images, in 25 languages. Pages that already have a text layer are read directly, which is faster and exact.
  • RAG-ready chunks. Split by heading, or by N tokens with overlap. Every chunk has page_start, page_end, heading_path and a token count.
  • Images. Embedded images can be saved to the key-value store and linked from the Markdown.
  • Pay per page. You pay only for pages that produced text. Failed, encrypted and empty files are free.

Docs, examples, MCP setup and an honest comparison with other actors: plugin.benjaminbritton.co.uk/doc-to-markdown/

What can you use it for?

Use caseHow
RAG knowledge base from PDFs, policies, manuals and contractsoutputFormat: json, chunking: tokens, then embed chunks[].text
LLM-ready context for agents (Claude, GPT, Gemini)outputFormat: markdown, then pass markdown to the model
Scanned archives (invoices, letters, faxes) to searchable textLeave OCR on and set ocrLanguages
Slide decks and spreadsheets into a vector storePPTX gives one page per slide, with speaker notes. XLSX gives one Markdown table per sheet
Convert files your crawler foundPass a datasetId from Website Content Crawler or any scraper, plus a datasetUrlField
n8n / Make / Zapier automationsCall the Actor with a file URL and read the dataset row

Supported formats

FormatEngineNotes
PDF (digital)pdfminer.six + pdfplumber (MIT), PDFium (BSD/Apache)Headings, lists, ruled tables, images, header/footer removal, encrypted PDFs with pdfPassword
PDF (scanned)Tesseract 5 OCR (Apache-2.0)Auto-detected per page. Rotated scans are corrected
DOCXMicrosoft MarkItDown (MIT) + mammothHeadings, lists, tables, bold/italic, images
PPTXMarkItDown + python-pptxOne page per slide, with tables, speaker notes and images
XLSX / XLSopenpyxl / xlrdOne Markdown table per sheet. Formulas without cached values are shown as =FORMULA
HTMLMarkItDown + BeautifulSoupKeeps <main>/<article>, drops nav/footer/scripts and makes links absolute
PNG, JPEG, TIFF (multi-page), WebP, GIF, BMPTesseract OCR
EPUB, CSV, JSON, XML, TXT, MD, IPYNBMarkItDown

Google Drive, Dropbox and GitHub share links are converted to direct downloads automatically.

Input example

{
"fileUrls": [
"https://arxiv.org/pdf/1706.03762v7",
"https://example.com/handbook.docx",
"https://example.com/scanned-invoice.png"
],
"outputFormat": "json",
"chunking": "tokens",
"chunkSize": 512,
"chunkOverlap": 64,
"ocr": true,
"ocrLanguages": ["eng"],
"extractTables": true,
"extractImages": false
}

The main options are listed below. The Input tab documents all of them.

FieldDefaultWhat it does
fileUrlsFile links
uploadedFilesUpload files in Console
keyValueStoreId, keyValueStoreKeys, keyValueStorePrefixConvert files from a key-value store
datasetId, datasetUrlFieldurlTake URLs from a dataset
outputFormatmarkdownmarkdown or json (chunks)
chunkingnonenone (one chunk per page), heading or tokens
chunkSize, chunkOverlap512 / 64Size and overlap in cl100k_base tokens
ocr, forceOcr, ocrLanguages, ocrDpion / off / eng / 300OCR settings
pageRangeallPages to convert, e.g. 1-5,8,10-. Applies to PDF pages, slides, sheets and TIFF frames
extractTablesonMarkdown tables from PDF ruling lines
extractImages, minImageSizeoff / 64 pxSave images to the key-value store and link them
pageMarkersoffAdd <!-- page N --> comments for citations
pdfPasswordPassword for encrypted PDFs
maxFileSizeMb, documentTimeoutSecs, maxConcurrency100 / 300 / 3Limits

Output example

One dataset row per document. The full .md file (and chunks.json in JSON mode) is always saved to the run's key-value store and linked from the row.

{
"doc_id": "doc-0001",
"status": "succeeded",
"source_url": "https://arxiv.org/pdf/1706.03762v7",
"filename": "1706.03762v7.pdf",
"mime": "application/pdf",
"title": "Attention Is All You Need",
"pages": 15,
"pages_processed": 15,
"ocr_pages": 0,
"tables_count": 0,
"images": [
{ "key": "doc-0001-1706.03762v7-img-001.png", "url": "https://api.apify.com/v2/key-value-stores/.../records/doc-0001-1706.03762v7-img-001.png", "page": 3, "width": 1520, "height": 2239, "mime": "image/png" }
],
"word_count": 6025,
"token_count": 9474,
"markdown_url": "https://api.apify.com/v2/key-value-stores/.../records/doc-0001-1706.03762v7.md",
"chunk_count": 24,
"chunks": [
{
"chunk_id": "doc-0001#2",
"chunk_index": 2,
"text": "## 1 Introduction\n\nRecurrent neural networks, long short-term memory [13] and gated recurrent [7] neural networks ...",
"token_count": 447,
"page_start": 2,
"page_end": 2,
"heading": "1 Introduction",
"heading_path": ["Attention Is All You Need", "1 Introduction"],
"source_url": "https://arxiv.org/pdf/1706.03762v7",
"filename": "1706.03762v7.pdf"
}
],
"processing_ms": 5937,
"errors": [],
"warnings": []
}

Failed documents get status: "failed" with an error code, such as encrypted_pdf, corrupt_file, download_failed, file_too_large, unsupported_format, no_text or timeout. They are never charged.

The dataset has four views: Overview, Markdown, Chunks (one row per chunk) and Status & errors.

Use with LangChain

from apify_client import ApifyClient
from langchain_core.documents import Document
client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("tidewell/doc-to-markdown").call(run_input={
"fileUrls": ["https://arxiv.org/pdf/1706.03762v7"],
"outputFormat": "json", "chunking": "tokens", "chunkSize": 512, "chunkOverlap": 64,
})
docs = [
Document(page_content=c["text"],
metadata={"source": row["source_url"], "page": c["page_start"], "heading": c["heading"]})
for row in client.dataset(run["defaultDatasetId"]).iterate_items() if row["status"] == "succeeded"
for c in row["chunks"]
]
# -> vectorstore.add_documents(docs)

If you prefer to chunk with your own splitter (for example MarkdownHeaderTextSplitter), use outputFormat: "markdown" and read row["markdown"].

Use with LlamaIndex

from apify_client import ApifyClient
from llama_index.core import VectorStoreIndex
from llama_index.core.schema import TextNode
client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("tidewell/doc-to-markdown").call(run_input={
"fileUrls": ["https://example.com/handbook.pdf"], "outputFormat": "json", "chunking": "heading",
})
nodes = [
TextNode(text=c["text"], id_=c["chunk_id"],
metadata={"source": row["source_url"], "page": c["page_start"], "section": " > ".join(c["heading_path"])})
for row in client.dataset(run["defaultDatasetId"]).iterate_items() if row["status"] == "succeeded"
for c in row["chunks"]
]
index = VectorStoreIndex(nodes)

Use from the API, n8n or Make

Run it synchronously and get the rows back in one HTTP call:

curl -X POST "https://api.apify.com/v2/acts/tidewell~doc-to-markdown/run-sync-get-dataset-items?token=<APIFY_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"fileUrls": ["https://example.com/report.pdf"], "outputFormat": "markdown"}'

In n8n or Make, use the Apify node with the action "Run Actor and get dataset items".

Use from Claude / ChatGPT (MCP)

Run this actor from an AI assistant through Apify's hosted MCP server. This server URL loads only this actor:

https://mcp.apify.com/?tools=tidewell/doc-to-markdown
  • Claude (claude.ai and Claude Desktop): Settings → Connectors → Add custom connector. Name it Apify, paste the URL, then sign in to Apify.
  • Claude Code: claude mcp add apify "https://mcp.apify.com/?tools=tidewell/doc-to-markdown" -t http
  • ChatGPT: add a custom MCP server named Apify with the URL above and OAuth. In the desktop app: Settings → MCP servers → Add server → Streamable HTTP.
  • Cursor, VS Code and other MCP clients: {"mcpServers": {"apify": {"url": "https://mcp.apify.com/?tools=tidewell/doc-to-markdown"}}}. Clients without OAuth can add the header Authorization: Bearer <APIFY_TOKEN>.

Then ask in plain language, for example: "Use tidewell/doc-to-markdown to convert https://arxiv.org/pdf/1706.03762v7 to Markdown and summarise section 3." The actor appears as the tool tidewell--doc-to-markdown. Runs are billed to your Apify account at the prices below.

Full setup guide, examples and comparisons: plugin.benjaminbritton.co.uk/doc-to-markdown/

Pricing

Pay per event. You pay only for output.

EventPriceWhen
Document converted$0.002Once per document that converts successfully
Page converted$0.0003Per page that produced text (PDF page, slide, image or TIFF frame)
OCR page+$0.0027Extra per page read with OCR, so an OCR page costs $0.003 in total

DOCX, HTML, XLSX, EPUB and text files have no fixed pages, so they are billed at one page per 500 words (minimum 1).

ExampleCost
10-page digital PDF$0.005
100-page digital PDF$0.032
10-page scanned PDF (OCR)$0.032
1,000 one-page Word memos$2.30
1,000 photographed receipts (OCR)$5.00

Failed downloads, corrupt, encrypted, unsupported and empty files cost nothing, and neither do pages without text. The Actor respects your maximum cost per run. It caps pages before converting, so it never does work it cannot bill.

FAQ

Is the text from digital PDFs OCR'd? No. Pages with a text layer are read directly, which is exact and about 10x faster. OCR runs only on pages without usable text, or on every page if you set forceOcr.

How good is table extraction? Tables with ruling lines (Word, Excel and most business PDFs) come out as Markdown tables. Tables drawn with whitespace alone, such as many academic papers, come out as aligned text lines. Word, PowerPoint, Excel and HTML tables are always converted.

Does it handle two-column PDFs? Yes. Reading order follows pdfminer's layout analysis, so columns are read one after the other instead of line by line across the page.

Which OCR languages are available? eng, deu, fra, spa, ita, por, nld, pol, ces, swe, dan, nor, fin, tur, rus, ukr, ell, ara, heb, hin, chi_sim, chi_tra, jpn, kor, vie. You can combine them, e.g. ["eng", "deu"].

What about password-protected PDFs? Set pdfPassword. Without it the document fails with encrypted_pdf and is not charged.

How big can files be? 100 MB by default, and up to 1,000 MB with maxFileSizeMb. Each document runs in its own process with a timeout, so one broken file cannot stop the run.

Are my documents stored? Only in your own Apify run storage, as the dataset and key-value store, under your retention settings. Nothing is sent to third-party AI services. Conversion runs entirely inside the Actor.

Is it a web crawler? No. It converts files you point it at. To crawl a site first, run a crawler and pass its dataset here with datasetId.

Which tokenizer is used for chunk sizes? cl100k_base (OpenAI), which gives a close estimate for most embedding models.

Keywords

pdf to markdown, docx to markdown, pptx to markdown, xlsx to markdown, html to markdown, document parser, document to markdown, pdf text extractor, pdf parser api, ocr, ocr api, scanned pdf to text, image to text, tesseract, rag, rag chunking, llm ready, llm document loader, langchain document loader, llamaindex, markitdown, unstructured alternative, docling alternative, chunk pdf for embeddings, pdf to json