# Image to Text OCR — Images & Scanned PDFs, 40+ Languages (`saliga/image-to-text-ocr`) Actor

Extract text from images, screenshots, photos of documents and scanned PDFs with OCR in 40+ languages. Plain text, Markdown, confidence, lines and words with boxes. PDF text layers read for less.

- **URL**: https://apify.com/saliga/image-to-text-ocr.md
- **Developed by:** [Salih Küçükkayış](https://apify.com/saliga) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 image or page read with 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/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

Extract text from images and scanned PDFs with OCR: screenshots, photos of documents, receipts, invoices, scanned contracts, multi-page TIFFs. Give it image or PDF links, or a dataset from another Actor, and get the text of every image and page, as **plain text and Markdown**, with an **average confidence score**, and optionally **every line and word with its bounding box**. 40+ languages, including Turkish, Arabic, Chinese, Japanese, Korean, Hindi and Russian. PDF pages that already have a text layer are read from it instead of OCR: exact and cheaper.

**$1.50 per 1,000 images or pages read with OCR. $0.50 per 1,000 PDF pages read from their text layer.** Failed downloads and unreadable files are free. No subscription, no API key, no outside OCR service: Tesseract runs inside the run.

### Quick start

Press **Start** with the example input. It reads a sample image with English, German, French, Italian, Spanish and Portuguese text:

```json
{
  "imageUrls": ["https://raw.githubusercontent.com/tesseract-ocr/tessdoc/main/images/eurotext.png"],
  "outputDetail": "lines"
}
```

One dataset row per image (trimmed):

```json
{
  "url": "https://raw.githubusercontent.com/tesseract-ocr/tessdoc/main/images/eurotext.png",
  "format": "png",
  "page": 1,
  "method": "ocr",
  "confidence": 91.1,
  "wordCount": 73,
  "width": 640,
  "height": 500,
  "language": "eng",
  "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. ...",
  "markdown": "The (quick) [brown] {fox} jumps! Over the $43,456.78 <lazy> #90 dog & duck/goose, as 12.5% of E-mail from aspammer@website.com is spam. ...",
  "lines": [
    {"text": "The (quick) [brown] {fox} jumps!", "confidence": 96.0, "x": 65, "y": 41, "w": 450, "h": 30},
    {"text": "Over the $43,456.78 <lazy> #90 dog", "confidence": 95.0, "x": 65, "y": 72, "w": 490, "h": 31}
  ]
}
```

### What it does

- **Images**: PNG, JPEG, TIFF (every frame), WebP, GIF, BMP. Transparent images are put on white.
- **Scanned PDFs**: each page is rendered (300 DPI by default) and read with OCR. Pages with a text layer are read from it in *Auto* mode.
- **Text and Markdown**: `text` keeps Tesseract's line breaks; `markdown` joins wrapped lines into paragraphs, keeps bullet lists and turns large text into headings.
- **Confidence**: `confidence` is the average word confidence (0-100); `lowConfidence` is true under 60, so a bad scan does not pass as a good one. `minConfidence` drops uncertain words from the text.
- **Layout**: with *Layout detail* = lines or words, every line or word comes with its confidence and box (`x`, `y`, `w`, `h` in pixels of the original image).
- **Clean-up**: small images (screenshots, thumbnails) are enlarged and contrast is stretched before OCR. Turn it off with *Image clean-up* = None.
- **Chain with other Actors**: give a `datasetId` and the `urlField` that holds the image link (nested fields like `image.url` and lists of links work) to OCR the images another Actor found.

### Input

| Field | Default | What it does |
|---|---|---|
| `imageUrls` | | Image or PDF links |
| `languages` | `eng` | One or more languages: `eng+tur`, `"en, de"`, `["jpn", "eng"]` |
| `outputDetail` | `text` | `text`, `lines` or `words` |
| `layout` | `auto` | `auto`, `sparse_text` (labels, signs, receipts), `single_block`, `single_line` |
| `minConfidence` | 0 | Drop words below this confidence |
| `preprocess` | `auto` | `auto` or `none` |
| `pdfMode` | `auto` | `auto`, `ocr` (always), `text` (text layer only) |
| `pdfDpi` | 300 | PDF render resolution for OCR |
| `maxPagesPerFile` | 0 | Cap pages per file (0 = all) |
| `datasetId`, `urlField`, `maxDatasetItems` | | Read links from a dataset |
| `requestHeaders` | | Headers for files behind a token (sent only to the file's own host) |
| `maxFileSizeMb`, `maxConcurrency` | 50, 2 | Limits |

Languages: eng, deu, fra, spa, ita, por, nld, tur, pol, swe, rus, ukr, ara, chi_sim, chi_tra, jpn, kor, hin, vie, ind, ces, ron, hun, dan, nor, fin, ell, heb, tha, bul, hrv, slk, slv, lit, lav, est, msa, ben, tam, fas, urd, srp, cat. Two-letter codes (`tr`, `de`, `zh`) and English names (`Turkish`) are understood.

### Output

One row per image, image frame or PDF page: `url`, `fileName`, `format`, `page`, `totalPages` (PDFs), `method` (`ocr` or `text-layer`), `text`, `markdown`, `confidence`, `lowConfidence`, `wordCount`, `chars`, `width`, `height`, `language`, and `lines` / `words` when requested. A file that fails gives one row with `error` and is not charged. Export as JSON, CSV, Excel or through the API.

### What it costs

| Run | Price |
|---|---|
| 1,000 screenshots | $1.50 |
| 200 scanned pages | $0.30 |
| 50 PDFs of 20 pages with a text layer | $0.50 |
| A 400-page scan, capped with `maxPagesPerFile: 20` | $0.03 |

You only pay for pages you get. The run stops cleanly at your spending limit and returns the pages that were paid for.

### Good to know

- This is OCR, not image understanding: printed text, screenshots and clean scans read well; handwriting, very small text, heavy skew and low contrast read poorly. Check `confidence`.
- Set the right language: it is the biggest single factor for accuracy, especially for accented letters (Turkish ş, ğ, ı) and non-Latin scripts.
- Typical speed is under a second per image or scanned page.
- Private and internal addresses (localhost, 10.x, 192.168.x, cloud metadata) are blocked, including every redirect.
- Files behind a login are not supported: the link must download the file directly, or use a pre-signed URL or `requestHeaders`.
- You are responsible for reading files you have the right to use.

### FAQ

**Can an AI agent use it?** Yes, through the Apify MCP server: the agent passes image links and gets text back.

**How do I OCR files I have locally?** Upload them anywhere that gives a direct download link (an Apify key-value store, S3, a public bucket) and pass the links.

**I need Word, PowerPoint or Excel files too, or tables as Markdown tables.** Use [Document to Markdown](https://apify.com/saliga/document-to-markdown): it converts every common document format to Markdown and uses the same OCR for scanned pages.

### More from this developer

- [Document to Markdown](https://apify.com/saliga/document-to-markdown): PDF, Word, PowerPoint, Excel and scans to clean Markdown for LLMs and RAG, $2 per 1,000 pages.
- [Bulk HTTP Requests](https://apify.com/saliga/bulk-http-requests): call any API once for every row of a CSV, JSON list or Apify dataset.
- [Website Screenshot + Change Detection](https://apify.com/saliga/website-screenshot-change-detection): screenshots of many URLs on desktop and mobile, with visual change detection between runs.
- [Audio to Text](https://apify.com/saliga/audio-to-text): audio, video and podcast feeds to timestamped text, SRT/VTT subtitles and RAG chunks with Whisper.
- [Video to Scenes](https://apify.com/saliga/video-to-scenes): scene cuts, one keyframe per scene, a contact sheet and the transcript of each scene.
- [Career Site Jobs](https://apify.com/saliga/career-site-jobs): open jobs from Greenhouse, Lever, Ashby, Workable, Recruitee and Workday career sites.
- [Keyword Search Volume](https://apify.com/saliga/keyword-search-volume): Google search volume, CPC and trends for a keyword list, in up to 20 countries in one run.
- [Sitemap Extractor](https://apify.com/saliga/sitemap-extractor): every URL a website lists in its sitemaps, with lastmod, images and hreflang, $0.25 per 1,000 URLs.
- [Tech Stack Detector](https://apify.com/saliga/tech-stack-detector): which CMS, ecommerce platform, analytics and hosting a website uses, $1.50 per 1,000 websites.

Code examples (curl, Python, Node.js, n8n) for this and the other Actors: [github.com/msalihk/apify-actor-examples](https://github.com/msalihk/apify-actor-examples).

Questions or a missing feature: open an issue on the Actor page.

# Actor input Schema

## `imageUrls` (type: `array`):

Links to images (PNG, JPEG, TIFF, WebP, GIF, BMP) or PDFs. One result per image, per frame of a multi-page TIFF, and per PDF page.

## `languages` (type: `string`):

Language(s) of the text. Combine with + or commas, for example eng+tur or "en, de". Codes: eng, deu, fra, spa, ita, por, nld, tur, pol, swe, rus, ukr, ara, chi_sim, chi_tra, jpn, kor, hin, vie, ind, ces, ron, hun, dan, nor, fin, ell, heb, tha, bul, hrv, slk, slv, lit, lav, est, msa, ben, tam, fas, urd, srp, cat. Two-letter codes and English names also work.

## `outputDetail` (type: `string`):

Text: text and Markdown only. Lines: also every line with its confidence and box. Words: also every word with its confidence and box (x, y, w, h in pixels of the original image; for PDF pages, of the page rendered at the PDF DPI).

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

How the text is arranged. Auto suits documents and screenshots. Sparse text finds scattered text (labels, signs, receipts). Single block for one column of text, single line for a one-line crop.

## `minConfidence` (type: `integer`):

Drop recognised words below this confidence from the text. 0 keeps everything. Every page still reports its average confidence and a lowConfidence flag (under 60).

## `preprocess` (type: `string`):

Auto: grayscale, enlarge small images and stretch contrast before OCR (better on screenshots and thumbnails). None: use the image as it is.

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

Auto: read a page from its text layer when it has one (exact and cheaper), otherwise with OCR. OCR: OCR every page. Text: text layer only, no OCR.

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

Resolution used to render scanned PDF pages for OCR. 300 is right for most scans; lower is faster.

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

Stop after this many pages or frames of each file (0 = all). Caps the cost of long PDFs.

## `datasetId` (type: `string`):

ID of an Apify dataset, for example the output of a scraper run.

## `urlField` (type: `string`):

Field with the image or PDF link in each dataset item. Use dots for nested fields (image.url). A list of links is read too.

## `maxDatasetItems` (type: `integer`):

Read at most this many items from the dataset.

## `requestHeaders` (type: `object`):

Extra HTTP headers for downloading, for example {"Authorization": "Bearer …"} for files behind a token. Sent only to the host of each URL, never to a redirect on another host. They are stored with the run input, so prefer short-lived tokens or pre-signed URLs.

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

Files larger than this are skipped with an error (free).

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

How many files are downloaded and read in parallel. Raise it together with memory for big batches.

## Actor input object example

```json
{
  "imageUrls": [
    "https://raw.githubusercontent.com/tesseract-ocr/tessdoc/main/images/eurotext.png"
  ],
  "languages": "eng",
  "outputDetail": "text",
  "layout": "auto",
  "minConfidence": 0,
  "preprocess": "auto",
  "pdfMode": "auto",
  "pdfDpi": 300,
  "maxPagesPerFile": 0,
  "urlField": "url",
  "maxDatasetItems": 1000,
  "requestHeaders": {},
  "maxFileSizeMb": 50,
  "maxConcurrency": 2
}
```

# Actor output Schema

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

One row per image or PDF page: text, confidence and any error.

## `fullResults` (type: `string`):

Every field, including Markdown, image size, and lines and words with boxes when requested.

# 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 = {
    "imageUrls": [
        "https://raw.githubusercontent.com/tesseract-ocr/tessdoc/main/images/eurotext.png"
    ],
    "requestHeaders": {}
};

// Run the Actor and wait for it to finish
const run = await client.actor("saliga/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 = {
    "imageUrls": ["https://raw.githubusercontent.com/tesseract-ocr/tessdoc/main/images/eurotext.png"],
    "requestHeaders": {},
}

# Run the Actor and wait for it to finish
run = client.actor("saliga/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 '{
  "imageUrls": [
    "https://raw.githubusercontent.com/tesseract-ocr/tessdoc/main/images/eurotext.png"
  ],
  "requestHeaders": {}
}' |
apify call saliga/image-to-text-ocr --silent --output-dataset

```

## MCP server setup

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