# PDF & Scanned Document to Text, Tables & Fields (OCR) (`jakob_jerich/pdf-ocr-table-extractor`) Actor

Extract text, Markdown, tables and key fields (dates, amounts, invoice numbers) from PDFs and scanned images by URL. Built-in OCR for scanned pages in 11 languages, no external AI or API key. Pay per document and per page.

- **URL**: https://apify.com/jakob_jerich/pdf-ocr-table-extractor.md
- **Developed by:** [Jakob Jerič](https://apify.com/jakob_jerich) (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

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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 & Scanned Document to Text, Tables & Fields (OCR)

Turn PDFs, scanned documents and images into **clean text, LLM-ready Markdown, tables (rows and columns) and key fields** such as dates, money amounts, percentages and invoice or order numbers. Paste a list of document URLs and get one structured JSON record per document, ready to export as JSON, CSV or Excel or to pass to your RAG pipeline, spreadsheet or database.

- **Text PDFs**: reads the PDF's own text layer (fast and exact).
- **Scanned PDFs and images**: built-in OCR (Tesseract) in 11 languages. In `auto` mode, OCR runs only on pages that need it.
- **Tables**: detects ruled tables and returns them as a `header` plus `rows`, and as Markdown tables.
- **Key fields**: dates normalized to ISO 8601, amounts with currency, percentages, URLs, reference numbers, and `Label: value` pairs.
- **No external AI, no API key, no account needed.** Your documents are processed inside the Actor run and are not sent to any third-party service.

### What can I use it for?

- **RAG and AI agents**: convert PDF reports, manuals and filings into Markdown chunks with tables kept intact.
- **Invoice and receipt pre-processing**: pull totals, dates and invoice numbers from batches of PDFs or scans.
- **Statistics and government reports**: get the tables from PDF releases (census, central bank, ministry reports) as rows you can load into a spreadsheet.
- **Digitizing scans**: make scanned letters, forms, book pages and faxes searchable.

### How to use it

1. Add one or more **direct links** to PDF or image files (`.pdf`, `.png`, `.jpg`, `.tif`, `.webp`, `.gif`, `.bmp`). Up to 1,000 per run.
2. Pick the **OCR languages** used in your scans (for example English + German).
3. Click **Start**. Each document becomes one row in the dataset.

#### Example input

```json
{
    "documentUrls": [
        { "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf" },
        { "url": "https://raw.githubusercontent.com/tesseract-ocr/tessdoc/main/images/eurotext.png" }
    ],
    "ocrMode": "auto",
    "ocrLanguages": ["eng", "deu"],
    "extractTables": true,
    "extractFields": true,
    "pageRange": "",
    "maxPagesPerDocument": 50
}
```

#### Input options

| Field | Default | What it does |
|---|---|---|
| `documentUrls` | required | Direct public links to PDF or image files. |
| `ocrMode` | `auto` | `auto` = OCR only pages without a usable text layer. `always` = OCR every page (for PDFs with a broken text layer). `never` = text layer only. |
| `ocrLanguages` | `["eng"]` | Up to 4 of: English, German, French, Spanish, Italian, Portuguese, Dutch, Polish, Czech, Slovenian, Croatian. |
| `extractTables` | `true` | Return tables from text-based PDF pages. |
| `extractFields` | `true` | Return dates, amounts, percentages, URLs, reference numbers and label/value pairs. |
| `includeFullText` | `true` | Add the whole document as `text` and `markdown`. |
| `includePages` | `true` | Add per-page text, Markdown, method and OCR confidence. |
| `pageRange` | all pages | For example `1-3, 5, 10-`. |
| `maxPagesPerDocument` | `50` | Safety limit per document (max 1,000). |
| `maxFileSizeMb` | `50` | Larger files are skipped with a clear error. |
| `maxConcurrency` | `2` | Documents processed in parallel. |

### Output

One dataset item per document. The example below is a real result for the U.S. Census Bureau's *New Residential Construction* release (shortened with `...` and to the first item of each list). Every item has the same fields, including failed ones (`status: "error"` with a plain-language `errorMessage`), so your exports always have the same columns.

```json
{
    "url": "https://www.census.gov/construction/nrc/pdf/newresconst.pdf",
    "fileName": "newresconst.pdf",
    "status": "ok",
    "errorMessage": null,
    "fileType": "pdf",
    "fileSizeBytes": 329096,
    "pageCount": 7,
    "pagesProcessed": 7,
    "truncated": false,
    "ocrPageCount": 0,
    "tableCount": 10,
    "metadata": { "title": "Microsoft Word - newresconst_auto_text.docx", "subject": null, "keywords": null, "creatorTool": "PScript5.dll Version 5.2.2", "producer": "Acrobat Distiller 26.0 (Windows)", "createdAt": "2026-10-15T08:53:41", "modifiedAt": "2026-10-15T11:28:35" },
    "text": "FOR RELEASE AT 8:30 AM EDT, TUESDAY, OCTOBER 20, 2026\nMONTHLY NEW RE ...",
    "markdown": "FOR RELEASE AT 8:30 AM EDT, TUESDAY, OCTOBER 20, 2026\nMONTHLY NEW RE ...",
    "fields": {
        "dates": [
            { "raw": "OCTOBER 20, 2026", "iso": "2026-10-20", "ambiguous": false },
            { "raw": "October 20, 2026", "iso": "2026-10-20", "ambiguous": false }
        ],
        "amounts": [],
        "percentages": [
            { "raw": "76.3%", "value": 76.3 },
            { "raw": "-0.2%", "value": -0.2 }
        ],
        "urls": ["www.census.gov/economic-indicators/", "www.census.gov/construction/nrc/"],
        "references": [],
        "keyValues": [
            { "label": "Release Number", "value": "CB26-162" },
            { "label": "Building Permits", "value": "1,400,000 1,500" }
        ]
    },
    "tables": [
        {
            "pageNumber": 3,
            "tableIndex": 1,
            "title": null,
            "rowCount": 21,
            "columnCount": 12,
            "header": ["", "", "United", "States", "", "", "Northeast", "", "Midwest", "", "South", "West"],
            "rows": [
                ["September", "1,444", "888", "58", "498", "148", "57", "204", "130", "773", "517", "319"],
                ["October", "1,418", "886", "55", "477", "145", "58", "197", "123", "736", "516", "340"]
            ],
            "markdown": "|  |  | United | States |  |  | Northeas ..."
        }
    ],
    "pages": [
        { "pageNumber": 1, "method": "text-layer", "ocrConfidence": null, "charCount": 2544, "tableCount": 0, "text": "FOR RELEASE AT 8:30 AM EDT, TU ...", "markdown": "FOR RELEASE AT 8:30 AM EDT, TU ...", "processingMs": 92 }
    ],
    "warnings": [],
    "processingMs": 1900
}
```

- `pages[].method` is `text-layer` or `ocr`. `ocrConfidence` is Tesseract's mean word confidence (0–100) for OCR pages, so you can flag poor scans.
- `fields.dates[].ambiguous` is `true` for dates like `03/04/2024`, which could be day-first or month-first. These are read as day-first.
- The dataset has two views in the Console: **Overview** (status, pages, tables, errors) and **Text & fields**.

### Pricing (pay per event)

You pay only for what is extracted. Failed downloads and unreadable files are **not charged**.

| Event | Price |
|---|---|
| Document processed | $0.001 per document |
| Text page (text-layer PDF page, incl. tables and fields) | $0.0003 per page |
| OCR page (scanned page or image) | $0.004 per page |

Examples:

- 1,000 single-page text PDFs: about **$1.30**.
- 100 ten-page reports: **$0.40**.
- 1,000 scanned single-page images: **$5.00** (100 ten-page scanned PDFs: $4.10).

Platform usage is included in these prices. Set a **maximum cost per run** in the run options and the Actor stops cleanly when it is reached; documents near the limit are cut to the pages you can still afford, with a warning on the item.

### FAQ

**Does it work on scanned PDFs?** Yes. In `auto` mode, every page with no usable text layer is rendered at 300 DPI and read with OCR. Mixed PDFs (some scanned pages, some digital pages) are handled page by page.

**Does it use ChatGPT or another LLM?** No. Extraction is deterministic: a PDF parser, a table detector, Tesseract OCR and rule-based field extraction. The output is repeatable and nothing is sent to an AI provider. The Markdown output is designed to be fed to your own LLM.

**Can it extract tables from scanned documents?** Not yet. Tables are detected on text-based PDF pages that have ruled lines or cell borders. On scanned pages you get the text in reading order.

**Which links work?** Links that download the file directly, such as most `.pdf` URLs, raw GitHub files and S3 or CDN links. Viewer pages (Google Drive preview, DocSend, websites that embed a PDF viewer) return HTML; the Actor reports this clearly instead of failing silently. Files behind a login are not supported.

**Is personal data extracted?** The field extractor deliberately skips e-mail addresses, phone numbers and labels such as name, address, birth date, IBAN or signature, and the PDF `Author` metadata is omitted. The full `text` still contains whatever the document contains, so only process documents you are allowed to process.

**Password-protected PDFs?** They are reported as an error ("password-protected") and not charged.

**How fast is it?** Text PDFs take about 0.1–0.6 s per page. OCR takes about 2–8 s per page at the default 1 GB memory. For large OCR batches, use 2–4 GB memory and raise `maxConcurrency`.

### Limits

- Max 1,000 URLs per run, 1,000 pages per document, 200 MB per file.
- OCR quality depends on the scan. Low-resolution photos, handwriting and heavy skew reduce accuracy; check `ocrConfidence`.
- Table detection needs visible ruled lines or cell borders. Tables laid out only with whitespace come through as text.
- When only the number columns of a table are ruled (common in statistical releases), the row labels to the left are added as the first column. Multi-line column headers come out as several header rows.
- Field extraction is rule-based: numeric dates, amounts and percentages work in any language, but written month names are recognized in English only. Reference-number labels cover EN/DE/FR/ES/IT/SL. It won't find every field on every layout.

# Actor input Schema

## `documentUrls` (type: `array`):

Direct, public links to the files to extract: PDF (text or scanned), PNG, JPEG, TIFF (multi-page), WEBP, GIF or BMP. Up to 1,000 URLs per run. The link must download the file itself, not an HTML page that shows it.

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

When to run OCR (optical character recognition). 'auto' reads the PDF's own text layer and runs OCR only on pages that have no usable text (scanned pages). 'always' OCRs every page, which helps with PDFs whose text layer is garbled. 'never' only reads the text layer (fastest, cheapest; images return no text). Images are always OCR-ed unless set to 'never'.

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

Languages of the scanned text, used by OCR. Pick the languages that appear in your documents (max 4); fewer languages are faster and more accurate.

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

Detect tables on text-based PDF pages and return them as rows and columns (plus a Markdown version). Works on tables with ruled lines/borders; not on scanned (OCR) pages.

## `extractFields` (type: `boolean`):

Find dates (normalized to ISO 8601), money amounts with currency, percentages, URLs, reference numbers (invoice, order, PO, contract no.) and 'Label: value' pairs. Personal data such as e-mails, phone numbers and names is never extracted.

## `includeFullText` (type: `boolean`):

Add the whole document as plain text ('text') and as LLM/RAG-ready Markdown with tables ('markdown').

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

Add a 'pages' array with the text, Markdown, extraction method (text layer or OCR) and OCR confidence of each page. Turn off to get smaller results.

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

Optional. Only process these pages of each document, e.g. '1-3, 5, 10-' (10- means page 10 to the end). Leave empty for all pages.

## `maxPagesPerDocument` (type: `integer`):

Safety limit: at most this many pages are processed per document (after applying the page range). The output says when a document was cut off.

## `maxFileSizeMb` (type: `integer`):

Files larger than this are skipped with a clear error instead of being downloaded.

## `maxConcurrency` (type: `integer`):

How many documents to process at the same time. Higher is faster for many small files but needs more memory (use 2 GB+ memory for 4 or more).

## Actor input object example

```json
{
  "documentUrls": [
    {
      "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
    },
    {
      "url": "https://raw.githubusercontent.com/tesseract-ocr/tessdoc/main/images/eurotext.png"
    }
  ],
  "ocrMode": "auto",
  "ocrLanguages": [
    "eng",
    "deu"
  ],
  "extractTables": true,
  "extractFields": true,
  "includeFullText": true,
  "includePages": true,
  "maxPagesPerDocument": 50,
  "maxFileSizeMb": 50,
  "maxConcurrency": 2
}
```

# Actor output Schema

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

Dataset items: one per input document (status, text, markdown, tables, fields, pages).

# 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 = {
    "documentUrls": [
        {
            "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
        },
        {
            "url": "https://raw.githubusercontent.com/tesseract-ocr/tessdoc/main/images/eurotext.png"
        }
    ],
    "ocrLanguages": [
        "eng",
        "deu"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("jakob_jerich/pdf-ocr-table-extractor").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 = {
    "documentUrls": [
        { "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf" },
        { "url": "https://raw.githubusercontent.com/tesseract-ocr/tessdoc/main/images/eurotext.png" },
    ],
    "ocrLanguages": [
        "eng",
        "deu",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("jakob_jerich/pdf-ocr-table-extractor").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 '{
  "documentUrls": [
    {
      "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
    },
    {
      "url": "https://raw.githubusercontent.com/tesseract-ocr/tessdoc/main/images/eurotext.png"
    }
  ],
  "ocrLanguages": [
    "eng",
    "deu"
  ]
}' |
apify call jakob_jerich/pdf-ocr-table-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jakob_jerich/pdf-ocr-table-extractor"
        }
    }
}
```

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/C1HsVv2B0YDNWEpcC/builds/3V6dXnOqAXYlidQHV/openapi.json
