# PDF OCR & Text Extractor: Text from PDFs, Scans and Images (`lepidata/pdf-ocr`) Actor

PDF, scan and image text: extract it with OCR, one row per page. Reads the text layer when present, Tesseract OCR when not. Bulk PDF to text for RAG, search and data entry. Failed pages are free.

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

## Pricing

from $4.00 / 1,000 ocr page or images

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 OCR & Text Extractor: Text from PDFs, Scans and Images

Turn PDFs, scanned documents and photos of documents into searchable text, one row per page. Pages with a text layer are read directly; scanned pages and images go through Tesseract OCR.

### Common uses

- **PDF to text in bulk** for search, RAG and LLM pipelines: one row per page, with the page number.
- **Scanned PDF and image OCR** (TIFF, PNG, JPEG): turn scans into searchable text.
- **A cheap OCR API**: $4 per 1,000 OCR pages, $0.80 per 1,000 text-layer pages, and failed pages are free.

### Example inputs

Paste any of these into the JSON input tab:

**Text-layer PDF (IRS W-9, 6 pages, about $0.005)**

```json
{"urls": ["https://www.irs.gov/pub/irs-pdf/fw9.pdf"], "mode": "text"}
```

**Scanned image OCR (Tesseract test images, about $0.008)**

```json
{"urls": ["https://raw.githubusercontent.com/tesseract-ocr/test/main/testing/phototest.tif", "https://raw.githubusercontent.com/tesseract-ocr/tessdoc/main/images/eurotext.png"], "mode": "auto"}
```

**PDF with tables (IRS W-4, 5 pages)**

```json
{"urls": ["https://www.irs.gov/pub/irs-pdf/fw4.pdf"], "mode": "auto", "extractTables": true}
```

### Input

- **File URLs**: public links to PDFs or images (PNG, JPEG, TIFF, GIF, WebP, BMP). The type is detected from the file contents, not the extension.
- **Files from a key-value store** (optional): pick one of your stores; each record is processed (filter with **Key prefix**). The Actor can read only the store you pick.
- **URLs from a dataset** (optional): the `url` field of each item.
- **Mode**: `auto` (text layer, OCR only where it is missing), `text` (never OCR), `ocr` (OCR every page).
- **Also extract tables as CSV** (off by default, free, best effort).

### Output

One `page` row per extracted page: `file`, `page`, `method` (`text` or `ocr`), `text`, `charCount`, `ocrMeanConfidence` (OCR only), and `tables` (only when table extraction is on). Each file also gets a free `file-summary` row (`pagesExtracted`, `pagesFailed`, `pagesBilled`, `truncatedAtPageCap`). Problems come back as free `page-error` / `file-error` rows with an `errorCode`.

### Pricing (pay per event)

| Event | Price |
|---|---|
| Text page (PDF text layer) | $0.80 per 1,000 pages |
| OCR page or image (images up to 10 MB) | $4 per 1,000 |
| Large image OCR (10–20 MB) | $8 per 1,000 |

You pay only for pages actually extracted, never for the page count a file claims. Failed pages and files are free. Free-plan accounts get up to 100 pages per run. Your maximum charge per run is respected: the run stops when it is reached and nothing past it is charged.

### Limits

- PDFs up to 50 MB and 500 pages per file (later pages are skipped and reported in the summary).
- Images up to 20 MB and 50 megapixels; large images are scaled to about 3,500 px on the long edge before OCR.
- Each file is processed in its own sandboxed process with a time limit; damaged, password-protected or malicious files return a free error row.
- English OCR. OCR text is a searchable draft, not exact field values: check numbers from receipts or invoices.

### Privacy

Files are processed in your own run and not stored or viewed by us. You're responsible for having the right to process them.
The Actor never logs extracted text, only counts and error codes.

### Open-source components

pypdf (BSD-3), pdfplumber (MIT), Pillow (MIT-CMU), Tesseract (Apache-2.0), poppler `pdftoppm` (GPL, run as a separate program).

# Actor input Schema

## `urls` (type: `array`):

Public http/https links to PDFs or images (PNG, JPEG, TIFF, GIF, WebP, BMP). The type is detected from the file itself. Private, internal and unusual addresses are refused with a free error row. Up to 1,000 files per run.

## `keyValueStore` (type: `string`):

Optional. Pick one of your key-value stores with uploaded PDFs or images; every record (or those matching the key prefix) is processed. The Actor gets read access to this store only.

## `keyPrefix` (type: `string`):

Optional. Only process store records whose key starts with this.

## `dataset` (type: `string`):

Optional. Pick one of your datasets; the `url` field of each item is processed like the File URLs above. The Actor gets read access to this dataset only.

## `mode` (type: `string`):

auto: use the PDF's text layer and OCR only pages with no or almost no text. text: never OCR (pages without text return a free error row). ocr: OCR every page. Images are always OCR'd.

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

For text-layer pages, add tables that read cleanly (at least 3 rows, most cells filled) as CSV strings in the `tables` field. Not billed separately.

## Actor input object example

```json
{
  "urls": [
    "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
  ],
  "mode": "auto",
  "extractTables": false
}
```

# Actor output Schema

## `items` (type: `string`):

All rows from this run.

# 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 = {
    "urls": [
        "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("lepidata/pdf-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 = { "urls": ["https://www.irs.gov/pub/irs-pdf/fw9.pdf"] }

# Run the Actor and wait for it to finish
run = client.actor("lepidata/pdf-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 '{
  "urls": [
    "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
  ]
}' |
apify call lepidata/pdf-ocr --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,lepidata/pdf-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/gjNU2TjREv4zQhRet/builds/GcLBnwW9m7ZGIjqQH/openapi.json
