# Image to Text OCR — Bulk, 30 Languages, Lines (`sumitr_mardy/image-to-text-ocr`) Actor

Extract text from images by URL with OCR: screenshots, scans, photos, receipts. 30 languages, text plus lines with confidence and position. One flat row per URL, never null; only images with text are billed. PNG, JPEG, WebP, TIFF, GIF, BMP.

- **URL**: https://apify.com/sumitr_mardy/image-to-text-ocr.md
- **Developed by:** [Sumitr Mardy](https://apify.com/sumitr_mardy) (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 with texts

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

## Image to Text OCR — Bulk, 30 Languages, Lines

Give it image links, get back the text in each image. Screenshots, scanned pages, photos of documents, receipts, signs. 30 languages, plus every text line with its confidence and position when you want it. One flat row per URL, every field always present, never `null`, built to be called from pipelines, n8n/Make/Zapier flows and AI agents.

**$2 per 1,000 images with text, down to $1.50 on Gold. Images with no text, failed downloads and broken files are free.**

### Why this one

- **One row for every URL you send.** A 404, an HTML page, a PDF sent by mistake or a corrupt file still comes back as a row with `status: "failed"` and a machine-readable `subStatus`. You never have to diff your input against the output.
- **You pay for text, not for attempts.** An image is billed only when OCR found text in it.
- **Confidence you can filter on.** Every row has `meanConfidence` (0–100). Turn on `includeLines` to get each line with its own confidence and pixel box, in the original image's coordinates.
- **Images are prepared for OCR.** EXIF rotation is applied, transparent PNGs are flattened onto white, small screenshots are upscaled and huge photos scaled down to the size Tesseract reads best.
- **Fast.** On Apify at the default 2 GB: a 640×500 screenshot in 0.5–1 s, an 11-megapixel scan in 2.7 s, download included.

### Input

| Field | Default | What it does |
|---|---|---|
| `urls` | — | Image links, one per item: PNG, JPEG, WebP, TIFF, GIF (first frame), BMP, AVIF. Google Drive and Dropbox share links work. Required. |
| `ocrLanguage` | `eng` | Tesseract language code; join up to 3 with `+` (`eng+vie`). 30 built in, including `vie`, `chi_sim`, `chi_tra`, `jpn`, `kor`, `ara`, `hin`, `rus`, `tha`. |
| `layout` | `auto` | `auto` for documents and screenshots, `sparse` for text scattered over a photo (memes, signs, UI), `block` for one cropped paragraph. |
| `includeLines` | `false` | Adds `lines[]`: each line's text, confidence and box. |
| `maxFileSizeMb` | `20` | Larger files fail as `too_large`, uncharged (max 50). |
| `downloadTimeoutSecs` | `30` | Total time for one download. Slower URLs fail as `download_timeout`, uncharged. |
| `maxConcurrency` | `8` | Parallel downloads. OCR runs one image per CPU core (1 core per 4 GB). |
| `proxyConfiguration` | off | Optional. Hosts answering 401/403/429 are retried once through Apify's datacenter proxy anyway. |

```json
{
  "urls": ["https://raw.githubusercontent.com/tesseract-ocr/tessdoc/main/images/eurotext.png"],
  "ocrLanguage": "eng",
  "includeLines": true
}
```

### Output

One row per input URL, in completion order; `inputIndex` lines rows up with your input. A real row (shortened):

```json
{
  "inputIndex": 0,
  "url": "https://raw.githubusercontent.com/tesseract-ocr/tessdoc/main/images/eurotext.png",
  "resolvedUrl": "https://raw.githubusercontent.com/tesseract-ocr/tessdoc/main/images/eurotext.png",
  "status": "ok",
  "subStatus": "text_found",
  "error": "",
  "text": "The (quick) [brown] {fox} jumps!\nOver the $43,456.78 <lazy> #90 dog\n…",
  "charCount": 414, "wordCount": 66, "lineCount": 12,
  "meanConfidence": 91.4,
  "lines": [{ "text": "The (quick) [brown] {fox} jumps!", "confidence": 96.1, "left": 65, "top": 41, "width": 450, "height": 30 }],
  "language": "eng",
  "imageFormat": "png", "width": 640, "height": 500, "fileSizeBytes": 31481,
  "notes": [],
  "processingMs": 1027
}
```

Strings are `""` when absent, numbers `0`, arrays `[]`. No field is ever `null` or missing. `text` keeps reading order: lines are separated by `\n`, paragraphs by a blank line.

#### Status

| `status` | `subStatus` | Charged |
|---|---|---|
| `ok` | `text_found` | yes |
| `no_text` | `no_text_found`: the image was read and has no text | free |
| `failed` | `http_404` (any `http_<code>`), `not_an_image`, `unsupported_format` (HEIC), `too_large`, `download_timeout`, `download_error`, `corrupt_image`, `ocr_timeout`, `ocr_error`, `invalid_url`, `cost_limit_reached`, `free_work_limit` | free |

A PDF sent by mistake fails as `not_an_image` with a pointer to [PDF Text Extractor](https://apify.com/sumitr_mardy/pdf-text-extractor), which reads text layers and OCRs scanned pages.

### Pricing

| Event | Price | When |
|---|---|---|
| `image-ocr` | $2.00 per 1,000 (free plan), $1.80 Bronze, $1.60 Silver, $1.50 Gold | An image with text (`status: "ok"`) |

The only other charge is a start fee of $0.0005 per GB of memory per run ($0.001 at the default 2 GB). No platform usage is added on top. A run stops starting new images when your *maximum cost per run* is reached, and marks the rest `cost_limit_reached`, uncharged.

### Fair use

Failed and no-text rows are free, so a run that spends its compute on URLs that fail stops starting new ones. The rest come back as `failed` / `free_work_limit`, uncharged, and can be resubmitted. Ordinary lists with a share of dead links never get near this. Also: at most 3 OCR languages per run, a 50 MB file cap, 80 megapixels per image, datacenter proxy only.

### What it does not do (yet)

- **Tesseract, not a vision model.** Very good on screenshots, scans and photos of printed text (confidence 85–96 in our tests). Weaker on handwriting, heavy blur, curved or stylised text and very low resolution. Check `meanConfidence`: under ~60 the text is likely noisy.
- **No table structure.** Table rows come out as lines; use `includeLines` boxes to rebuild columns.
- **One frame only** for animated GIFs and multi-page TIFFs; `notes` says so. For multi-page documents use PDF Text Extractor.
- **No HEIC.** Convert iPhone photos to JPEG first.

### Use it from AI agents (MCP)

This Actor is a tool on the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp), so Claude, Cursor, VS Code or any MCP client can call it to read text in an image. Load only this tool: a short tool list keeps the agent from picking the wrong one.

**Recommended: hosted server, OAuth sign-in (nothing secret on disk).** Add to your MCP client config (`claude_desktop_config.json`, `.cursor/mcp.json`, …):

```json
{
  "mcpServers": {
    "image-ocr": {
      "url": "https://mcp.apify.com?tools=sumitr_mardy/image-to-text-ocr"
    }
  }
}
```

**With an API token instead** (clients without OAuth, or unattended setups):

```json
{
  "mcpServers": {
    "image-ocr": {
      "url": "https://mcp.apify.com?tools=sumitr_mardy/image-to-text-ocr",
      "headers": { "Authorization": "Bearer YOUR_APIFY_TOKEN" }
    }
  }
}
```

**Local server (stdio):**

```json
{
  "mcpServers": {
    "image-ocr": {
      "command": "npx",
      "args": ["-y", "@apify/actors-mcp-server@latest", "--tools", "sumitr_mardy/image-to-text-ocr"],
      "env": { "APIFY_TOKEN": "YOUR_APIFY_TOKEN" }
    }
  }
}
```

The hosted server also gives the agent this Actor's output schema, so it knows every field before the first call. `apify mcp install <client>` writes the config for you if you use the Apify CLI. Get a token at [Apify Console → API & Integrations](https://console.apify.com/settings/integrations), and never commit it.

**Tips for agents:**

- *"What does this screenshot say?"*: pass the link in `urls`, read `text`. Check `status` first; `no_text` and `failed` rows carry the reason in `subStatus` and `error`.
- Non-English text: set `ocrLanguage` (`vie`, `jpn`, `chi_sim`…). The wrong language shows up as a low `meanConfidence`.
- Text on a photo (a sign, a product label, a meme): `layout: "sparse"`.
- Need positions (to highlight, crop or rebuild a table)? `includeLines: true`.
- The MCP `call-actor` tool waits up to 45 seconds; a few images finish well inside that.

### Use it from code

```python
from apify_client import ApifyClient

client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("sumitr_mardy/image-to-text-ocr").call(run_input={
    "urls": ["https://raw.githubusercontent.com/tesseract-ocr/test/main/testing/phototest.tif"],
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(row["status"], row["meanConfidence"], row["text"][:80])
```

```bash
curl -X POST "https://api.apify.com/v2/acts/sumitr_mardy~image-to-text-ocr/run-sync-get-dataset-items?token=<APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://raw.githubusercontent.com/tesseract-ocr/tessdoc/main/images/eurotext.png"]}'
```

# Actor input Schema

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

Direct links to images, one per item: PNG, JPEG, WebP, TIFF, GIF (first frame), BMP or AVIF. Google Drive and Dropbox share links are converted to direct downloads. Each URL gives exactly one output row.

## `ocrLanguage` (type: `string`):

Tesseract language code of the text in the images; join up to 3 with + (e.g. "eng+vie"). Available: eng, fra, deu, spa, por, ita, nld, pol, ces, ron, hun, swe, dan, nor, fin, tur, rus, ukr, ell, vie, ind, msa, tha, chi_sim, chi_tra, jpn, kor, ara, heb, hin.

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

How the text is laid out. Auto works for most documents and screenshots; sparse finds scattered words that auto skips; block is best for one cropped paragraph.

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

Add lines\[]: every text line with its confidence (0-100) and pixel box (left, top, width, height) in the original image. Useful for receipts, tables and highlighting.

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

Larger files fail as too_large, uncharged.

## `downloadTimeoutSecs` (type: `integer`):

Total time allowed for one download. Slower URLs fail as download_timeout, uncharged.

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

Images downloaded at once. OCR itself runs one image per CPU core (1 core per 4 GB of memory).

## `proxyConfiguration` (type: `object`):

Optional proxy for downloads. Hosts that answer 401/403/429 are retried once through the Apify datacenter proxy automatically. Residential is not supported.

## Actor input object example

```json
{
  "urls": [
    "https://raw.githubusercontent.com/tesseract-ocr/tessdoc/main/images/eurotext.png",
    "https://raw.githubusercontent.com/tesseract-ocr/test/main/testing/phototest.tif"
  ],
  "ocrLanguage": "eng",
  "layout": "auto",
  "includeLines": false,
  "maxFileSizeMb": 20,
  "downloadTimeoutSecs": 30,
  "maxConcurrency": 8,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# 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": [
        "https://raw.githubusercontent.com/tesseract-ocr/tessdoc/main/images/eurotext.png",
        "https://raw.githubusercontent.com/tesseract-ocr/test/main/testing/phototest.tif"
    ]
};

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

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

```

## MCP server setup

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