Documents to Markdown for LLMs: PDF, Office, HTML, OCR avatar

Documents to Markdown for LLMs: PDF, Office, HTML, OCR

Pricing

Pay per usage

Go to Apify Store
Documents to Markdown for LLMs: PDF, Office, HTML, OCR

Documents to Markdown for LLMs: PDF, Office, HTML, OCR

Convert PDF, Word, PowerPoint, Excel, HTML, text and scanned documents into clean Markdown with tables, token counts and RAG-ready chunks with page numbers. Tesseract OCR for scans. Failed documents are free.

Pricing

Pay per usage

Rating

0.0

(0)

Developer

RADO

RADO

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

Documents to Markdown for LLMs

Turn PDF, Word, PowerPoint, Excel, HTML and scanned documents into clean Markdown (or plain text) that you can feed to an LLM, a vector database or a RAG pipeline. Each document comes back with its title, page count, table count and token estimate. You can also have it split into chunks sized in tokens, with the section heading and page numbers attached to each chunk.

It is built for batch jobs and AI agents: give it a list of URLs and get one structured dataset item per document, plus one item per chunk if you turn chunking on.

What it does

  • PDF with a text layer. Text is read in reading order, including two-column layouts. Headings are detected from font size and weight, and bullet lists are kept. Ruled tables become Markdown tables. Bare page numbers and rotated margin stamps are removed.
  • Scanned PDFs and images (PNG, JPG, TIFF, WebP, BMP, GIF). OCR runs with Tesseract. In auto mode only pages without a text layer are OCR'd, so a mixed PDF is handled page by page.
  • DOCX, PPTX, XLSX, HTML, CSV, JSON, XML, TXT, MD. Headings, lists, tables, slide titles and speaker notes are kept. Each spreadsheet sheet becomes a Markdown table.
  • Chunking for RAG. Chunks follow section boundaries. A table is only split if it is larger than the chunk size, and then the header row is repeated in every part. Each chunk carries headingPath, pageStart and pageEnd. An optional token overlap is added when a long section has to be split.
  • Clear errors. Password-protected, corrupt, empty and unsupported files each get a stable error code. Nothing is charged for them.

Input

FieldDefaultNotes
sources–List of { "url": "https://..." } direct file links
keyValueStore–Optional: pick one of your key-value stores holding the files (read-only access to that store only)
keyValueStoreKeys[]Keys to convert from that store; empty = all records (except INPUT/OUTPUT)
outputFormatmarkdownmarkdown or text
ocrautoauto (scanned pages only), always, never
ocrLanguages["eng"]Any of eng, pol, deu, fra, spa, ita, por, nld, ces, ukr
chunkingEnabledfalseAlso output chunk items
chunkMaxTokens / chunkOverlapTokens800 / 80Counted with the cl100k_base tokenizer
includeContentWithChunkstrueKeep full text on the document item as well
includeTablestrueConvert tables to Markdown tables
includePageMarkersfalseInsert <!-- page: N --> before each page/slide
maxPagesPerDoc300Pages beyond this are skipped and not billed
maxFileSizeMb50Larger files are rejected without charge
docTimeoutSecs600Slow documents are stopped and reported (not billed)
maxConcurrency2Parallel documents; more helps only with more memory

Default memory is 1 GB. For long documents (100+ pages) or OCR-heavy batches you can raise it to 4 GB (1 full CPU core) for faster runs. Compute is included in the per-event price, so the memory setting does not change what you pay.

Example:

{
"sources": [
{ "url": "https://arxiv.org/pdf/1706.03762v7" },
{ "url": "https://example.com/contract.docx" }
],
"ocr": "auto",
"ocrLanguages": ["eng", "deu"],
"chunkingEnabled": true,
"chunkMaxTokens": 800
}

Output

The dataset has one document item per source. If chunking is on, it also has one chunk item per chunk. Use the Documents and Chunks views in the Output tab, or filter by recordType.

