# OCR API (Image to Text, Searchable PDF, 20 Languages) (`locaihost/ocr`) Actor

Extract text from images and scanned PDFs with OCR: plain text, Markdown, word boxes with confidence, or a searchable PDF. Each row has page, text, confidence, wordCount, rotation and method. Pages that already have text are read free. 20 languages, no API key. $3 per 1,000 OCR pages.

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

## Pricing

from $1.65 / 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

## OCR API (Image to Text, Searchable PDF, 20 Languages)

Turn scans, phone photos, screenshots and scanned PDFs into text, one row per page, as plain text, Markdown, word boxes with confidence or a searchable PDF, in 20 languages. Pages of a PDF that already have selectable text are read straight from the file, for free. No login, no API key.

- **Make scanned archives searchable:** add an invisible text layer to old scans so any PDF viewer can search and copy them.
- **Read receipts and labels:** narrow columns are read line by line, so prices stay on the line of their item.
- **Feed documents to an LLM or RAG pipeline:** get clean text or Markdown from scans that text extractors return empty.
- **Build redaction and form tools:** every line and word comes with a pixel box and a 0-100 confidence.
- **Process mixed batches:** images and PDFs in one run, and only the pages that need OCR are charged.

### Quick start

Paste this into the **JSON** tab of the input and click **Start**. It reads a scanned letter (PNG) and a 4-page PDF that mixes typed and scanned pages:

```json
{
  "startUrls": [
    {"url": "https://api.apify.com/v2/key-value-stores/nEPO9kOfmRIEPfuSU/records/scan-letter.png?signature=1bcKYhjnnj39zb3oJzzcL"},
    {"url": "https://api.apify.com/v2/key-value-stores/nEPO9kOfmRIEPfuSU/records/mixed.pdf?signature=1FMu7z7IhbDt9yOUxaxS"}
  ],
  "languages": ["eng"]
}
```

You get 5 rows (one per page) in about 15 seconds: 3 pages read with OCR and charged, and 2 typed PDF pages read from the text layer for free.

### What data can I get?

| Group | Fields |
|---|---|
| Source | `source`, `fileName`, `fileType` (`pdf`, `png`, `jpeg`, `webp`, `tiff`, `gif`, `bmp`, `heic`, `avif`), `page`, `pageCount`, `languages` |
| Text | `text` (always), `markdown` (Markdown output), `lines` (Boxes output: each line and word with a pixel box `[x0, y0, x1, y1]` and a confidence) |
| Result | `method` (`ocr`, `text-layer`, `blank` or `error`), `charged`, `confidence` (0-100), `wordCount`, `characters` |
| Clean-up | `rotation` (degrees turned upright), `skewDegrees`, `enhanced`, `scale` |
| Image | `width`, `height` (pixels), `dpi` (PDF render resolution) |
| Files and errors | `searchablePdfUrl` (with *Also make a searchable PDF*), `error` |

One real row (run on 2026-10-18, page 3 of the sample PDF, `text` and `source` shortened):

```json
{
  "source": "https://api.apify.com/v2/key-value-stores/nEPO9kOfmRIEPfuSU/records/mixed.pdf?signature=…",
  "fileName": "mixed.pdf",
  "fileType": "pdf",
  "page": 3,
  "pageCount": 4,
  "method": "ocr",
  "charged": true,
  "text": "Signed minutes\n\nNext steps: order the timber by the end of the month, ask the council about a second water tap, …",
  "confidence": 96.5,
  "wordCount": 64,
  "characters": 267,
  "languages": ["eng"],
  "rotation": 0,
  "skewDegrees": 0,
  "enhanced": false,
  "scale": 1,
  "width": 2481,
  "height": 3508,
  "dpi": 300,
  "searchablePdfUrl": null,
  "error": null
}
```

The dataset has three views (**Pages**, **Markdown**, **Page details**) and exports to JSON, CSV, Excel or HTML. The run's `SUMMARY` record lists every file with its OCR, text-layer, blank, failed and skipped page counts.

