PDF to Markdown & Text Extractor (with OCR)
Pricing
Pay per usage
PDF to Markdown & Text Extractor (with OCR)
Convert PDFs from URLs into clean Markdown and plain text, page by page, with optional OCR for scanned pages. Output goes to the dataset and a Markdown file per PDF.
Pricing
Pay per usage
Rating
0.0
(0)
Developer
Cole Cassel
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
17 days ago
Last modified
Categories
Features
Share
Convert PDFs from any public URL into clean Markdown or plain text, ready for LLMs, RAG pipelines, search indexes or plain reading. It keeps the structure:
- headings, detected from font size;
- bullet lists;
- real tables, as Markdown tables;
- page markers.
Scanned pages with no text layer are automatically run through Tesseract OCR.
You pay $0.0008 per text page and $0.004 per OCR page. Empty pages are free.
What it does
- Downloads each PDF, up to 50 MB by default. It checks that the file really is a PDF, follows redirects, retries on timeouts and on 429/5xx, and is polite (
PdfToMarkdownBot/1.0, robots.txt respected). - Reads every page with PyMuPDF:
- Markdown: larger fonts become
#/##/###headings, short all-bold blocks become####headings, bullets become list items, and hyphenated line breaks are joined. Detected tables become Markdown tables, with junk "tables" from form layouts filtered out. - Text: clean plain text in reading order.
- Markdown: larger fonts become
- Decides per page whether OCR is needed (
ocrMode: auto): if a page has fewer thanminTextCharscharacters of real text, it is rendered atocrDpiand OCR'd. You can forcealwaysornever. - Returns one dataset row per document, or per page, with metadata and per-page statistics. The full Markdown/text file is also saved in the key-value store, so huge documents are never cut off.
Use cases
- Feeding reports, papers and manuals into ChatGPT, Claude or a RAG/vector database.
- Making scanned public archives (government reports, old manuals) searchable.
- Bulk-converting public research papers, standards and policy documents to Markdown for notes or a wiki.
- Monitoring public PDF publications by extracting text for diffing or keyword alerts.
Input
{"startUrls": [{ "url": "https://arxiv.org/pdf/1706.03762" },{ "url": "https://nvlpubs.nist.gov/nistpubs/CSWP/NIST.CSWP.29.pdf" },{ "url": "https://ntrs.nasa.gov/api/citations/19930091108/downloads/19930091108.pdf" }],"outputFormat": "markdown","ocrMode": "auto"}
| Field | Default | Notes |
|---|---|---|
outputFormat | markdown | markdown, text or both |
outputMode | document | document gives one row per PDF; page gives one row per page |
ocrMode | auto | auto OCRs only pages without text; always or never |
ocrLanguage / ocrDpi | eng / 200 | The prebuilt image includes English |
minTextChars | 25 | Below this, a page counts as scanned in auto mode |
pageRange | all | For example 1-3, 7, 10- |
maxPages / maxFileMb | 500 / 50 | |
respectRobotsTxt | true | |
maxConcurrency / minDelayBetweenRequestsSecs | 2 / 1 s | |
requestTimeoutSecs / maxRetries | 120 s / 2 |
Output
A real document row from a test run on 2026-10-18, with the Markdown and the page list shortened here:
{"url": "https://arxiv.org/pdf/1706.03762","ok": true,"fileName": "1706.03762","creator": "LaTeX with hyperref","producer": "pdfTeX-1.40.25","pageCount": 15,"pagesProcessed": 15,"textPages": 15,"ocrPages": 0,"emptyPages": 0,"pages": [{ "pageNumber": 1, "method": "text", "chars": 2857 },{ "pageNumber": 2, "method": "text", "chars": 4255 }],"charCount": 39757,"wordCount": 6155,"markdown": "<!-- page 1 -->\nProvided proper attribution is provided, Google hereby grants permission … \n\n## Attention Is All You Need\n\nAshish Vaswani∗\n\nGoogle Brain …\n\n### Abstract\n\nThe dominant sequence transduction models are based on …","contentTruncated": false,"markdownUrl": "https://api.apify.com/v2/key-value-stores/<store-id>/records/1706-dd93e904.md","processingTimeMs": 1795,"attempts": 1}
A real OCR page row from a 1930s NACA scanned report (outputMode: page, ocrMode: always), shortened:
{"url": "https://ntrs.nasa.gov/api/citations/19930091108/downloads/19930091108.pdf","ok": true,"pageNumber": 4,"pageCount": 61,"method": "ocr","chars": 2215,"text": "NATIONAL ADVISORY COMMITTEE FOR AERONAUTICS\nNAVY BUILDING, WASHINGTON, D.C.\n\n(An independent Government establishment, created by act of Congress approved March 3, 1915, …"}
Failures become free rows with ok: false and an error.code:
NOT_A_PDF,HTTP_404and otherHTTP_<status>codes;TOO_LARGE,ENCRYPTED_PDF,BROKEN_PDF;ROBOTS_DISALLOWED,TIMEOUT,NETWORK_ERROR,DNS_ERROR,PRIVATE_ADDRESS,INVALID_URL.
Test run results:
- Main run: 4 public PDFs (arXiv, IRS W-9, NIST, NASA), 114 pages, in 18 seconds. 112 pages came from the text layer, 1 needed OCR and 1 was empty and free. Two bad URLs were recorded as
NOT_A_PDFandHTTP_404. - OCR run: 10 pages of the scanned NASA report in 27 seconds.
Pricing
Pay per event:
| Event | Price |
|---|---|
| Page extracted from text layer | $0.0008 ($0.80 per 1,000) |
| Page extracted with OCR | $0.004 ($4 per 1,000) |
| Run start (Apify standard) | $0.00005 per GB of memory |
Empty pages, failed files and pages outside pageRange are free. Use pageRange, maxPages or a maximum cost per run to cap spending.
Limits
- OCR quality depends on the scan. Expect typical OCR mistakes on old or low-quality scans, especially in names and numbers. Only English (
eng) is installed in the image. - OCR is CPU-heavy. For scan-heavy jobs, give the run 2–4 GB of memory, which on Apify also means more CPU, so it finishes faster.
- Multi-column layouts can sometimes interleave columns. Tables without ruling lines may come out as text rather than a Markdown table.
- Password-protected PDFs aren't supported (
ENCRYPTED_PDF). - Public URLs only: no logins or cookies.
FAQ
Will it cut off long documents?
The full output is always saved as a .md/.txt file in the key-value store (markdownUrl/textUrl). Only the copy inside the dataset row is capped at 2 million characters, and contentTruncated tells you when that happened.
How do I know which pages were OCR'd?
Each entry in pages has method: text, ocr or empty. ocrPages gives the total.
Can I process only the first pages?
Yes, for example "pageRange": "1-5". You are only charged for the pages processed.
Does it handle forms such as tax forms? Yes. Text is extracted, and layout boxes that aren't real tables are filtered out instead of producing junk tables.