Documents to Markdown for LLMs: PDF, Office, HTML, OCR
Pricing
Pay per usage
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
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
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
automode 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,pageStartandpageEnd. 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
| Field | Default | Notes |
|---|---|---|
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) |
outputFormat | markdown | markdown or text |
ocr | auto | auto (scanned pages only), always, never |
ocrLanguages | ["eng"] | Any of eng, pol, deu, fra, spa, ita, por, nld, ces, ukr |
chunkingEnabled | false | Also output chunk items |
chunkMaxTokens / chunkOverlapTokens | 800 / 80 | Counted with the cl100k_base tokenizer |
includeContentWithChunks | true | Keep full text on the document item as well |
includeTables | true | Convert tables to Markdown tables |
includePageMarkers | false | Insert <!-- page: N --> before each page/slide |
maxPagesPerDoc | 300 | Pages beyond this are skipped and not billed |
maxFileSizeMb | 50 | Larger files are rejected without charge |
docTimeoutSecs | 600 | Slow documents are stopped and reported (not billed) |
maxConcurrency | 2 | Parallel 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)
| Event | Price | When |
|---|---|---|
| Document converted | $0.002 | Once per document that produced text |
| Page converted | $0.0005 | Per 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.008 | Per 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.