{
"recordType": "document",
"docId": "349f7d2cc5034659",
"sourceUrl": "https://example.com/report.pdf",
"status": "ok",
"fileType": "pdf",
"title": "Quarterly report",
"pages": 1,
"billedPageUnits": 1,
"ocrPages": 0,
"tables": 1,
"contentFormat": "markdown",
"content": "# Quarterly report\n\nThe quick brown fox ...\n\n| Name | Qty | Price |\n|---|---|---|\n| Item 1 | 3 | 1.50 PLN |",
"wordCount": 247,
"tokenEstimate": 368,
"chunkCount": 4,
"warnings": []
}
{
"recordType": "chunk",
"docId": "349f7d2cc5034659",
"chunkIndex": 1,
"chunkCount": 4,
"headingPath": ["Quarterly report", "Section A"],
"pageStart": 1,
"pageEnd": 1,
"tokens": 93,
"text": "## Section A\n\nThe quick brown fox ..."
}

A failed document looks like this, and is not charged:

{ "recordType": "document", "sourceUrl": "https://example.com/protected.pdf", "status": "error",
"error": { "code": "PASSWORD_PROTECTED", "message": "The PDF is password-protected. Remove the password and try again." } }

Error codes: EMPTY_FILE, CORRUPT_FILE, PASSWORD_PROTECTED, UNSUPPORTED_TYPE, FILE_TOO_LARGE, HTTP_<status>, DOWNLOAD_FAILED, NOT_FOUND, INVALID_SOURCE, TIMEOUT, OUT_OF_MEMORY, OCR_DISABLED, OCR_FAILED, SPENDING_LIMIT_REACHED, CONVERSION_FAILED.

Warnings (the document is still delivered): PAGE_LIMIT, SPENDING_LIMIT, NO_TEXT_PAGES, NO_TEXT, PAGE_ERROR.

If the content is larger than about 4 MB, the full text is saved to the run's key-value store. The item then gets contentTruncated: true and a contentUrl. A run summary is saved as OUTPUT in the key-value store.

Pricing (pay per event)

EventPriceWhen
Document converted$0.002Once per document that produced text
Page converted$0.0005Per PDF page with a text layer or per slide. For DOCX/HTML/XLSX/TXT and similar formats, per started 3,000 characters of output
Page converted with OCR$0.008Per scanned page or image where OCR found text. Charged instead of the page event, not on top of it

Examples: a 20-page text PDF costs $0.012. A 10-page scan costs $0.082. A short Word document costs $0.0025. Failed documents, empty pages and pages above your limits are free.

The run never charges more than the maximum cost per run that you set. When the budget runs low, the Actor converts only the pages it can still bill, adds a SPENDING_LIMIT warning and skips the remaining sources.

Limitations (please read)

  • Tables without ruling lines in PDFs (common in academic papers) usually come out as plain text, not as Markdown tables. Ruled tables, and all tables in DOCX/XLSX/HTML/PPTX, are converted.
  • Math formulas, charts and figures are not reconstructed. Formulas come out as approximate text, and images inside PDF/DOCX are not OCR'd. Only scanned pages and standalone images are.
  • OCR quality depends on the scan. Clean 200–300 dpi scans work well. Photos of documents, handwriting and very small print do not. Choose only the languages you need, because each extra language slows OCR down.
  • Legacy formats (.doc, .xls, .ppt, .rtf, .odt) and archives (.zip) are not supported.
  • HTML is converted as a whole page, including navigation and footer text. There is no main-content extraction yet.
  • Password-protected PDFs are rejected. Passwords are not accepted as input.
  • The Actor downloads what you give it. It does not crawl websites or log in anywhere.

Privacy

Documents are processed inside your own Actor run. The Actor keeps no copy outside your run's storages. The converted text sits in your run's dataset and key-value store, under your account's data retention settings. Only process documents you have the right to process.

Open-source components

pdfplumber and pdfminer.six (MIT), pypdfium2 (Apache-2.0 / BSD-3-Clause), mammoth (BSD-2-Clause), python-pptx, openpyxl, markdownify and Beautiful Soup (MIT), Tesseract OCR and pytesseract (Apache-2.0), tiktoken (MIT), Apify SDK (Apache-2.0). AGPL-licensed PDF libraries are not used.

Feedback

Found a document that converts badly? Open an issue in the Issues tab and include a public link to the file if you can. Real failing files are the fastest way to improve the converter.