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

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

Pricing

from $7.00 / 1,000 document converteds

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

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

Convert PDF, Word, PowerPoint and HTML into clean Markdown with optional heading-aware RAG chunks and page numbers. No OCR, no AI costs.

Pricing

from $7.00 / 1,000 document converteds

Rating

0.0

(0)

Developer

James Brown

James Brown

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

PDF to Markdown for RAG (also DOCX, PPTX, HTML)

This PDF to Markdown converter turns PDF, Word (DOCX), PowerPoint (PPTX) and HTML into clean, LLM-ready Markdown, with optional heading-aware RAG chunks (text, heading_path, page) and page numbers. One actor for your whole document pile, fast and cheap: no OCR, no AI model calls, no GPU. A 100-page PDF converts in about 3 seconds using under 100 MB of RAM.

Use it to prepare documents for vector databases, ChatGPT/Claude context windows, search indexes, knowledge bases and document pipelines, or to monitor documents for changes.

What the PDF to Markdown converter gives you

  • Multi-format: PDF, DOCX, PPTX and HTML in the same run (format is detected from file content, not the extension).
  • Structure preserved: headings (#..), lists, tables (DOCX, PPTX, HTML), bold/italic (DOCX, HTML), slide notes (PPTX). PDF headings are inferred from font size/weight.
  • RAG chunks: [{index, text, heading_path, page}]. Chunks never cross a heading boundary, so heading_path (e.g. ["3. Outlook", "3.1 Risks"]) is exact. Configurable size and overlap.
  • Page numbers: <!-- page: N --> markers and a page on every chunk (PDF pages, PPTX slides).
  • Scanned pages flagged, not OCR'd: image-only PDF pages and empty slides are listed in scannedPages and not charged.
  • Clean PDFs: repeated running headers, footers and page numbers are removed; line-wrap hyphenation is repaired.
  • Monitoring mode: re-run on a schedule; unchanged documents are skipped and free, new/changed ones are converted (changeStatus: new | changed | unchanged).
  • Polite and safe: honours robots.txt, one request per host per second by default, refuses private/internal addresses (every redirect hop checked), 50 MB size cap. robots.txt is matched per RFC 9309 (wildcards and $ supported) and fails closed: if robots.txt returns a server error, 401/403 or cannot be reached, the host is skipped.
  • Credentials in URLs (user:pass@host) are not sent: the actor never makes authenticated requests, so use a URL that needs no login.

Input

FieldDefaultMeaning
urlsRFC 9111 samplePublic http(s) links to PDF/DOCX/PPTX/HTML
keyValueStoreKeys–Keys of files you uploaded to the run's default key-value store
enableChunkingtrueOutput RAG chunks
chunkSize / chunkOverlap1000 / 100Characters (about 4 per token)
includePageNumberstruePage markers + chunk page
maxPagesPerDocument500Only the first N PDF pages are processed and billed
htmlMainContentOnlytrueDrop nav/header/footer for HTML
monitoringModefalseSkip unchanged documents
maxDocuments100Safety cap per run
requestDelaySeconds1Pause between requests to one host (minimum 1 s; robots.txt is always honoured, there is no switch to turn it off)

No input? The actor converts the first 5 pages of a public sample PDF (IETF RFC 9111) so you can see real output immediately (about $0.025).

Uploading files: put them in the run's default key-value store (Console → Storage → Key-value stores, or the API) and list their keys in keyValueStoreKeys.

Example input:

{
"urls": ["https://www.rfc-editor.org/rfc/rfc9111.pdf", "https://example.com/report.docx"],
"keyValueStoreKeys": ["board-deck.pptx"],
"enableChunking": true,
"chunkSize": 800,
"includePageNumbers": true
}

Example output: Markdown and RAG chunks (one dataset item per document)

{
"source": "https://www.rfc-editor.org/rfc/rfc9111.pdf",
"sourceType": "url",
"status": "ok",
"format": "pdf",
"title": "RFC 9111: HTTP Caching",
"pageCount": 35,
"pageCountEstimated": false,
"billablePages": 5,
"scannedPages": [],
"markdown": "<!-- page: 1 -->\n\n# RFC 9111 HTTP Caching\n\n## Abstract\n\nThe Hypertext Transfer Protocol (HTTP) is a stateless application-level protocol ...",
"chunks": [{"index": 0, "text": "The Hypertext Transfer Protocol (HTTP) ...", "heading_path": ["RFC 9111 HTTP Caching", "Abstract"], "page": 1}],
"chunkCount": 10,
"characters": 7921,
"changeStatus": null,
"contentSha256": "2663227a...",
"warnings": ["Only the first 5 of 35 pages were processed (maxPagesPerDocument)."],
"error": null,
"processedAt": "2026-10-21T18:41:07+00:00"
}

Failed documents are still returned (status: "failed", error explains why: robots.txt block, HTTP error, password-protected, unsupported type...) and are never charged.

Pricing (pay per event)

EventPriceWhen
Actor start$0.005Once per run
Document converted$0.01Per successfully converted document
Page converted$0.002Per page/slide successfully converted to text
ExampleApproximate cost
One 10-page PDF$0.035
One 80-page PDF$0.175
100 documents of 10 pages$3.005

Not charged: failed or blocked documents, unsupported files, fully scanned documents, scanned/empty pages, and unchanged documents in monitoring mode. Example: one 80-page PDF = $0.005 + $0.01 + $0.16 = $0.175. DOCX files have no verifiable per-page text map: pageCount is Word's saved count or an estimate, but only pages the extracted text accounts for (~500 words each, never more than pageCount) are billed (billablePages). Image-only, blank or unsupported DOCX content is flagged in warnings, not billed. HTML counts as one page. Output is saved to the dataset before charging, so you are never billed for output that was not stored. Set a maximum cost per run in Apify to cap spend; the actor stops cleanly when it is reached.

How it compares (checked 21 Oct 2026; verify on the Store pages)

  • gochujang/pdf-text-extractor: PDF only; offers rag_chunks, optional Tesseract OCR, table detection, password-protected PDFs, share-link resolution; $0.02/PDF + $0.001/page. Pick it if you need OCR, passwords or per-page pricing on PDF-heavy workloads.
  • clearpath/pdf-to-markdown-api: PDF only; AI layout detection (multi-column, tables, formulas), OCR, image extraction; no chunks; $0.01/PDF + $0.005/page. Pick it if you need the highest-fidelity layout/OCR on complex scientific or scanned PDFs.
  • This actor: one actor for PDF + DOCX + PPTX + HTML, heading-path chunks with page numbers, new/changed monitoring, tiny footprint and speed (no models), cheaper than clearpath per page. It does not do OCR, password-protected PDFs or complex PDF table reconstruction.

Limitations

  • No OCR. Scanned/image-only pages are flagged (scannedPages) and free; their text is not extracted.
  • PDF headings, lists and paragraphs are inferred heuristically from fonts and spacing. Multi-column layouts may interleave, and PDF tables are returned as text, not Markdown tables. A table of contents can come out as one long paragraph.
  • Password-protected/encrypted PDFs are reported as failed. Legacy .doc, .ppt are unsupported. Images, charts and equations are skipped.
  • DOCX/PPTX: headers, footers, text boxes and footnotes in DOCX are not extracted; page numbers do not exist for DOCX/HTML (page: null).
  • URLs must be publicly reachable without login (no login or paywall bypass). Hosts that disallow automated access in robots.txt are skipped.
  • Max 50 MB per file, 2000 pages per PDF. Very short PDF pages (e.g. a title page) are kept and listed in sparsePages; a short-text page that also contains an image is treated as likely scanned (text kept, flagged in scannedPages, not charged).
  • Restarts and migrations. The run keeps a small progress record (RUN_PROGRESS: source label, delivery/billing stage and charge counts, no document content) in its default key-value store, under standard Apify retention. After a restart or migration, sources already delivered are skipped and nothing is saved or charged twice. If a restart interrupts a charge call whose outcome is unknown, that charge is not repeated, so you are never billed twice for it.
  • No third-party data source. The actor only processes files and URLs you supply, so there is no source-site ToS to review; you are responsible for having the right to process the documents. Robots.txt is always honoured for URLs (it cannot be switched off). Do not use it to process personal data without a lawful basis (UK/EU GDPR); what is stored: the dataset of your run holds the converted Markdown/chunks plus the source URL/key (kept per your Apify plan's dataset retention, deletable by you at any time); downloaded files are written to a temporary file in the run container and deleted right after parsing; uploaded files stay in your own key-value store until you delete them; in monitoring mode a separate named key-value store keeps, per source, a SHA-256 hash, the source name and a timestamp (persists until you delete it). No data is sent to any third party.
  • Dependency licence check (permissive only, no copyleft; verified from installed package metadata of the full resolved tree, 21 Oct 2026). HTTP is done with the Python standard library, so httpx and its MPL-2.0 certifi dependency are not installed; TLS uses the container's system CA store. Other transitive packages (Apify SDK/crawlee, impit, pydantic, cryptography, ...) are MIT, BSD, Apache-2.0, ISC, Unlicense or MIT-0:
PackageLicence
pypdfium2 (PDFium)BSD-3-Clause / Apache-2.0 (not PyMuPDF, which is AGPL)
python-docx, python-pptxMIT
beautifulsoup4, markdownifyMIT
lxmlBSD-3-Clause
protego (robots.txt)BSD-3-Clause
apify SDKApache-2.0
  • Sample PDF: IETF RFC 9111 © IETF Trust, redistribution permitted under the IETF Trust Legal Provisions; rfc-editor.org robots.txt allows the path.

FAQ

How do I convert a PDF to Markdown for RAG? Add the PDF link to urls (or upload the file and list its key in keyValueStoreKeys). Each document returns a markdown field and, with enableChunking, a list of chunks with heading_path and page, ready for a vector database.

Does it convert DOCX, PPTX and HTML as well? Yes. PDF, DOCX, PPTX and HTML can be mixed in one run; the format is detected from the file content.

Does it do OCR on scanned PDFs? No. Scanned pages are listed in scannedPages and are not charged.

Am I charged for failed documents? No. Failed, blocked, fully scanned and unchanged documents are free.

Run locally

pip install -r requirements.txt
python tests/make_fixtures.py ./fixtures # needs requirements-dev.txt; makes DOCX, PPTX, 100-page PDF, scanned PDF
APIFY_PURGE_ON_START=0 python tests/seed_local_kvs.py ./fixtures # "upload" them to the local key-value store
# put input in storage/key_value_stores/default/INPUT.json, then:
python -m src # or: apify run
pytest tests -q

Publishing notes (pay-per-event)

.actor/pay_per_event.json documents the three events and prices; Apify reads pricing from Console, not from this file, so set it in Console → Monetization → Pay per event (a manual, one-time step that is not verified by this repo):

EventPriceWho charges it
apify-actor-start$0.005Apify, automatically, once per run. Runs up to 1 GB are charged once; each extra GB adds another event. maxMemoryMbytes is therefore capped at 1024 so a run costs exactly one $0.005 start event. Never charge it in code (the call fails). Do not also create a custom actor-start event.
document$0.01Actor.charge, after the result is saved
page$0.002Actor.charge, after the result is saved, for verified text pages only

Do not define apify-default-dataset-item (remove it in Console if it is pre-filled): it would bill every dataset record, including failed and unchanged ones, which must stay free. Memory: the 1 GB cap is ample (measured ≈ 95 MB RSS for a 194-page PDF; files are streamed to disk and capped at 50 MB).