# PDF to Text & Markdown with OCR for Scans (19 Languages) (`prime619/pdf-to-text-markdown-ocr`) Actor

Convert PDFs to clean Markdown and plain text. Digital pages are read exactly from the text layer; scans, phone photos and skewed or sideways pages are cleaned up and read with OCR in 19 languages (auto-detected). Tables, form fields and RAG chunks included.

- **URL**: https://apify.com/prime619/pdf-to-text-markdown-ocr.md
- **Developed by:** [Philipp Primisser](https://apify.com/prime619) (community)
- **Categories:** AI, Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.50 / 1,000 ocr pages

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## PDF to Text & Markdown with OCR for Scans (19 Languages)

> **Try it free:** Apify's free plan includes monthly platform credit, no credit card needed. One example run with the input below costs about **$0.02** (2 PDFs ($0.002 each), 6 text pages ($0.0002 each) and 3 scanned pages read with OCR ($0.005 each)).

```json
{
  "pdfUrls": [
    {
      "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
    },
    {
      "url": "https://raw.githubusercontent.com/tinytoolkit-org/pdf-sample-files/master/out/sample-scanned.pdf"
    }
  ]
}
```

Paste this into the **JSON** tab of the input form and click **Start**.

Turn PDFs into clean **Markdown** and **plain text**. Born-digital pages are read from the PDF's own text layer, so they're fast and exact. Scanned pages go through **OCR in 19 languages**, and the language is detected automatically. Headings, lists and ruled tables come out as Markdown, and an optional chunk list with page numbers is ready for RAG and LLM pipelines.

### Who it is for

- **RAG and LLM builders** who need clean Markdown with headings and tables, plus chunks that keep page numbers and the heading path.
- **Back offices and archives** that mix born-digital PDFs with scans (invoices, letters, contracts, forms) and need searchable text.
- **Researchers and journalists** extracting text from reports, laws and papers in many languages.
- **Automation builders** (Make, Zapier, n8n, API) who want one call that handles both digital and scanned PDFs.

### What you get for each PDF

| Field | Content |
|---|---|
| `markdown` | Markdown with `#` headings, `-` lists and `|` tables |
| `text` | The same content as plain text |
| `markdownFile`, `textFile` | Links to `.md` / `.txt` files in the run's key-value store (always saved, also for very long documents) |
| `pageCount`, `pagesProcessed`, `textPages`, `ocrPages`, `fastTextPages` | What was read and how (`fastTextPages`: text pages beyond the first 15, plain text only) |
| `chargedEvents` | What this PDF was charged for |
| `ocrLanguages`, `ocrConfidence` | OCR language(s) used and mean word confidence (0–100) |
| `language` | Detected document language (ISO code, e.g. `en`, `de`, `pt`, `hi`, `ja`) |
| `title`, `author`, `creationDate` | From the PDF metadata, if present |
| `formFields` | Values of fillable form fields (AcroForm), if the PDF has any |
| `chunks` | Optional: overlapping chunks with `pageStart`, `pageEnd`, `section` (heading path) and `text` |
| `pages` | Optional: Markdown per page with method (`text`/`ocr`) and confidence |
| `warnings`, `error` | Clear messages, e.g. "password-protected", "low OCR confidence on page 7" |

`results.xlsx` and `results.csv` contain one row per PDF (without the full text). `RUN_SUMMARY` has the run's counts.

### Quick start

1. Paste one or more **direct links to PDF files** (Google Drive and Dropbox share links are converted automatically).
2. Leave **OCR** on *Auto*. If you know the language of your scans, pick it under **OCR languages**. That's faster and a little more accurate.
3. Run. Open the **PDFs** table, or download the `.md` files.

```json
{
  "pdfUrls": [{ "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf" }],
  "ocrMode": "auto",
  "ocrLanguages": [],
  "chunkForRag": true,
  "chunkSizeChars": 2000,
  "chunkOverlapChars": 200
}
```

### How OCR works

- **Auto (default):** a page gets OCR only if it has no usable text layer (fewer than 25 characters, or a garbled layer from broken font encodings). Everything else is read directly from the PDF, which is fast and exact.
- **Force:** OCR every page, for example when the text layer is known to be wrong.
- **Off:** never run OCR; scanned pages come back empty with a warning.
- Engine: Tesseract 5 (LSTM models) at 300 dpi. With *Clean up scans* (on by default) each scanned page is first prepared: uneven lighting is evened out (phone photos, shadows), skew up to ±8° is straightened, sideways or upside-down pages are turned upright, specks are removed, and a page that reads poorly is retried with local (adaptive) thresholding. Lines that the OCR engine itself barely trusts (stains, show-through, pictures) are dropped. What was fixed is listed per page in `pages[].scanFixes`.
- **Languages:** English, German, Spanish, Portuguese, French, Italian, Dutch, Polish, Turkish, Indonesian, Vietnamese, Russian, Ukrainian, Arabic, Hindi, Bengali, Japanese, Chinese (Simplified) and Korean, plus a model for **German Fraktur** (historic blackletter print).
- **Automatic language:** if a PDF also has text pages, their language is used. Otherwise the script is detected (Latin, Fraktur, Cyrillic, Arabic, Devanagari, Han, Japanese, Hangul and others) and, for Latin script, the language is guessed from a first OCR pass. German pages that read badly are retried with the Fraktur model. You can choose up to 3 languages yourself.

#### Measured accuracy (our own tests, 2026-10-09)

Character error rate (CER) against proofread transcriptions, lower is better.

**Real old scans** (36 public-domain book pages from Wikimedia Commons, 1700s–1900s, with Wikisource's proofread text as ground truth; automatic language, default settings):

| Language (pages) | Original scan | Simulated phone photo\* | Page turned 90° |
|---|---|---|---|
| German, Fraktur print (6) | median 6.3 % (3.6–13.6) | 5.8 % | 5.7 % |
| English, 18th/19th c. with long ſ (6) | 4.8 % (1.4–19.8) | 4.7 % | 4.5 % |
| Spanish (6) | 2.0 % (0–13) | 2.4 % | 1.8 % |
| French (6) | 4.4 % (2.1–10.3) | 3.1 % | 3.2 % |
| Hindi (6) | 6.8 % (3.5–10.6) | 6.3 % | 5.7 % |
| Portuguese (5) | 0.9 % (0.2–11.1) | 0.9 % | 0.7 % |

\* uneven lighting, low contrast, blur, strong JPEG compression and 2° skew. Before the lighting correction, the same phone simulations had a mean CER of 21 %; now 5.7 % (original scans 5.5 %). In separate tests on the same pages, ~100 dpi scans, 4.5° skew and photocopy-style noise stayed at 5.4–6.6 % mean CER.

**Clean documents:** EU regulation (German) rendered at 300 dpi: 0.3–1.7 %; scientific paper (English, formulas): 0.9–2.9 %; synthetic noisy scans in 10 languages: 0 % except Arabic (16 %).

What this means: modern printed documents usually come out nearly exact; old, damaged or photographed pages have a few wrong characters per hundred, sometimes many more (a stained 18th-century English page: 20 %). Check `ocrConfidence` and the warnings. A handwritten page (Portuguese manuscript) produced no usable text.

### Pricing

Pay per event, with no subscription:

- **PDF processed:** once per successfully processed file. Failed files (404, password-protected, not a PDF) are not charged.
- **Text page:** each page read from the text layer, **charged for at most 15 pages per PDF**. Longer born-digital PDFs cost the same as a 15-page one. Pages 16 and later are extracted at no charge in a **fast plain-text mode**: the text comes straight from the PDF's text layer with its line breaks, but without Markdown structure (no headings, lists or tables, no column detection, line-end hyphens are kept). Those pages are marked `scanFixes.mode = "fast"` in `pages` and counted in `fastTextPages`. Tip: to get full Markdown for a specific part of a long PDF, select it with the page range (the first 15 text pages of the selection get full layout).
- **OCR page:** each page read with OCR (not capped, because every OCR page takes real compute; use *Maximum pages per PDF* or the page range to limit it).

See the **Pricing** tab for current prices. *Maximum pages per PDF*, the page range and Apify's *Maximum cost per run* cap what a run can cost. When the limit is reached, the run stops cleanly before the next page and tells you so. With **Only new files**, files that an earlier run already processed are skipped and not charged.

### Limits

- Files must be **publicly downloadable**. The Actor does not log in anywhere and does not crawl websites. Give it the direct PDF link.
- Up to 500 PDFs per run, 200 MB per file, 2,000 pages per PDF.
- **Password-protected PDFs** are reported, not cracked. Remove the password first.
- Full Markdown structure (headings, lists, tables, columns) covers the first 15 text pages of each PDF; later text pages are plain text (see Pricing).
- Tables are detected on text-layer pages with ruled lines. Tables without lines, and tables on scanned pages, come out as plain text lines.
- Complex layouts (forms with side-by-side boxes, magazine layouts) keep all the text, but the reading order can be imperfect. Two-column pages are detected.
- Formulas, subscripts and superscripts are flattened to plain characters.
- Handwriting is not supported (a handwritten test page gave no usable text).
- **Existing OCR layers:** many archive PDFs already contain an old, invisible OCR text layer. In *Auto* mode that layer is used as is, even if it is poor (in our tests: wrong word order on a 1808 French scan). If the text looks garbled, run again with OCR set to *Force*.
- Old prints with damaged, stained or very faint pages, long ſ and ligatures still produce errors (see the table above). Arabic is clearly weaker than the other languages. Vertical Japanese text is not supported well.
- Very large scan pages take longer: on Apify's default 1 GB memory an OCR page took about 4–26 seconds in our tests.
- Line-end hyphens are joined ("Bür-" + "ger" → "Bürger"). Hyphenated compounds split at a line end (e.g. "English-" + "to-German") lose that hyphen.

### Rights and data protection

Only process documents you are allowed to use. Files are processed inside your Apify run, and the results stay in your own Apify storage. No third-party OCR or AI service is called. Personal data in your documents remains your responsibility under the GDPR or other laws that apply.

### Integrations and API

Use the Apify API, schedules, webhooks, or the Make, Zapier and n8n integrations. Typical flow: a new file in a folder triggers a run with its link, and the Markdown goes to your vector database or document system.

### FAQ

**Does it work on scanned PDFs?** Yes. Scanned pages are found automatically and read with OCR. Born-digital pages in the same file are read directly.

**Which language should I choose?** Leave it empty to detect automatically. If all your documents are in one language, select it. That skips detection and is slightly faster.

**Can I get one result per page?** Turn on *Include per-page results*.

**Is my file sent anywhere else?** No. Download, extraction and OCR all run inside the Actor.

**Why is a page empty?** Probably a scanned page with OCR set to *Off*, or an image without text. Check `warnings`.

### Related Actors by the same developer

- [EU Tender Monitor (TED)](https://apify.com/prime619/eu-tender-monitor): new public tenders from the EU's TED portal, filtered by CPV code, country and contract value.

### En español

Convierte archivos PDF en Markdown y texto plano. Las páginas digitales se leen directamente de la capa de texto del PDF; las páginas escaneadas se leen con OCR (Tesseract) en 19 idiomas, y el idioma se detecta automáticamente. Antes del OCR se corrigen la iluminación desigual (fotos de móvil), las páginas torcidas o giradas y las motas. La escritura a mano no es compatible. A partir de la página de texto 16 de un PDF, el texto se extrae sin coste en modo rápido (texto plano, sin estructura Markdown).

```json
{ "pdfUrls": [{ "url": "https://example.com/escaneo.pdf" }], "ocrLanguages": ["spa"] }
```

### Em português (Brasil)

Converte arquivos PDF em Markdown e texto simples. Páginas digitais são lidas diretamente da camada de texto do PDF; páginas digitalizadas são lidas com OCR (Tesseract) em 19 idiomas, com detecção automática do idioma. Antes do OCR, a iluminação irregular (fotos de celular), páginas tortas ou giradas e manchas são corrigidas. Texto manuscrito não é suportado. A partir da 16ª página de texto de um PDF, o texto é extraído sem custo em modo rápido (texto simples, sem estrutura Markdown).

```json
{ "pdfUrls": [{ "url": "https://example.com/digitalizacao.pdf" }], "ocrLanguages": ["por"] }
```

### Auf Deutsch

Wandelt PDF-Dateien in Markdown und reinen Text um. Digitale Seiten werden direkt aus der Textebene des PDFs gelesen, gescannte Seiten per OCR (Tesseract) in 19 Sprachen erkannt; die Sprache wird automatisch bestimmt, auch alte Frakturschrift. Vor der OCR werden ungleichmäßige Beleuchtung (Handyfotos), schiefe oder gedrehte Seiten und Flecken korrigiert. Handschrift wird nicht unterstützt. Ab der 16. Textseite eines PDFs wird kostenlos im schnellen Modus extrahiert (reiner Text, ohne Markdown-Struktur).

```json
{ "pdfUrls": [{ "url": "https://example.com/scan.pdf" }], "ocrLanguages": ["deu"] }
```

### En français

Convertit des fichiers PDF en Markdown et en texte brut. Les pages numériques sont lues directement dans la couche texte du PDF ; les pages numérisées sont lues par OCR (Tesseract) en 19 langues, avec détection automatique de la langue. Avant l'OCR, l'éclairage irrégulier (photos prises au téléphone), les pages inclinées ou pivotées et les taches sont corrigés. L'écriture manuscrite n'est pas prise en charge. À partir de la 16e page de texte d'un PDF, le texte est extrait gratuitement en mode rapide (texte brut, sans structure Markdown).

```json
{ "pdfUrls": [{ "url": "https://example.com/numerisation.pdf" }], "ocrLanguages": ["fra"] }
```

### हिंदी में

यह Actor PDF फ़ाइलों को Markdown और सादे टेक्स्ट में बदलता है। डिजिटल पेज सीधे PDF की टेक्स्ट लेयर से पढ़े जाते हैं; स्कैन किए गए पेज 19 भाषाओं में OCR (Tesseract) से पढ़े जाते हैं, और भाषा अपने-आप पहचानी जाती है। OCR से पहले असमान रोशनी (मोबाइल फ़ोटो), टेढ़े या घूमे हुए पेज और धब्बे ठीक किए जाते हैं। पुरानी हिंदी किताबों के स्कैन पर हमारे परीक्षणों में कुछ प्रतिशत अक्षर गलत पढ़े गए, इसलिए ज़रूरी दस्तावेज़ों में परिणाम जाँच लें। हस्तलिखित टेक्स्ट समर्थित नहीं है। किसी PDF के 16वें टेक्स्ट पेज से आगे का टेक्स्ट बिना शुल्क के तेज़ मोड में निकाला जाता है (सादा टेक्स्ट, Markdown संरचना के बिना)।

```json
{ "pdfUrls": [{ "url": "https://example.com/scan.pdf" }], "ocrLanguages": ["hin"] }
```

### 日本語

PDF ファイルを Markdown とプレーンテキストに変換します。デジタル PDF のページはテキストレイヤーから直接読み取り、スキャンされたページは Tesseract による OCR(19 言語、言語は自動判定)で読み取ります。OCR の前に、照明ムラ(スマートフォン撮影)、傾いたページや回転したページ、汚れを補正します。手書き文字には対応していません。縦書きの文章は正しく読み取れない場合があります。PDF の 16 ページ目以降のテキストページは、高速モード(Markdown 構造なしのプレーンテキスト)で無料で抽出されます。

```json
{ "pdfUrls": [{ "url": "https://example.com/scan.pdf" }], "ocrLanguages": ["jpn"] }
```

# Changelog

This Actor's version history is a separate document: https://apify.com/prime619/pdf-to-text-markdown-ocr/changelog.md

# Actor input Schema

## `pdfUrls` (type: `array`):

Direct links to PDF files (up to 500 per run). Google Drive and Dropbox share links are converted to their direct-download form. The files must be publicly downloadable; this Actor does not log in anywhere. Only process files you have the right to use.

## `outputFormat` (type: `string`):

Markdown keeps headings, lists and tables, which works well for LLMs and RAG. Plain text has no markup.

## `ocrMode` (type: `string`):

Auto reads the PDF's own text where it exists and runs OCR only on scanned pages (and on pages whose text layer is garbled). OCR pages cost more than text pages.

## `ocrLanguages` (type: `array`):

Leave empty to detect the language automatically (script detection plus a language guess; German pages that read badly are retried with the Fraktur model for historic print). Set it when you know the language: it is faster and more accurate. Up to 3; each extra language makes OCR slower.

## `pageRange` (type: `string`):

Optional, for example 1-5,8,10- . Empty means all pages.

## `maxPagesPerPdf` (type: `integer`):

Pages beyond this are skipped (and not charged). Protects you from unexpectedly long files.

## `extractTables` (type: `boolean`):

Turn ruled tables in text-layer pages into Markdown tables. Tables on scanned pages come out as text.

## `chunkForRag` (type: `boolean`):

Adds a 'chunks' array: overlapping pieces with page numbers and the heading path, ready for embeddings.

## `chunkSizeChars` (type: `integer`):

Maximum characters per chunk.

## `chunkOverlapChars` (type: `integer`):

Characters repeated from the end of the previous chunk.

## `includePages` (type: `boolean`):

Adds a 'pages' array with the Markdown, method (text or OCR) and OCR confidence of every page.

## `cleanScans` (type: `boolean`):

Evens out uneven lighting (phone photos, shadows), straightens skewed pages, removes speckles, and turns sideways pages upright. Leave it on unless very small print comes out broken.

## `onlyNewFiles` (type: `boolean`):

Skip files (by content hash) that an earlier run with the same state name already processed. Skipped files are not charged. Useful for scheduled runs over a folder of links.

## `stateName` (type: `string`):

Name for the 'Only new files' memory, so different schedules do not share it.

## Actor input object example

```json
{
  "pdfUrls": [
    {
      "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
    },
    {
      "url": "https://raw.githubusercontent.com/tinytoolkit-org/pdf-sample-files/master/out/sample-scanned.pdf"
    }
  ],
  "outputFormat": "both",
  "ocrMode": "auto",
  "ocrLanguages": [],
  "pageRange": "",
  "maxPagesPerPdf": 300,
  "extractTables": true,
  "chunkForRag": false,
  "chunkSizeChars": 2000,
  "chunkOverlapChars": 200,
  "includePages": false,
  "cleanScans": true,
  "onlyNewFiles": false,
  "stateName": ""
}
```

# Actor output Schema

## `results` (type: `string`):

One item per PDF with Markdown, text and metadata.

## `files` (type: `string`):

All .md and .txt files of this run.

## `excel` (type: `string`):

results.xlsx

## `summary` (type: `string`):

RUN_SUMMARY

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "pdfUrls": [
        {
            "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
        },
        {
            "url": "https://raw.githubusercontent.com/tinytoolkit-org/pdf-sample-files/master/out/sample-scanned.pdf"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("prime619/pdf-to-text-markdown-ocr").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "pdfUrls": [
        { "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf" },
        { "url": "https://raw.githubusercontent.com/tinytoolkit-org/pdf-sample-files/master/out/sample-scanned.pdf" },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("prime619/pdf-to-text-markdown-ocr").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "pdfUrls": [
    {
      "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
    },
    {
      "url": "https://raw.githubusercontent.com/tinytoolkit-org/pdf-sample-files/master/out/sample-scanned.pdf"
    }
  ]
}' |
apify call prime619/pdf-to-text-markdown-ocr --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,prime619/pdf-to-text-markdown-ocr"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/umD4wZHdx12alEvAk/builds/LDpIWtSywAILTdZiI/openapi.json