**What it handles:**

- **Files:** PNG, JPG, WebP, TIFF (every page of a multi-page TIFF), GIF, BMP, HEIC/HEIF (iPhone photos) and AVIF, plus scanned, mixed or born-digital PDFs, from links, an upload, or an Apify key-value store. Google Drive and Dropbox share links are turned into direct downloads.
- **Only the pages that need it:** in a mixed PDF, typed pages are read from their text layer (exact and free) and only scanned pages are OCR'd. Set *Which PDF pages to OCR* to every page to re-read them all.
- **Clean-up:** sideways and upside-down pages are turned upright, tilted scans (up to 15 degrees) are levelled, shadows on phone photos and faded print are evened out, small print is enlarged, and hard pages get a second pass. Each row says what was done.
- **Layout:** Markdown output keeps headings, paragraphs and lists, and two-column pages come out column by column. A narrow column such as a receipt is read as one block; set *Page layout* to force it either way.
- **20 languages, up to 4 per run:** English, Spanish, French, German, Portuguese, Italian, Dutch, Polish, Russian, Ukrainian, Turkish, Arabic, Hindi, Chinese (Simplified and Traditional), Japanese, Korean, Vietnamese, Indonesian and Thai. Codes (`deu`), short codes (`de`) and names (`German`) all work.

**How accurate is it?** The engine is Tesseract 5 with our own clean-up in front of it. On 28 test pages we made ourselves (clean, tilted and noisy scans, a fax, phone photos with shadows, faded and tiny print, six languages), the average character error was 0.0% on the 20 standard pages and 4.0% on the 8 hard ones, against 5.3% and 39% for plain Tesseract without the clean-up. We tuned the clean-up on these pages, so treat the numbers as a guide, not a promise.

### How much does it cost?

You pay per page read with OCR that returned text. There is no charge for Apify platform usage on top.

| Row | Free and Starter | Scale | Business |
|---|---|---|---|
| OCR page | $3.00 per 1,000 | $2.25 per 1,000 | $1.65 per 1,000 |

Plus $0.00005 per run start (charged per GB of memory, so $0.0002 at the default 4 GB). **Apify's free $5 monthly credit covers about 1,600 OCR pages.** Free-plan runs OCR up to 100 pages each (text-layer pages don't count); any paid Apify plan removes the cap.

**What's free:** PDF pages read from their text layer, blank pages and pictures with no text, pages and files that fail (bad links, web pages instead of files, unsupported formats, password-protected PDFs), duplicate links, and pages not reached because of a cap or the run's time limit. To cap a run's spend, set **Maximum cost per run** in the run options, or *Max OCR pages per run*: the run stops before going over and `SUMMARY` says how many pages were left.

### Limits, stated up front

- **Handwriting** is not read well. Typed and printed text only.
- **Tables** come out as lines of text, not as table cells.
- **Low-resolution faxes** (around 100 dpi with broken letters) still come back with mistakes; scan at 200 dpi or more when you can.
- **Strong perspective** (a page photographed at a steep angle) is not flattened. Photograph from above.
- **Password-protected PDFs** are skipped with a message. Remove the password first.
- Up to 1,000 files per run. Files up to 100 MB by default (adjustable to 500 MB). *Max pages per file* reads only the first N pages of each PDF or TIFF.
- **Speed:** 2 pages at a time at the default 4 GB memory, one more for each extra 2 GB, up to 8. The quick start's 5 pages took 7 seconds of run time. If the run's time limit is reached, pages already read are delivered and the rest are listed in `SUMMARY`, not charged.
- Word boxes are in pixels of the original image; for PDF pages, pixels at the render resolution in `dpi` (300 by default), so `x / dpi * 72` gives PDF points.

### Schedules

Save a task that points at a key-value store or a list of links, then add a schedule under **Schedules** in Apify Console to OCR new scans as they arrive. Each run reads every file you give it, so point it at the new files only.

### Integrations: API, n8n, Make and AI agents

