# OCR: Images & Scanned PDFs to Text (`datagleaner/ocr-images-pdfs`) Actor

OCR for images and scanned PDFs: per-page text, a confidence score and optional word boxes, in 27 languages (Tesseract). Give file URLs or a key-value store. Pages with no text and failed files are not charged.

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

## Pricing

$3.00 / 1,000 page ocr'ds

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

## OCR: Images & Scanned PDFs to Text

**Give it images or scanned PDFs, get the text of every page back, with a confidence score and optional word boxes.** Runs the open-source Tesseract 5 OCR engine with 27 languages built in. Works on PNG, JPEG, TIFF (multi-page fax scans too), GIF, BMP, WebP and PDF. No API key, no Google account, no subscription.

**Price: $0.003 per page** ($3 per 1,000 pages), no start fee. Pages with no text, and files that fail to download or open, are returned with the reason and **not charged**. See Pricing below.

### Why use this one

- **Plain OCR, priced per page.** One flat price per page that has text. Blank pages, photos without text and broken files cost nothing.
- **One row per page.** Each page has its own `text`, `confidence` (0 to 100) and `wordCount`, so you can send low-confidence pages to a person for review and process the rest automatically.
- **Word boxes when you need them.** Turn on `includeWordBoxes` and every word comes with its pixel box and confidence, plus its block, paragraph and line number, for highlighting, redaction, form reading or layout work.
- **27 languages in the image.** English, German, French, Spanish, Italian, Portuguese, Dutch, Polish, Czech, Swedish, Danish, Norwegian, Finnish, Turkish, Russian, Ukrainian, Greek, Arabic, Hebrew, Hindi, Thai, Vietnamese, Indonesian, Chinese (Simplified and Traditional), Japanese and Korean. Mix up to 5 per run.
- **Your files, not a scraper.** Give file links or a key-value store with files you uploaded. The Actor fetches only the files you name, and keeps nothing after the run.
- **Safe on bad input.** One broken link never stops the run: every file returns at least one row, and an `error` field says what happened.

### Minimal input

```json
{"urls": [{"url": "https://keencrest-guides.web.app/samples/sample-scan.png"}], "languages": ["eng"]}
```

Start it with no input and it reads a built-in 3-page sample scan made by Keencrest (one PNG and a 2-page image-only PDF), and says so in the run's status message.

### Image to text

Turn a photo, screenshot or scan into text: give the Actor links to PNG, JPEG, TIFF, GIF, BMP or WebP files and it returns the text of each image, with a confidence score. For a scanned PDF to text, give the PDF link and you get one row per page. Use `single_block` for receipts and labels, `sparse` for screenshots and forms, and list up to 5 languages (for example `["deu", "eng"]`) for mixed-language letters. Extract text from an image, run PDF OCR on an archive, or feed pages to a search index or an AI model.

### What does it do?

