PDF to Markdown Converter: Word, Excel, PPT & OCR for LLMs avatar

PDF to Markdown Converter: Word, Excel, PPT & OCR for LLMs

Pricing

Pay per event

Go to Apify Store
PDF to Markdown Converter: Word, Excel, PPT & OCR for LLMs

PDF to Markdown Converter: Word, Excel, PPT & OCR for LLMs

Convert PDF, Word, Excel, PowerPoint, HTML and scans to clean Markdown or JSON for LLMs, RAG pipelines and AI agents. Keeps headings and tables, runs OCR only on scanned pages, returns a page map for citations. $0.002 per page, failed files free.

Pricing

Pay per event

Rating

0.0

(0)

Developer

tinplatelabs

tinplatelabs

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

9 hours ago

Last modified

Share

PDF to Markdown Converter: Word, Excel, PowerPoint and OCR for LLMs

Turn PDF, Word (DOCX), Excel (XLSX), PowerPoint (PPTX), HTML, CSV and scanned images into clean Markdown or JSON that an LLM can read straight away, with headings and tables intact. $0.002 per page.

For RAG pipelines, AI agents (Claude, ChatGPT, Cursor and others through the Apify MCP server), and anyone who needs document text with its structure: one Actor for every format, priced per page.

Use cases

  • RAG and vector search: convert a document library to Markdown, one dataset item per page, ready to embed.
  • AI agents reading files: an agent passes a PDF or Office URL and reads back Markdown it can quote by page.
  • Tables to data: Excel sheets, Word tables and ruled PDF tables come out as Markdown tables or row arrays.
  • Scanned documents: pages without a text layer are OCRed automatically (26 languages), only those pay the OCR price.
  • Web pages to Markdown: main content only, without menus, sidebars, cookie banners or footers.

Quick start

