# Image to Text OCR - Photos, Screenshots & Scanned PDFs (`kantolabs/image-to-text-ocr`) Actor

Extract text from images, screenshots, photos and scanned PDFs with a local open-source OCR engine (PP-OCR). Line-level bounding boxes, confidence scores and clean reading-order text. No Google, no API key. Pay only per page read.

- **URL**: https://apify.com/kantolabs/image-to-text-ocr.md
- **Developed by:** [Kanto Labs](https://apify.com/kantolabs) (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

from $3.00 / 1,000 image or page processeds

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

## Image to Text OCR - Extract Text from Photos, Screenshots & Scanned PDFs

Turn **images and scanned PDFs into clean, searchable text** in bulk. Paste links to PNG, JPG, WEBP,
TIFF, HEIC or PDF files and get back the text of every image or page in reading order, with a
**bounding box and confidence score for every line**.

OCR runs **inside the actor** with the open-source PP-OCRv6 models (Apache-2.0). No Google Lens, no
Google Cloud Vision, no API key, no residential proxies - so results are stable, fast and cheap.
You pay **only for images and pages that were actually read**.

### What you can use it for

- **Invoices, receipts and forms** - pull totals, dates and IDs out of scans and photos.
- **Scanned PDFs** - make old scanned documents searchable or feed them into a RAG pipeline.
- **Screenshots** - extract text from app screenshots, chat logs, dashboards and error messages.
- **AI agents** - give an LLM agent eyes for documents via the Apify MCP server (the output is plain JSON text).
- **E-commerce and ads** - read text on product photos, banners and packaging (English and Chinese both work).
- **Data entry automation** - replace manual typing with a scheduled run plus a webhook to Make, Zapier or n8n.

### Why this OCR actor

| | This actor | Google-Lens-based OCR actors |
|---|---|---|
| Engine | Local PP-OCRv6 (open source) | Scrapes Google Lens |
| Scanned PDFs | Yes, page by page | Usually images only |
| PDFs that already contain text | Uses the exact text layer (auto) | - |
| Line bounding boxes + confidence | Yes | Varies |
| Price per 1,000 images/pages | **$3** | $4 - $6 |

### Languages

The multilingual PP-OCRv6 models read **English and other Latin-script languages** (German, French,
Spanish, Italian, Portuguese, Dutch, Polish, Turkish, Vietnamese...), **Chinese**, **Japanese**,
numbers and symbols.

- `fast` is the right choice for Latin-script text, screenshots and documents.
- Use `accurate` for **Japanese** (kana), dense **Chinese**, stylized fonts and poor photos. In our
  tests the fast model garbled Japanese kana while the accurate model read the same lines perfectly.

Handwriting works only when it is neat.

### Input

| Field | What it does | Default |
|---|---|---|
| `sources` | Image or PDF URLs, one per line | 3 sample files |
| `quality` | `fast` (tiny model) or `accurate` (small model, ~4x slower, better on dense CJK text and poor photos) | `fast` |
| `includeLines` | Add every text line with its pixel box `[x0, y0, x1, y1]` and confidence | `true` |
| `minConfidence` | Drop lines recognized below this confidence | `0.5` |
| `pdfMode` | `auto` = use a page's own text layer when it has one, OCR otherwise; `ocr` = always OCR | `auto` |
| `maxPdfPages` | Read only the first N pages of each PDF | `50` |
| `pdfDpi` | Render resolution for scanned PDF pages (300 for small print) | `200` |
| `skipEmpty` | Pages with no text are reported but not billed | `true` |

```json
{
    "sources": [
        "https://example.com/scans/invoice-0142.pdf",
        "https://example.com/photos/receipt.jpg"
    ],
    "quality": "fast",
    "includeLines": true
}
```

### Output

One dataset item **per image or per PDF page**. Real output from a test run (the `lines` array is
trimmed to its first two entries):

```json
{
    "sourceUrl": "https://raw.githubusercontent.com/tesseract-ocr/tessdoc/main/images/eurotext.png",
    "fileName": "eurotext.png",
    "fileType": "image",
    "page": 1,
    "pageCount": 1,
    "method": "ocr",
    "width": 640,
    "height": 500,
    "text": "The (quick) [brown] {fox} jumps!\nOver the $43,456.78 <lazy> #90 dog\n& duck/goose, as 12.5% of E-mail\nfrom aspammer@website.com is spam.\nDer ,schnelle\" braune Fuchs springt\nüber den faulen Hund. Le renard brun\n«rapide» saute par-dessus le chien\nparesseux. La volpe marrone rapida\nsalta sopra il cane pigro. El zorro\nmarrón rápido salta sobre el perro\nperezoso. A raposa marrom rápida\nsalta sobre o cão preguiçoso.",
    "lineCount": 12,
    "charCount": 412,
    "averageConfidence": 0.9842,
    "lines": [
        { "text": "The (quick) [brown] {fox} jumps!", "confidence": 0.9792, "box": [61, 32, 522, 74] },
        { "text": "Over the $43,456.78 <lazy> #90 dog", "confidence": 0.9835, "box": [61, 62, 560, 105] }
    ],
    "processingTimeMs": 2106,
    "error": null,
    "quality": "fast"
}
```

`method` tells you how the text was obtained: `ocr`, or `pdf-text-layer` when a PDF page already
contained selectable text (exact and instant). Files that fail are listed with an `error` message and
are **not charged**.

### Pricing

Pay per event - you are billed only for results:

| Event | Price |
|---|---|
| Image or PDF page read (fast model, or PDF text layer) | **$0.003** ($3 per 1,000) |
| Image or PDF page read with the accurate model | $0.008 ($8 per 1,000) |

Worked examples:

- 1,000 receipt photos, fast model: **$3.00**
- A 40-page scanned contract: 40 pages x $0.003 = **$0.12**
- 200 screenshots with the accurate model: 200 x $0.008 = **$1.60**

Downloads that fail, unreadable files and pages without text cost nothing. Set **Maximum cost per
run** in the run options and the actor stops cleanly when it is reached.

### FAQ

**Can I upload files instead of giving URLs?** Put them in an Apify key-value store (or any bucket,
Google Drive/Dropbox direct-download link, your own server) and pass the record URLs.

**Is my data used for anything else?** No. Files are processed in the run's container and only the
results you see in the dataset are stored, in your own Apify account.

**How accurate is it?** Clean screenshots and scans come out nearly perfect - the sample above
averages 0.98 confidence. For blurry phone photos, Japanese or dense Chinese text, or decorative
fonts, switch `quality` to `accurate`. Every line carries its own `confidence`, so you can flag
uncertain lines for review.

**Does it keep layout?** Text is returned in reading order with line breaks and blank lines between
paragraphs. Use `lines[].box` if you need exact positions (for example to rebuild tables or to find a
value to the right of a label).

**Is there a size limit?** 50 MB per file by default (up to 200 MB), and up to 500 pages per PDF.

**Something is not working?** Open an issue on the actor's Issues tab - it is answered quickly.

# Actor input Schema

## `sources` (type: `array`):

Direct links to images (PNG, JPG, WEBP, TIFF, BMP, GIF, HEIC) or PDF files, one per line. Scanned PDFs are OCR'd page by page. Files stored in an Apify key-value store or any public bucket work too.

## `quality` (type: `string`):

`fast` (PP-OCRv6 tiny) is excellent for screenshots, documents and Latin-script text and costs $3 per 1,000 pages. `accurate` (PP-OCRv6 small) is about 4x slower and better on dense Chinese/Japanese text, stylized fonts and poor photos; it is billed as a separate, higher-priced event.

## `includeLines` (type: `boolean`):

Add a `lines` array: each detected text line with its confidence and pixel box \[x0, y0, x1, y1]. Turn off for plain text only.

## `minConfidence` (type: `string`):

Drop text lines recognized with lower confidence than this. 0.5 removes most noise; lower it for very poor scans.

## `pdfMode` (type: `string`):

`auto` uses a PDF page's own text layer when it has one (exact, fast) and runs OCR only on scanned pages. `ocr` always runs OCR on every page.

## `maxPdfPages` (type: `integer`):

Only the first N pages of each PDF are read (each page is billed as one result).

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

Resolution used to rasterize scanned PDF pages before OCR. 200 is right for normal documents; use 300 for small print.

## `skipEmpty` (type: `boolean`):

When an image or page contains no readable text it is reported with an error message and not charged.

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

Larger files are skipped (free).

## `requestTimeoutSecs` (type: `integer`):

Give up on a file that does not download within this time.

## Actor input object example

```json
{
  "sources": [
    "https://raw.githubusercontent.com/tesseract-ocr/tessdoc/main/images/eurotext.png",
    "https://raw.githubusercontent.com/RapidAI/RapidOCR/main/python/tests/test_files/ch_en_num.jpg",
    "https://raw.githubusercontent.com/ocrmypdf/OCRmyPDF/main/tests/resources/skew.pdf"
  ],
  "quality": "fast",
  "includeLines": true,
  "minConfidence": "0.5",
  "pdfMode": "auto",
  "maxPdfPages": 50,
  "pdfDpi": 200,
  "skipEmpty": true,
  "maxFileSizeMb": 50,
  "requestTimeoutSecs": 60
}
```

# 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 = {
    "sources": [
        "https://raw.githubusercontent.com/tesseract-ocr/tessdoc/main/images/eurotext.png",
        "https://raw.githubusercontent.com/RapidAI/RapidOCR/main/python/tests/test_files/ch_en_num.jpg",
        "https://raw.githubusercontent.com/ocrmypdf/OCRmyPDF/main/tests/resources/skew.pdf"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("kantolabs/image-to-text-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 = { "sources": [
        "https://raw.githubusercontent.com/tesseract-ocr/tessdoc/main/images/eurotext.png",
        "https://raw.githubusercontent.com/RapidAI/RapidOCR/main/python/tests/test_files/ch_en_num.jpg",
        "https://raw.githubusercontent.com/ocrmypdf/OCRmyPDF/main/tests/resources/skew.pdf",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("kantolabs/image-to-text-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 '{
  "sources": [
    "https://raw.githubusercontent.com/tesseract-ocr/tessdoc/main/images/eurotext.png",
    "https://raw.githubusercontent.com/RapidAI/RapidOCR/main/python/tests/test_files/ch_en_num.jpg",
    "https://raw.githubusercontent.com/ocrmypdf/OCRmyPDF/main/tests/resources/skew.pdf"
  ]
}' |
apify call kantolabs/image-to-text-ocr --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,kantolabs/image-to-text-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/MdJtIEcpJwh2orPZt/builds/fep4SiWJaOLmfn1hV/openapi.json