For each file it downloads it (up to 50 MB, redirects followed, each hop checked against the host's robots.txt), detects the type from the file's first bytes, renders each PDF page at 300 DPI (you can change this), converts it to grayscale and runs Tesseract on it. Typical uses:

- **Scanned contracts, letters and archives** turned into searchable text.
- **Invoices and receipts** read into text for bookkeeping or for an AI model to extract fields from.
- **Image-only PDFs** that a PDF text extractor returns empty.
- **Screenshots, slides and photos of signs** (use the `sparse` page layout).
- **Old books and reports** fed into search indexes, RAG pipelines and LLMs.
- **Multi-page TIFF fax scans**, one row per page.

### Input

| Field | Type | What it does |
|---|---|---|
| `urls` | list of links | Direct links to image or PDF files. Leave empty (and no store) for the built-in sample. |
| `keyValueStoreId` | store ID | A key-value store whose records are images or PDFs, such as files you uploaded in Console > Storage, or another run's store. |
| `keyValueStoreKeys` | list | Only these record keys. Empty means every image or PDF record in the store (others are skipped). |
| `languages` | list | Tesseract language codes, up to 5. Default `["eng"]`. |
| `includeWordBoxes` | true/false | Add the `words` array with boxes and confidences. Default false. |
| `pageSegmentationMode` | choice | `auto` (default), `single_column`, `single_block` (receipts, labels), `single_line`, `sparse` (screenshots, signs, forms). |
| `pdfDpi` | 100 to 400 | Render resolution for PDF pages. Default 300. |
| `pageRange` | text | For example `1-5, 8` or `3-`, applied to every file. Empty means all pages. |
| `maxPagesPerFile` | number | Default 100. |
| `maxPages` | number | Cap on pages read in the whole run: your cost cap. Default 1,000. |
| `maxFileSizeMb` | number | Default 50. Larger files are skipped and not charged. |
| `maxConcurrency` | 1 to 8 | Files read at once. Default 2. |

#### Uploading your own files

In Apify Console open **Storage > Key-value stores**, create a store, upload your images or PDFs as records, and put the store's ID in `keyValueStoreId`. From code, write the files to a store with the Apify API or SDK and pass its ID. In an integration after another Actor, use `{{resource.defaultKeyValueStoreId}}`.

### Output

One row per page. A file that fails before any page is read returns one row with `pageNumber: null`.

```json
{
  "source": "https://keencrest-guides.web.app/samples/sample-scan.png",
  "sourceType": "url",
  "finalUrl": "https://keencrest-guides.web.app/samples/sample-scan.png",
  "fileName": "sample.pdf",
  "fileType": "pdf",
  "pageNumber": 1,
  "pageCount": 2,
  "status": "ok",
  "billed": true,
  "text": "Keencrest sample document\n\nThis short PDF is hosted by Keencrest so that the PDF to Markdown Actor has a sample it is allowed to read.\nIt contains headings, paragraphs, a list and ...",
  "confidence": 96.0,
  "wordCount": 111,
  "charCount": 600,
  "languages": ["eng"],
  "imageWidth": 2481,
  "imageHeight": 3508,
  "dpi": 300,
  "words": null,
  "robotsBlocked": 0,
  "error": null,
  "failureReason": null,
  "scrapedAt": "2026-11-02T12:00:00+00:00",
  "input": {"source": "https://keencrest-guides.web.app/samples/sample-scan.png", "languages": ["eng"], "pageSegmentationMode": "auto", "includeWordBoxes": false, "pdfDpi": 300, "pageRange": null, "maxPagesPerFile": 100}
}
```

With `includeWordBoxes: true`, `words` holds objects like `{"text": "Keencrest", "confidence": 96.4, "left": 300, "top": 410, "width": 380, "height": 70, "block": 1, "paragraph": 1, "line": 1}` (pixels of the page image, whose size is `imageWidth` x `imageHeight`).

`status` is one of `ok` (text read, charged), `no_text` (the page has no readable text, free), `failed` (free, see `error` and `failureReason`) and `notice` (an input problem, free).

### Pricing

Pay per event: **$0.003 per page with text** (`ocr-page`), no start fee, no monthly plan.

- 1 scanned letter = $0.003. A 40-page scanned contract = $0.12. 1,000 receipts = $3.
- Not charged: pages where no text is found, pages that fail to render or OCR, files that fail to download, refused links, and pages outside your `pageRange` or past `maxPages`.
- Set a **maximum total charge** on the run and the Actor stops before reading a page it cannot charge for.

### Limits

- OCR quality depends on the scan. Straight, 300 DPI, high-contrast scans of printed text read best. Handwriting is not supported in practice, and photos taken at an angle read poorly. Check `confidence`: under about 60 usually means a poor scan.
- The text is returned in reading order with lines and paragraphs. Tables come back as lines of text, not as table cells (use `includeWordBoxes` to rebuild columns).
- Each page is OCR'd, even when a PDF already has a text layer (that keeps the output the same for every file). For digital PDFs with a text layer, a PDF text extractor is faster.
- Files must download without a login. Links on social networks and other sites whose terms forbid automated downloads are refused with `failureReason: refused_host` and not charged.
- Speed: a dense A4 page takes about 16 s at the default 1 GB memory and about 4 s at 4 GB (Apify gives more CPU with more memory). The price per page is the same either way, so raise memory for big batches.
- A page that takes more than 120 seconds to OCR is stopped and returned as `failed`, free.

### Use it from n8n, Make, Zapier or an AI agent

Call it through the Apify API with `run-sync-get-dataset-items` and a JSON body like the minimal input above. Each dataset item is one page, so a workflow can loop over pages, or join `text` by `source` for the whole document. AI agents can call it through Apify's MCP server as a tool that reads any image or scanned PDF link into text.

### FAQ

**Which OCR engine is this?** Tesseract 5 (Apache-2.0) with its standard fast language models, running inside the Actor. Your files are not sent to any third-party OCR service.

**Is it accurate?** On clean printed scans Tesseract typically reads well over 95% of words correctly. On noisy photos, low resolution or unusual fonts it does worse, and the per-page `confidence` tells you which pages to check.

**Can it read several languages on one page?** Yes: list them all in `languages`, for example `["eng", "deu"]`. Fewer languages are faster and more accurate.

**Can I get the text of the whole document in one piece?** Group the rows by `source` and join `text` in `pageNumber` order.

**Why was a page not charged?** Look at its `status` and `error`: `no_text` pages and every `failed` row are free.

### Responsible use

Use it only on files you have the right to process. The Actor processes the files you give it on your behalf and keeps nothing after the run apart from the run's own dataset, which follows your Apify data retention. If your files contain personal data, you are the controller and the obligations that come with it (lawful basis, retention, data subject rights) rest with you. **It is not meant for identity documents:** do not use it to read passports, ID cards, driving licences or other KYC documents, and it gives no verification of any kind. It is not built to read handwriting or to get past any site's download restrictions.

***

Built by Keencrest. Questions or a missing feature: open an issue on the Actor's Issues tab.

# Actor input Schema

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

Direct links to image or PDF files, one per line. Each must download without a login. The host's robots.txt is respected. Links on social networks and other sites whose terms forbid automated downloads are refused. Leave empty (with no key-value store) to read a built-in 3-page sample scan.

## `keyValueStoreId` (type: `string`):

Optional. A key-value store whose records are images or PDFs, for example files you uploaded in Apify Console (Storage > Key-value stores) or another run's store (`{{resource.defaultKeyValueStoreId}}` in an integration). Without keys below, every image or PDF record in the store is read and other records are skipped.

## `keyValueStoreKeys` (type: `array`):

Optional. Only these record keys of the key-value store above.

## `languages` (type: `array`):

The languages printed in the files, up to 5 (more makes OCR slower and less accurate). Pick every language a page mixes, e.g. English and German. Default: English.

## `includeWordBoxes` (type: `boolean`):

Add a `words` array per page: each word with its confidence and pixel box (left, top, width, height) and its block, paragraph and line number, for highlighting, redaction or layout work.

## `pageSegmentationMode` (type: `string`):

How the page is split into text before reading. Automatic suits most scans; try Sparse for screenshots and photos, One uniform block for receipts.

## `pdfDpi` (type: `integer`):

PDF pages are rendered at this resolution before OCR. 300 is the standard; use 400 for small print, 200 for speed on large text.

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

Optional, 1-based, applied to every file: for example 1-5, 8, 10-12 or 3- (page 3 to the end). Empty means all pages.

## `maxPagesPerFile` (type: `integer`):

Only the first N selected pages of each file are read; pageCount still shows the full length.

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

Upper bound on pages read in this run, across all files: your cost cap.

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

Larger files are skipped with an error and not billed.

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

How many files are downloaded and read at once. OCR is CPU-bound: more memory gives the run more CPU, so raise both together.

## Actor input object example

```json
{
  "urls": [
    {
      "url": "https://keencrest-guides.web.app/samples/sample-scan.png"
    }
  ],
  "languages": [
    "eng"
  ],
  "includeWordBoxes": false,
  "pageSegmentationMode": "auto",
  "pdfDpi": 300,
  "maxPagesPerFile": 100,
  "maxPages": 10,
  "maxFileSizeMb": 50,
  "maxConcurrency": 2
}
```

# Actor output Schema

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

No description

# 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": [
        {
            "url": "https://keencrest-guides.web.app/samples/sample-scan.png"
        }
    ],
    "maxPages": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("datagleaner/ocr-images-pdfs").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": [{ "url": "https://keencrest-guides.web.app/samples/sample-scan.png" }],
    "maxPages": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("datagleaner/ocr-images-pdfs").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": [
    {
      "url": "https://keencrest-guides.web.app/samples/sample-scan.png"
    }
  ],
  "maxPages": 10
}' |
apify call datagleaner/ocr-images-pdfs --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,datagleaner/ocr-images-pdfs"
        }
    }
}
```

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/zJgp5vAElBRqfWqYp/builds/o9a6pES7dSmjMYbc9/openapi.json