This input (the Actor's example) converts a PDF, a spreadsheet and a slide deck in about 10 seconds for about $0.016:

{
"sources": [
"https://arxiv.org/pdf/1706.03762",
"https://raw.githubusercontent.com/microsoft/markitdown/main/packages/markitdown/tests/test_files/test.xlsx",
"https://raw.githubusercontent.com/microsoft/markitdown/main/packages/markitdown/tests/test_files/test.pptx"
],
"maxPages": 3
}

One dataset item per document (abridged):

{
"source": "https://arxiv.org/pdf/1706.03762",
"status": "ok",
"format": "pdf",
"pageCount": 15,
"pagesConverted": 3,
"ocrPages": 0,
"truncated": true,
"warnings": ["The PDF has 15 pages; only the first 3 were converted (maxPages = 3). Raise maxPages to convert more."],
"markdown": "<!-- page 1 -->\n\n...\n\n## Attention Is All You Need\n\n...",
"pages": [{"page": 1, "ocr": false, "chars": 2828, "startOffset": 17, "endOffset": 2845}, "..."]
}

The spreadsheet comes back as ## Sheet1 followed by a Markdown table, one section per sheet.

Pricing in plain words

You pay $0.002 per page read from a text layer (a PDF page, a slide, or about 3,000 characters of a Word, Excel, HTML or CSV file) and $0.006 per page that needs OCR. A 20-page digital PDF costs $0.04. Files that fail cost nothing, and maxPages caps the cost of any single document. Full table under Pricing.

Why this one

  • Every common format in one call. Mix PDFs, Office files, web pages and scans in the same run. The format is detected from the file's content, not its name.
  • Tables stay tables. Ruled tables in PDFs, Word tables, Excel sheets and HTML tables come out as Markdown tables (or as row arrays in JSON), and table text isn't repeated as loose paragraphs.
  • Headings and lists kept. Larger fonts in PDFs become #/##/### headings, bullets become lists.
  • OCR only where it's needed. With ocr: auto, only pages with no text layer (scans) go through Tesseract OCR, and only those pay the OCR price.
  • Agent-shaped output. Markdown with <!-- page N --> markers plus a page map of character offsets, document metadata, warnings and a truncated flag. Optionally one dataset item per page, ready for embedding.
  • Reliable and predictable. Every failure is an error item that says what to change, and the rest of the run carries on. Failed files cost nothing, and the run's spending limit is respected.
  • Private and deterministic. No LLMs, no third-party APIs, no telemetry. Your files never leave the run, and the same file always gives the same output.

Input

FieldTypeDefaultWhat it does
sourcesarray of stringsrequiredPublic http(s) URLs, or key-value store records as kvs://KEY (the store in keyValueStoreId, else this run's store) or kvs://STORE_ID_OR_NAME/KEY. Signed Apify record URLs (?signature=) work too.
keyValueStoreIdstringYour key-value store holding the files (pick it in Console, or pass its ID). Required to read your own store: the Actor runs with limited permissions and can read only this store.
outputFormatmarkdown | jsonmarkdownMarkdown string + page map, or typed JSON blocks per page.
ocrauto | always | neverautoauto OCRs only scanned pages and images.
ocrLanguagesarray of strings["eng"]Tesseract codes: eng, deu, fra, spa, ita, por, nld, pol, ces, swe, dan, nor, fin, tur, ron, hun, rus, ukr, ell, ara, heb, hin, chi_sim, chi_tra, jpn, kor.
chunkByPagebooleanfalseOne dataset item per page instead of per document.
maxPagesinteger 1–500500Pages converted (and billed) per document at most.
{
"sources": [
"https://example.com/annual-report.pdf",
"https://example.com/price-list.xlsx",
"kvs://scanned-contract.pdf"
],
"outputFormat": "markdown",
"ocr": "auto",
"ocrLanguages": ["eng"],
"chunkByPage": false,
"maxPages": 100
}

To convert a file that isn't online, upload it to a key-value store (Apify Console → Storage, or the API), pick that store as keyValueStoreId and pass kvs://KEY. Apify lets this Actor read only the store you pick there (it runs with limited permissions); any other store gives an ACCESS_DENIED item. A signed record URL (get_record_public_url() in the Apify client) also works without picking the store.

Output

One dataset item per document (or per page with chunkByPage).

{
"source": "https://example.com/annual-report.pdf",
"status": "ok",
"fileName": "annual-report.pdf",
"contentType": "application/pdf",
"sizeBytes": 48211,
"format": "pdf",
"pageUnit": "page",
"pageCount": 2,
"pagesConverted": 2,
"ocrPages": 0,
"truncated": false,
"metadata": {"title": "Quarterly Report", "author": "Finance", "created": "2026-09-30T10:12:00"},
"warnings": [],
"markdown": "<!-- page 1 -->\n\n# Quarterly Report\n\nSales grew in every region.\n\n## Revenue by region\n\n| Region | Revenue | Growth |\n|---|---|---|\n| North | 1200 | 4% |\n\n<!-- page 2 -->\n\n...",
"pages": [
{"page": 1, "ocr": false, "chars": 151, "startOffset": 16, "endOffset": 167},
{"page": 2, "ocr": false, "chars": 47, "startOffset": 185, "endOffset": 232}
]
}

markdown[startOffset:endOffset] is exactly that page's text, so you can cite or chunk by page. Slides and sheets also carry a label (slide title or sheet name).

With "outputFormat": "json", markdown is replaced by blocks on each page:

"pages": [{"page": 1, "ocr": false, "chars": 151, "blocks": [
{"type": "heading", "level": 1, "text": "Quarterly Report"},
{"type": "paragraph", "text": "Sales grew in every region."},
{"type": "table", "rows": [["Region", "Revenue", "Growth"], ["North", "1200", "4%"]]}
]}]

A file that can't be converted produces an error item, and the rest of the run carries on:

{"source": "https://example.com/old.doc", "status": "error", "fileName": "old.doc",
"error": {"code": "LEGACY_OFFICE_FORMAT", "message": "This is an old binary Office file (.doc, .xls or .ppt) or a password-protected Office file. Save it as .docx, .xlsx or .pptx without a password and submit it again."}}

Error codes: INVALID_SOURCE, DOWNLOAD_FAILED, ACCESS_DENIED, FILE_TOO_LARGE, EMPTY_FILE, UNSUPPORTED_FORMAT, LEGACY_OFFICE_FORMAT, ENCRYPTED, CORRUPT_FILE, OCR_DISABLED, TIMEOUT, SPENDING_LIMIT_REACHED, INTERNAL_ERROR. Every message says what to do next.

How each format is handled

FormatPage unitNotes
PDFpageText layer with headings, lists and ruled tables. Scanned pages are OCRed (ocr: auto). Password-protected PDFs are rejected.
PPTXslideTitles, text, tables, speaker notes.
DOCXsectionHeadings, lists, tables, links. Images become their alt text.
XLSXsectionOne ## Sheet table per sheet; cached formula values; dates as ISO 8601.
HTMLsectionMain content only: menus, language lists, sidebars, cookie banners and footers are dropped (whole page if that leaves almost nothing); scripts and styles dropped; links made absolute; <title> and lang as metadata.
CSV / TXT / MDsectionCSV becomes a table; text passes through.
PNG, JPEG, TIFF, BMP, GIF, WebPimage (frame)OCR. Multi-page TIFFs give one page per frame.

A section is up to about 3,000 characters of Markdown (roughly one printed page), split at paragraph boundaries. A long table is split between rows and every piece repeats the header row.

Pricing (pay per event)

EventPriceWhen
page-converted$0.002Each PDF page read from its text layer, each slide, each section of DOCX/XLSX/HTML/CSV/TXT
ocr-page$0.006Each scanned page or image frame read with OCR (instead of page-converted)
Actor startset by ApifyPer run

Examples: a 20-page digital PDF costs $0.04. A 10-page scanned contract costs $0.06. A 3-sheet small workbook costs $0.006. Failed documents, and pages whose OCR failed, are never charged. Pages over maxPages are not converted or charged. If the run's maximum total charge is reached, the Actor stops and marks the remaining sources SPENDING_LIMIT_REACHED.

Limits

  • 50 MB per file, 1,000 sources per run.
  • Up to 500 pages, slides or sections per document (maxPages).
  • Downloads time out after 60 s and follow up to 5 redirects. Only public addresses are fetched.
  • Each document gets 15 minutes. If a PDF or image is still running at that point, the pages already done are returned with truncated: true.
  • Not supported yet: legacy .doc/.xls/.ppt, OpenDocument, EPUB, e-mail files; OCR of images embedded in Office files; tables without ruling lines in PDFs; multi-column reading order in PDFs.

Privacy

Your files are downloaded into the run, converted inside it, and written only to that run's dataset (and its key-value store, for results too large for a dataset item). Nothing is sent to any third party, no LLM sees your content, and nothing is kept outside your run's storage, which follows your Apify data-retention settings. The Actor fetches only the exact URLs you give it and never follows links found inside documents.

Use from an AI agent (MCP)

Add the Apify MCP server to your agent and enable this Actor. The agent can then call it with {"sources": ["<url>"]} and read the markdown field of the first dataset item.

Open-source licenses

All dependencies are permissively licensed. No AGPL (PyMuPDF) or GPL (marker) code is used. CI fails on any copyleft dependency (tools/check_licenses.py).

ComponentLicenseRole
markitdown 0.1.8MITDOCX, PPTX, HTML conversion
mammoth 1.11.0, cobble 0.1.4BSD-2-ClauseDOCX (via markitdown)
python-pptx 1.0.2MITPPTX (via markitdown)
XlsxWriter 3.2.9BSD-2-Clausepython-pptx dependency
beautifulsoup4 4.15.0, soupsieve 3.0.1, markdownify 1.2.3MITHTML
magika 0.6.3, onnxruntime 1.31.0, flatbuffers, protobuf, numpyApache-2.0 / MIT / BSD-3-Clausemarkitdown dependencies
pdfplumber 0.11.10, pdfminer.six 20260107MITPDF text, headings, tables
pypdfium2 5.14.0 (PDFium)Apache-2.0 / BSD-3-ClausePDF opening, metadata, rendering for OCR
openpyxl 3.1.5, et_xmlfile 2.0.0MITXLSX
pytesseract 0.3.13Apache-2.0OCR wrapper
Tesseract OCR 5 + language data, Leptonica (Debian packages)Apache-2.0 / BSD-2-ClauseOCR engine
Pillow 12.3.0MIT-CMUImages
httpx 0.28.1, httpcore, h11, anyio, idnaBSD-3-Clause / MITDownloads
defusedxml 0.7.1PSF-2.0Safe XML parsing
lxml 6.1.3BSD-3-ClauseXML
apify 4.1.0, apify-client, crawlee and their dependencies (pydantic, cryptography, websockets, ...)Apache-2.0 / MIT / BSDApify SDK
certifiMPL-2.0 (used unmodified)CA certificates
charset-normalizer, requests, urllib3, typing-extensionsMIT / Apache-2.0 / PSFUtilities

Test-only: pytest (MIT), reportlab (BSD), python-docx (MIT).


Made by Tinplate Labs. This Actor is maintained by an AI; there is no support desk, so every error message is written to tell you exactly what to change.