PDF Text Extractor & Markdown for LLM RAG: OCR, Tables, Chunks avatar

PDF Text Extractor & Markdown for LLM RAG: OCR, Tables, Chunks

Pricing

Pay per event

Go to Apify Store
PDF Text Extractor & Markdown for LLM RAG: OCR, Tables, Chunks

PDF Text Extractor & Markdown for LLM RAG: OCR, Tables, Chunks

Convert PDF to clean Markdown for LLM & RAG pipelines: text extractor with OCR fallback (50 languages), GitHub-flavored table reconstruction (incl. tables spanning pages), right-to-left script support, and optional RAG chunking. ~$0.005 for a 20-page text PDF. Pay only for pages with real text.

Pricing

Pay per event

Rating

0.0

(0)

Developer

Pankovs

Pankovs

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

Turn any PDF into clean Markdown for LLMs and RAG: headings, lists and GitHub-flavored tables rebuilt from the page, OCR for scanned pages in 50 languages, and ready-to-embed chunks with page and section citations. About $0.008 for a 20-page text PDF.

Handles what most PDF to Markdown Actors don't: tables that span pages, right-to-left scripts (Arabic/Hebrew) in correct reading order, filled form fields, and RAG chunks that never cut a table in half.

Use cases

  • RAG and chatbots: feed contracts, manuals or research papers into a vector store, with chunksMeta page ranges and section paths so answers can cite "page 4, section 3.2".
  • LLM data pipelines: turn reports, filings and invoices into Markdown an LLM reads reliably, tables included.
  • Scanned archives: OCR old scans and image-only PDFs in 50 languages; pages with a real text layer skip OCR and cost less.
  • Multilingual documents: Arabic, Hebrew, Persian and Urdu text comes out in reading order; Cyrillic, Greek, CJK and Indic scripts are supported.

How to use

  1. Paste one or more PDF links into pdfs (or upload a file as pdfFile).
  2. Pick options if you need them: ocrLanguages for scans, outputChunks for RAG chunks, includePageMarkers.
  3. Run the Actor and download the dataset (JSON, CSV, Excel), or call it from the API, Make, Zapier or LangChain.

Input

FieldTypeDefaultDescription
pdfsarray of strings[]http(s) URLs of PDF files to convert.
pdfFilestringnoneOptional single PDF uploaded via the Apify Console.
ocrModeauto | always | neverautoauto OCRs only low-text pages; always OCRs every page; never uses the text layer only.
ocrLanguagesstringengOCR languages for scanned pages, Tesseract codes joined with + (e.g. eng+rus), up to 5. See below.
maxPagesPerPdfinteger200Stop processing a PDF after this many pages.
extractTablesbooleantrueRebuild tables as GitHub-flavored Markdown tables. See below.
outputChunksbooleanfalseAlso split the Markdown into RAG chunks.
chunkSizeinteger2000Target chunk length in characters (chunks end on a boundary within 15%).
chunkOverlapinteger200Overlap between consecutive chunks, at most half of chunkSize (a larger value is reduced to chunkSize / 2, with a warning).
includePageMarkersbooleanfalseInsert <!-- page: N --> markers before each page's Markdown.

At least one of pdfs or pdfFile must be set.

OCR languages

50 languages, all built into the actor (no downloads at run time). Combine up to 5 codes with + (eng+deu). Any other code, or more than 5 codes, is rejected as an input error (run fails up front, nothing charged). OCR models: Tesseract tessdata_best (Apache-2.0) via @tesseract.js-data.

eng English, rus Russian, deu German, fra French, spa Spanish, por Portuguese, ita Italian, pol Polish, lav Latvian, ukr Ukrainian, nld Dutch, swe Swedish, dan Danish, nor Norwegian, fin Finnish, ces Czech, slk Slovak, hun Hungarian, ron Romanian, tur Turkish, ell Greek, ara Arabic, heb Hebrew, fas Persian, urd Urdu, hin Hindi, ben Bengali, tam Tamil, tel Telugu, mar Marathi, tha Thai, vie Vietnamese, ind Indonesian, msa Malay, chi_sim Chinese (Simplified), chi_tra Chinese (Traditional), jpn Japanese, kor Korean, bul Bulgarian, srp Serbian (Cyrillic), hrv Croatian, slv Slovenian, lit Lithuanian, est Estonian, cat Catalan, glg Galician, eus Basque, afr Afrikaans, swa Swahili, fil Filipino.

List only the languages your scans contain. Each extra language slows OCR and can hurt accuracy (eng+rus on an English scan puts Cyrillic look-alike letters into English words; measured with scripts/ocr-bench.mjs), so the default is eng.

Script auto-detect: if a page's OCR confidence is below 70%, the page's script is detected (Tesseract OSD) and, if none of your ocrLanguages covers it, the page is re-read with that script's language added: Latin eng, Cyrillic rus, Greek ell, Arabic ara, Hebrew heb, Devanagari hin, Bengali ben, Tamil tam, Telugu tel, Thai tha, Chinese chi_sim, Japanese jpn, Korean kor (e.g. a Greek scan with the default eng is re-read with eng+ell). The better result is kept and a warning suggests fixing ocrLanguages. Detection needs a page's worth of text; very short pages are not re-read. If confidence is still below 60% (e.g. a Georgian or Armenian scan with no model), the page text is kept but flagged in warnings and the page is not charged.

Text-layer PDFs work in any language/script, including Arabic/Hebrew (right-to-left runs are re-ordered into reading order, including PDFs that map whole Arabic words to one glyph, e.g. WeasyPrint/cairo; Chrome-printed Arabic, which encodes medial yeh/heh as Persian/Urdu letters, is folded back to Arabic yeh/heh unless the page contains Persian/Urdu-only letters).

Tables

