PDF & Image to Text / Markdown (with OCR)
Pricing
from $10.00 / 1,000 page read with ocrs
PDF & Image to Text / Markdown (with OCR)
Convert PDFs and images to clean Markdown and text for RAG, AI agents and automations. Keeps headings, lists, tables and page breaks; OCRs scanned pages and images automatically (28 languages). Pay only for pages that produce text.
Pricing
from $10.00 / 1,000 page read with ocrs
Rating
0.0
(0)
Developer
Nick Na
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
Turn PDFs and images into clean Markdown and plain text, ready for RAG pipelines, AI agents and no-code automations. The Actor keeps the document's structure (headings, bullet and numbered lists, tables, reading order across columns, page breaks) and reads scanned pages and images with OCR automatically, in 28 languages. One dataset row per file, plus downloadable .md, .txt and .json files. You only pay for pages that produced text. Blank pages, broken links, password-protected or corrupt files cost nothing.
Built for RAG and LLM ingestion (LangChain, LlamaIndex, vector databases), AI agents through Apify's MCP server, and n8n / Make / Zapier flows.
What it does
- PDF text layer first. Born-digital PDFs are read directly from their text layer (fast and exact). OCR runs only on pages that need it: scans, photos of documents, pages whose fonts have no readable text. You can force OCR on every page or turn it off.
- Images too. PNG, JPEG, TIFF (including multi-page faxes and scans), WebP, BMP and GIF are OCR'd page by page.
- Real Markdown, not a text dump. Headings (
#,##,###) from font sizes and bold lines,-and1.lists from bullets and numbering, Markdown tables from ruled tables and clearly aligned columns, end-of-line hyphenation undone, running headers, footers and page numbers removed. - Multi-column layouts (reports, newsletters, fact sheets) are read column by column instead of across the page.
- Rotated scans are detected and turned upright before OCR.
- Page markers (
<!-- page 3 -->) in the Markdown and a form feed between pages in the text, so you can chunk and cite by page. Optional one row per page in a second dataset. - Metadata per file: page count, title, author, PDF dates and producer, whether it has a text layer, how many pages were OCR'd, OCR confidence, detected language, word count.
- Robust bulk processing. Downloads are retried with backoff, file types are detected from the bytes (not the URL), each document runs in its own process with a time limit, and one bad file never stops the run. Every failure gets a clear
errorCode. - Pay only for results, never more than you allow. Charges happen after the files are saved. If you set a maximum cost per run, the Actor stops cleanly before going over it.
Use cases
- RAG and chatbots: convert manuals, reports, contracts and policies into Markdown that chunks well (headings and tables intact) before embedding.
- AI agents (Apify MCP): let an agent read any PDF or screenshot from a URL and get back Markdown it can reason over.
- Document automation in n8n / Make / Zapier: "new invoice in Drive → extract text → send to an LLM or a spreadsheet".
- Digitizing scans: archives, faxes, receipts, old reports and typewritten pages become searchable text.
- Data extraction from statistical PDFs: government and financial reports with tables come out as Markdown tables.
How to use
- Paste one or more PDF or image URLs (Google Drive, Dropbox and GitHub share links are converted automatically). Files already in an Apify key-value store work too.
- Leave OCR mode on Auto and pick the OCR languages of your scans (English by default).
- Click Start. Each file gets a row in the Output tab with its title, page counts, language and links to the Markdown, text and JSON files.
Input example
{"fileUrls": ["https://pubs.usgs.gov/fs/2013/3082/pdf/fs2013-3082.pdf","https://images-assets.nasa.gov/image/S69-39334/S69-39334~medium.jpg"],"ocrMode": "auto","ocrLanguages": ["eng"],"perPageOutput": true}
| Field | Default | What it does |
|---|---|---|
fileUrls | none | Public http(s) links to PDFs or images. Objects like {"url": "..."} are accepted too. |
ocrMode | auto | auto (OCR only where needed), always (OCR every page, ignore the text layer), never (text layer only) |
ocrLanguages | ["eng"] | Up to 4 of: eng, deu, fra, spa, ita, por, nld, pol, ces, slk, swe, dan, nor, fin, ron, hun, tur, ell, rus, ukr, ara, hin, vie, ind, chi_sim, chi_tra, jpn, kor. ISO codes (de) and names (German) work too. |
perPageOutput | false | Also write one row per page to the pages dataset |
keyValueStoreId / keyValueStoreKeys | none | Process PDFs/images stored in an Apify key-value store |
detectTables | true | Turn tables into Markdown tables |
removeHeadersFooters | true | Drop running headers, footers and page numbers |
pageSeparator | comment | comment (<!-- page N -->), rule (---) or none |
maxCharsInDataset | 200000 | Markdown and text are also included inline in the dataset row up to this length (files always have everything) |
maxPagesPerDocument | 300 | Only the first N pages of each file are processed and billed (max 5,000) |
maxFileSizeMb | 50 | Larger files are skipped with FILE_TOO_LARGE (max 300) |
documentTimeoutSecs | 600 | Per-file time limit; finished pages are still returned (status partial) |
ocrDpi | 300 | Rendering resolution for OCR (150-400) |
pdfPassword | none | Password for protected PDFs (stored encrypted) |
downloadTimeoutSecs | 30 | Connect/read timeout per download attempt (3 retries) |
Output example
One dataset row per file (Markdown shortened here):
{"index": 0,"source": "https://pubs.usgs.gov/fs/2013/3082/pdf/fs2013-3082.pdf","status": "succeeded","fileType": "pdf","title": "Natural Hazards Science at the U.S. Geological Survey","titleSource": "heading","language": "en","pageCount": 2,"pagesProcessed": 2,"textLayerPages": 2,"ocrPages": 0,"emptyPages": 0,"hasTextLayer": true,"ocrUsed": false,"wordCount": 1072,"tableCount": 0,"headingCount": 6,"markdown": "<!-- page 1 -->\n\n# Natural Hazards Science at the U.S. Geological Survey\n\nA sustainable society needs robust assessments of hazard and risk ...\n\n## USGS and Natural Hazards\n\n...","text": "Natural Hazards Science at the U.S. Geological Survey\n\nA sustainable society ...","markdownUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/markdown-0000-fs2013-3082.md","textUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/text-0000-fs2013-3082.txt","jsonUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/json-0000-fs2013-3082.json","metadata": { "producer": "Adobe PDF Library 10.0.1", "creationDate": "2013-09-12T14:28:01-05:00", "pdfVersion": "1.6" },"warnings": [],"processingMs": 315,"chargedEvents": { "page-extracted": 2 },"errorCode": null}
A statistics table from a USGS commodity report comes out like this:
| Salient Statistics—United States: | 2020 | 2021 | 2022 | 2023 | 2024e || --- | --- | --- | --- | --- | --- || Production: | | | | | || Mine, recoverable | 1,200 | 1,230 | 1,230 | 1,130 | 1,100 || Primary (from ore) | 872 | 922 | 930 | 843 | 850 |
Files (in the run's key-value store, linked from each row): markdown-<index>-<name>.md, text-<index>-<name>.txt and json-<index>-<name>.json (metadata plus per-page Markdown, text and table rows). The OUTPUT record holds a run summary.
Per-page rows (with perPageOutput): docIndex, pageNumber, method (text, ocr, empty), reason (e.g. text-layer, scanned-page, blank-page), markdown, text, ocrConfidence, billedAs.
Statuses: succeeded, partial (some pages missing: timeout, max cost reached or failed pages; see warnings), failed, skipped (run stopped). Failed and skipped files are never charged:
{ "index": 4, "source": "https://example.com/contract.pdf", "status": "failed", "chargedEvents": {},"errorCode": "PASSWORD_REQUIRED", "errorMessage": "The PDF is password-protected. Provide the password in pdfPassword to extract it." }
Error codes: INVALID_URL, URL_NOT_ALLOWED, DOWNLOAD_FAILED, DOWNLOAD_TIMEOUT, FILE_TOO_LARGE, EMPTY_FILE, UNSUPPORTED_FILE_TYPE (for example an HTML page or a DOCX), PASSWORD_REQUIRED, WRONG_PASSWORD, CORRUPT_FILE, IMAGE_TOO_LARGE, OCR_DISABLED, DOCUMENT_TIMEOUT, OUT_OF_MEMORY, KVS_RECORD_NOT_FOUND, PROCESSING_FAILED, RUN_STOPPED.
Pricing
Pay-per-event: you pay for pages with text, not for compute time.
| Event | Price | When |
|---|---|---|
| Page extracted | $0.001 per page | A PDF page read from its text layer that contains text |
| OCR page | $0.01 per page | A scanned page or image read with OCR that contains text |
Each page is billed at most once. Free: blank pages, pictures without text, pages beyond your limits, failed or skipped files.
Examples:
- A 20-page digital report: $0.02
- A 10-page scanned contract: $0.10
- 1,000 receipt photos: $10
- A 100-page PDF where 3 pages are scans: 97 × $0.001 + 3 × $0.01 = $0.127
Set Maximum cost per run in the run options to cap spending. The Actor reserves the cost of each file before processing it, never goes over the limit, and marks what it could not afford as skipped (or partial for a file cut short).
Limits and good to know
- Speed (CPU). Text-layer pages take about 0.1 s each. OCR takes about 2-3 s per dense page on one CPU (default 2 GB memory = half a CPU, so ~5 s). More memory means more CPUs and pages OCR'd in parallel: 8 GB = 2 CPUs, 16 GB = 4 CPUs (about 4x faster on scanned documents).
- Tables. Detected in PDFs from ruling lines and from clearly aligned columns, and in scans from aligned columns. Complex tables (merged cells, nested headers) come out simplified, and tables without visible column gaps come out as text.
- Existing OCR layers. Many scanned PDFs already contain a hidden OCR text layer. In
automode that layer is used (cheap). If it is poor, useocrMode: "always"to re-OCR the scan. - OCR quality depends on the scan. Clean 200-300 DPI prints are read with 95%+ word accuracy. Handwriting, very low resolution, decorative fonts and unusual layouts are much less accurate. Bullets that OCR cannot read may merge list items.
- Very complex layouts (magazines, posters, scanned pages with line numbers in the gutter) can come out in a slightly mixed reading order.
- Languages. OCR uses Tesseract's language models for the 28 languages listed above. PDFs with a text layer work in any language.
- Limits. Up to 5,000 pages and 300 MB per file, images up to 150 megapixels, 5,000 files per run.
- Where your files live. Results are written only to your run's own storage. Unnamed storages are deleted after your plan's retention period (7 days by default), so download or copy what you need.
- Public URLs only. Links to private or local network addresses are blocked. For private files, upload them to an Apify key-value store and pass the keys.
Use it from code, n8n, Make, Zapier or AI agents
Run it synchronously and get the rows back in one HTTP call (best for a few files; synchronous runs time out after 300 s):
curl -X POST "https://api.apify.com/v2/acts/<ACTOR_ID>/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \-H "Content-Type: application/json" \-d '{"fileUrls": ["https://example.com/report.pdf"], "maxCharsInDataset": 1000000}'
Then use markdown from each row, or download markdownUrl. In n8n, Make or Zapier, use the Apify app's "Run Actor" action with the same JSON and map markdown, text or markdownUrl to the next step. AI agents can call the Actor through the Apify MCP server; the input and output schemas describe every field.
Privacy
Files are downloaded, processed in memory and in temporary files for the duration of the run only, then deleted. The results are written only to your own run's storage; nothing is kept elsewhere or used for anything else. Logs contain file URLs, page counts and timings, never document content. Only public URLs (or your own key-value stores) are fetched. Make sure you have the right to process the documents you submit.
FAQ
Do I pay for pages that fail or are blank? No. Only pages that produced text are billed, and only after the results are saved.
Why is a page billed as OCR when my PDF "has text"? In auto mode OCR runs only when a page has no usable text layer: a scanned page (maybe with a small stamp or page number in its text layer), or fonts that map to unreadable characters. The per-page dataset (perPageOutput) shows the reason for every page. Use ocrMode: "never" to never pay for OCR.
Can it read Word, Excel or PowerPoint files? Not directly. Export them to PDF first. You get a clear UNSUPPORTED_FILE_TYPE error instead of a charge.
Password-protected PDFs? Pass pdfPassword. PDFs with only an owner password (printing or copying restrictions) open without one.
How do I chunk the Markdown by page? Split on <!-- page N --> in markdown, split the text on the form feed character, or turn on perPageOutput.
What OCR engine is it? Tesseract 5 (Apache-2.0), with its tessdata_fast language models, on CPU. It was chosen over a deep-learning OCR after a benchmark: about 6x faster on CPU at the same word accuracy, and it keeps multi-column reading order.
Something looks wrong? Open an issue on the Issues tab with the run ID and, if you can, a link to the file. We read every one.
Credits
- PDF parsing and rendering: PDFium via pypdfium2 (BSD-3-Clause / Apache-2.0). Table grid detection helpers from pdfplumber (MIT).
- OCR: Tesseract and tessdata_fast (Apache-2.0). Language detection: py3langid (BSD-3-Clause).
- Example files: USGS Fact Sheet 2013-3082 (public domain, U.S. Geological Survey) and NASA photo S69-39334 (public domain). Neither agency endorses this Actor.