# PDF OCR (`ely_source/pdf-ocr`) Actor

Read scanned PDFs and get clean text per page, with a confidence score for each one. Pages that already carry a text layer are read directly and never billed as OCR, so mixed batches come back faster, more accurately and cheaper.

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

## Pricing

from $20.00 / 1,000 page read by ocrs

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

Read the text out of scanned PDFs — invoices, contracts, delivery notes, archives —
and get clean, page-by-page text back as JSON.

Scans are pictures of documents: there is no text inside them to copy, search or feed
to an AI model. This Actor renders every page and reads it with OCR, so the content
becomes usable data.

### What does PDF OCR do?

Give it one or more links to PDF files. For each page it returns the text, how that
text was obtained, and a confidence score. Export as JSON, CSV or Excel in one click,
or pull it through the Apify API.

**It only runs OCR where OCR is needed — and only bills for those pages.** Real-world
document batches are rarely all scans: exports and digital invoices already carry a
text layer. Reading that layer is instant and character-perfect, while OCR is slow and
approximate. This Actor checks each page first, takes the embedded text when it exists,
and falls back to OCR only for genuine scans. Mixed batches therefore come back faster,
more accurately, and cheaper than with a tool that OCRs everything blindly.

### Why use this OCR Actor?

**You see how much to trust each page.** Every OCR'd page carries a mean confidence
score, so you can route low-confidence pages to a human instead of discovering the
problem downstream.

**You know where each page's text came from.** The `textSource` field says `ocr` or
`text_layer`, which tells you immediately whether a value was read or recognised.

**A page limit protects your bill.** OCR is the expensive part of the job, so each
file stops after 50 pages by default. Raise it deliberately when you mean to process
long archives.

**One bad file never kills the batch.** An unreachable URL comes back as a row with a
`download_failed` status, so a 500-document run always finishes and you can see exactly
which files need attention.

### What data can it extract?

| Field | Description |
|---|---|
| `sourceUrl` | The PDF the page came from |
| `status` | `ok`, `no_text_found`, `no_pages`, `charge_limit_reached`, `download_failed` or `parse_failed` |
| `pageNumber` | Page number, starting at 1 |
| `pageCount` | Total pages in the document |
| `textSource` | `ocr` for a scan, `text_layer` when the PDF already held its text |
| `confidence` | Mean OCR confidence, 0 to 1 (null for text-layer pages) |
| `text` | The page text |

### How to extract text from a scanned PDF

1. Click **Try for free**.
2. Paste direct links to your PDF files into the **PDF URLs** field.
3. Leave it empty to run a bundled sample scan and see the output format.
4. Click **Start**, then open the **Dataset** tab when the run finishes.
5. Export as CSV or JSON, or copy the API endpoint to automate it.

### Input

```json
{
  "pdfUrls": [
    "https://example.com/scanned-invoice.pdf",
    "https://example.com/contract-1998.pdf"
  ],
  "maxPages": 50
}
````

### Output

```json
{
  "sourceUrl": "https://example.com/scanned-invoice.pdf",
  "status": "ok",
  "pageNumber": 1,
  "pageCount": 3,
  "textSource": "ocr",
  "confidence": 0.9713,
  "text": "DELIVERY NOTE DN-2026-0884\nSupplier: Demo Industrial Supplies BV\n..."
}
```

### Pricing

| Event | Price |
|---|---|
| Actor start | $0.01 per GB of memory — $0.02 at the 2 GB default |
| Page read by OCR | $0.02 |

#### How much does it cost to OCR 1,000 pages?

About **$20 plus $0.02 per run** for the start. Pages that already carry a text layer
are read for free — they are not billed as OCR — so a mixed batch costs less than its
page count suggests. Splitting one job into many small runs pays the start fee each
time, so send batches rather than single files.

### Works well with

Need the **tables** out of your documents rather than raw text? Run
[PDF Table Extractor](https://apify.com/ely_source/pdf-table-extractor) on the ones that
already have a text layer. This Actor is the step before it for anything scanned.

### FAQ

#### What languages does it read?

The recognition model handles Latin-script languages, including English, French, German,
Spanish and Italian, as well as Chinese. Text quality matters more than language: a clean
300 dpi scan reads far better than a photo taken at an angle.

#### How accurate is it?

On clean scans the mean confidence is typically above 0.95. Rather than asking you to
trust a number in a README, the Actor returns the confidence per page so you can measure
it on your own documents.

#### Why is a page returned with no text?

Either the page is genuinely blank, or the image is too degraded to recognise — very low
resolution, heavy skew, or handwriting. Those pages come back with a `no_text_found`
status rather than silently disappearing.

#### Can it read handwriting?

No. The model targets printed text. Handwritten notes will produce poor results or none.

#### Is there a size limit?

Yes, 50 MB per file, and 50 pages per document by default. Both are deliberate guards
against runaway costs; the page limit is adjustable in the input.

### Support

Found a document type that reads badly? Open an issue on the **Issues** tab with an
example. Real documents are what make an OCR Actor better.

# Actor input Schema

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

Direct links to the PDF files to read, typically scanned invoices, contracts, delivery notes or archives. Each link must point at the PDF file itself, not at a viewer page. Leave empty to run a bundled sample scan and see the output format.

## `maxPages` (type: `integer`):

Stop after this many pages in each document. OCR is the costly part of the run, so this cap protects you from an unexpectedly large file. Raise it when you knowingly process long archives.

## Actor input object example

```json
{
  "pdfUrls": [
    "https://example.com/scanned-invoice.pdf"
  ],
  "maxPages": 50
}
```

# Actor output Schema

## `pages` (type: `string`):

One item per page, carrying the text, whether it came from OCR or from an existing text layer, and the OCR confidence.

# 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": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("ely_source/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 = { "pdfUrls": [] }

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

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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": []
}' |
apify call ely_source/pdf-ocr --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=ely_source/pdf-ocr",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "PDF OCR",
        "description": "Read scanned PDFs and get clean text per page, with a confidence score for each one. Pages that already carry a text layer are read directly and never billed as OCR, so mixed batches come back faster, more accurately and cheaper.",
        "version": "0.1",
        "x-build-id": "jZOtqIaq1caWyPA0l"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/ely_source~pdf-ocr/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-ely_source-pdf-ocr",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/ely_source~pdf-ocr/runs": {
            "post": {
                "operationId": "runs-sync-ely_source-pdf-ocr",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/ely_source~pdf-ocr/run-sync": {
            "post": {
                "operationId": "run-sync-ely_source-pdf-ocr",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "pdfUrls": {
                        "title": "PDF URLs",
                        "type": "array",
                        "description": "Direct links to the PDF files to read, typically scanned invoices, contracts, delivery notes or archives. Each link must point at the PDF file itself, not at a viewer page. Leave empty to run a bundled sample scan and see the output format.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "maxPages": {
                        "title": "Maximum pages per file",
                        "minimum": 1,
                        "maximum": 2000,
                        "type": "integer",
                        "description": "Stop after this many pages in each document. OCR is the costly part of the run, so this cap protects you from an unexpectedly large file. Raise it when you knowingly process long archives.",
                        "default": 50
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