**API.** Run it and get the rows back in one call:

```bash
curl -X POST "https://api.apify.com/v2/acts/locaihost~ocr/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"startUrls": [{"url": "https://example.com/scan.png"}], "languages": ["eng", "deu"]}'
```

Apify's API clients for Python and JavaScript work the same way.

**n8n, Make, Zapier, Google Sheets, Slack, webhooks.** Apify's built-in integrations send each run's dataset wherever you need it.

**AI agents (MCP).** Add this Actor to Claude, Cursor or any MCP client with `https://mcp.apify.com/?tools=locaihost/ocr`, then ask something like *"Read this scanned invoice and tell me the total and the due date."*

### FAQ

#### How much does it cost to OCR a scanned PDF?

$3.00 per 1,000 OCR pages on the Free and Starter plans, less on Scale and Business. Typed pages, blank pages and failures are free. Apify's free $5 monthly credit covers about 1,600 OCR pages.

#### Can I use it through the API or from Python?

Yes. Use the `run-sync-get-dataset-items` call above, or Apify's Python and JavaScript clients. Every run's rows are also available as JSON, CSV or Excel from its dataset.

#### Can I use it from an AI agent?

Yes. Through the Apify MCP server (`https://mcp.apify.com/?tools=locaihost/ocr`) any MCP client can send an image or PDF link and read the text back.

#### Is it legal to OCR documents this way?

The Actor reads only the files you supply. It doesn't crawl or collect anything else, everything runs inside your own run, and the files and results stay in your run's storage. Documents can contain personal data: you are responsible for having the right to process them and for handling that data lawfully.

#### Why are some fields empty?

`markdown` and `lines` appear only with Markdown or Boxes output. Text-layer pages have no `confidence`, `rotation` or `skewDegrees`, because they were not OCR'd. A blank page has empty `text` and `method: "blank"`. A failed file has one row with `method: "error"` and the reason in `error`.

#### Does it work on Word or Excel files?

No: send PNG, JPG and other images, or PDFs. Word, PowerPoint and Excel files are reported as unsupported and not charged; the Document to Markdown Actor below converts them.

### More from locaihost data