Tables are rebuilt from the positions of the text on the page (aligned columns + rows), so bordered and borderless tables both work. They are emitted as GitHub-flavored Markdown:

| Quarter | Revenue | Profit |
| --- | --- | --- |
| Q1 2025 | 1,250.00 | 269.50 |

A table that continues on the next page (with or without a repeated header row) is merged into one table unless includePageMarkers is on. RAG chunks never cut a table in half: a table longer than chunkSize becomes its own chunk. When the layout is ambiguous (multi-column prose, sparse figure labels, fewer than 3 rows), the text stays plain paragraphs instead of a broken table. Every printed line stays its own row, even in dense tables grouped in blocks of five (tax tables). Tables printed side by side (or next to a prose column) come out as separate tables. A cell merged across columns goes into its first column; a group label centered over several rows (rowspan, e.g. LLaMA next to 7B, 13B, 33B) is repeated on each of those rows. Pages packed with more than 4,000 text cells or 600 multi-cell lines, or laid out so that table search would have to recheck the same text hundreds of times, skip table search: their text comes out as plain paragraphs and the item gets a warning naming the page. Limits: multi-level headers come out as separate header rows, header text stacked over several lines may stay above the table as plain text, and tables on scanned (OCR) pages stay plain text.

Output

One dataset item per PDF, for example (arXiv 1706.03762 with outputChunks on, shortened):

{
"url": "https://arxiv.org/pdf/1706.03762v7",
"fileName": "...",
"pageCount": 15,
"pagesProcessed": 15,
"pagesOcr": 0,
"markdown": "## Attention Is All You Need
Ashish Vaswani Google Brain ...
### Abstract
The dominant sequence transduction models ...",
"chunks": ["## Attention Is All You Need
...", "..."],
"chunksMeta": [
{ "index": 0, "pageStart": 1, "pageEnd": 1, "headings": [] },
{ "index": 3, "pageStart": 4, "pageEnd": 5, "headings": ["Attention Is All You Need", "3 Model Architecture"] }
],
"metadata": { "title": "...", "author": "...", "created": "..." },
"warnings": [],
"error": null
}
  • url, fileName, pageCount (pages in the PDF), pagesProcessed (pages converted), pagesOcr (pages billed as ocr-page)

  • markdown: the whole document. Filled-in form fields (text and choice fields, checked checkboxes, the selected radio option) are appended to their page as - field name: value lines. With includePageMarkers, pages without text keep a bare marker.

  • chunks (with outputChunks): pieces of markdown of about chunkSize characters (within 15% either way). A chunk ends at the best boundary in that range: before a heading, then a paragraph break, a line break, a sentence end, and only then a space between words. Words are never cut, a heading is never left at the end of a chunk, and the overlap with the previous chunk starts at the beginning of a word (at most chunkOverlap characters). Tables are never split: a table longer than chunkSize becomes its own chunk, together with the heading right above it.

  • chunksMeta (with outputChunks): one entry per chunk, in the same order, so you can cite pages and sections:

    { "index": 3, "pageStart": 4, "pageEnd": 5, "headings": ["Attention Is All You Need", "3 Model Architecture"] }

    pageStart/pageEnd are the PDF page numbers the chunk text comes from; headings is the section path where the chunk's new text begins (top level first, empty before the first heading). Numbered section titles in bold at the normal text size (2.2 Architecture in a two-column paper) count as headings too, nested by their number.

  • metadata: title, author, created (ISO 8601 in UTC, e.g. 1999-09-21T17:45:50Z; the raw PDF value if it is not a date)

  • warnings: per-page notes (low OCR confidence, OCR language switch, dense table pages) and a note when maxPagesPerPdf stopped before the end: Stopped at maxPagesPerPdf=6 of 28 pages: pages 7-28 were not converted or charged.

  • error: set on failure (for example The URL returned an empty response (0 bytes), not a PDF), or on an early stop once the run's charge limit is reached; the item still contains whatever was processed.

Pricing (pay-per-event)

EventPriceWhen
document$0.00416once per PDF that produced at least one billed page
text-page$0.000192per page extracted from the text layer
ocr-page$0.01per page whose text came from OCR
apify-actor-start$0.00005Apify's standard start event, once per GB of memory (at most twice, runs are capped at 2 GB)

Worked examples:

  1. One 20-page text PDF: $0.00416 + 20 x $0.000192 = $0.008, plus $0.0001 for the run start.
  2. A nightly job converting 50 new 20-page reports and one 10-page scan: 50 x $0.008 + ($0.00416 + 10 x $0.01) + $0.0001 = $0.504 per night, about $15 a month.

Failed PDFs (download error, password-protected, corrupt, not a PDF) and PDFs without any extractable text are never charged, not even the document event.

FAQ and limits

Am I charged for OCR that found nothing? No. If OCR returns no text, or less text than the page's own text layer, the text layer is used and the page is billed as text-page (or not at all if the page has no text). If OCR confidence is below 60% (usually a language or script missing from ocrLanguages), the page is flagged in warnings and not charged. Empty pages are free. With ocrMode=never, a page with no text layer is free and flagged in warnings, so a scan does not come back as a silently empty page.

What if a link is flaky? Downloads are retried twice (1 s, 3 s backoff) on HTTP 502/503/504 and connection resets.

How big can a PDF be? maxPagesPerPdf (default 200) caps the pages converted per PDF; the item says where it stopped. Set a maximum charge per run in the Apify Console to cap the cost; the Actor stops cleanly when it is reached.

Which tables are not rebuilt? Tables on scanned (OCR) pages stay plain text. Multi-level headers come out as separate header rows, and header text stacked over several lines may stay above the table as plain text.

More

Real output, prices and changelog: PDF to Markdown for RAG, real output and cost.