- [Document to Markdown converter](https://apify.com/locaihost/doc-to-markdown): PDFs with text, Word, PowerPoint and Excel files to clean Markdown.
- [Website screenshot API](https://apify.com/locaihost/screenshot): capture web pages as images or PDFs.
- [YouTube transcript scraper](https://apify.com/locaihost/youtube-transcripts): video captions as text for the same pipeline.

### Did it help?

If this saved you time, a quick rating on the Store helps other people find it.

### Support

Something not working, or a language or format you need? Open an issue on the **Issues** tab: we answer issues within 24 hours.

# Changelog

This Actor's version history is a separate document: https://apify.com/locaihost/ocr/changelog.md

# Actor input Schema

## `startUrls` (type: `array`):

Links to images (PNG, JPG, WebP, TIFF incl. multi-page, GIF, BMP, HEIC/HEIF, AVIF) or PDFs (scanned, mixed or born-digital). Public URLs or signed links (S3, GCS, Azure, Dropbox). Google Drive and Dropbox share links are turned into direct downloads. Up to 1,000 files per run.

## `uploadedFile` (type: `string`):

Or upload one image or PDF from your computer.

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

OCR files already stored on Apify. Pick the store; the Actor gets read-only access to it for this run.

## `keyValueStoreRecords` (type: `array`):

Keys of the files in the store above. Leave empty to OCR every record in the store.

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

The languages on the page, up to 4. More languages make each page slower, so pick only what the documents use.

## `outputMode` (type: `string`):

Every row has the page's plain text. Markdown adds a `markdown` field. Boxes adds `lines`, each with its words, pixel boxes \[x0, y0, x1, y1] on the original page and a 0-100 confidence.

## `layout` (type: `string`):

Auto runs full layout analysis (columns, paragraphs) and reads a narrow column such as a receipt line by line. Pick a fixed layout when you know what your images look like.

## `searchablePdf` (type: `boolean`):

Saves one PDF per file with an invisible text layer over each OCR'd page, so you can search and copy text. Pages that already had text are kept as they were. The link is in each row (`searchablePdfUrl`) and the file is in the run's key-value store.

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

Auto reads pages that already have selectable text straight from the PDF, exactly and for free, and OCRs scanned pages. Every page re-reads all pages with OCR (each one is charged), for PDFs whose text layer is broken or garbled.

## `autoRotate` (type: `boolean`):

Detects pages scanned or photographed at 90, 180 or 270 degrees and turns them upright.

## `deskew` (type: `boolean`):

Levels pages scanned at a slight angle (up to 15 degrees).

## `enhance` (type: `boolean`):

Evens out shadows on phone photos, darkens faded or low-contrast print, and gives hard pages a second pass. Clean scans are left as they are.

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

Only the first N pages of each PDF or TIFF. 0 = all pages.

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

Stop after this many charged pages in the whole run. 0 = no limit (your maximum cost per run still applies).

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

Resolution scanned PDF pages are rendered at before OCR. 300 suits most documents; 400 can help very small print.

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

Larger files are skipped with a message (not charged).

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://api.apify.com/v2/key-value-stores/nEPO9kOfmRIEPfuSU/records/scan-letter.png?signature=1bcKYhjnnj39zb3oJzzcL"
    },
    {
      "url": "https://api.apify.com/v2/key-value-stores/nEPO9kOfmRIEPfuSU/records/mixed.pdf?signature=1FMu7z7IhbDt9yOUxaxS"
    }
  ],
  "languages": [
    "eng"
  ],
  "outputMode": "text",
  "layout": "auto",
  "searchablePdf": false,
  "ocrMode": "auto",
  "autoRotate": true,
  "deskew": true,
  "enhance": true,
  "maxPagesPerDocument": 0,
  "maxPages": 0,
  "pdfDpi": 300,
  "maxFileSizeMb": 100
}
```

# Actor output Schema

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

One row per page: text, how it was read, whether it was charged, confidence.

## `markdown` (type: `string`):

Markdown per page (output = markdown).

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

The run's key-value store: searchable PDFs (when requested) and SUMMARY.

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

Per file: pages OCR'd, read from the text layer, blank, failed or skipped; totals; cost.

# 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 = {
    "startUrls": [
        {
            "url": "https://api.apify.com/v2/key-value-stores/nEPO9kOfmRIEPfuSU/records/scan-letter.png?signature=1bcKYhjnnj39zb3oJzzcL"
        },
        {
            "url": "https://api.apify.com/v2/key-value-stores/nEPO9kOfmRIEPfuSU/records/mixed.pdf?signature=1FMu7z7IhbDt9yOUxaxS"
        }
    ],
    "languages": [
        "eng"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("locaihost/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 = {
    "startUrls": [
        { "url": "https://api.apify.com/v2/key-value-stores/nEPO9kOfmRIEPfuSU/records/scan-letter.png?signature=1bcKYhjnnj39zb3oJzzcL" },
        { "url": "https://api.apify.com/v2/key-value-stores/nEPO9kOfmRIEPfuSU/records/mixed.pdf?signature=1FMu7z7IhbDt9yOUxaxS" },
    ],
    "languages": ["eng"],
}

# Run the Actor and wait for it to finish
run = client.actor("locaihost/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 '{
  "startUrls": [
    {
      "url": "https://api.apify.com/v2/key-value-stores/nEPO9kOfmRIEPfuSU/records/scan-letter.png?signature=1bcKYhjnnj39zb3oJzzcL"
    },
    {
      "url": "https://api.apify.com/v2/key-value-stores/nEPO9kOfmRIEPfuSU/records/mixed.pdf?signature=1FMu7z7IhbDt9yOUxaxS"
    }
  ],
  "languages": [
    "eng"
  ]
}' |
apify call locaihost/ocr --silent --output-dataset

```

## MCP server setup

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