# Batch Image Text OCR Extractor (`automation-lab/batch-image-text-ocr`) Actor

Run batch image OCR on supplied images and export text, reading-order blocks, word boxes, confidence, dimensions, and source provenance.

- **URL**: https://apify.com/automation-lab/batch-image-text-ocr.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (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 $1.81 / 1,000 image extracteds

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Batch Image Text OCR Extractor

Turn batches of supplied images into structured OCR records without an OCR API key. This **batch image OCR** Actor returns complete text, reading-order paragraph blocks, pixel bounding boxes, confidence, dimensions, language, status, and source provenance.

Use it for recurring document ingestion, searchable archives, RAG pipelines, accessibility workflows, receipt queues, screenshot indexing, and human-review systems. It accepts anonymous direct image URLs and base64/data-URI images supplied in Actor input.

### What does Batch Image Text OCR Extractor do?

For each image, the Actor:

1. validates the input and public URL destination;
2. downloads or decodes up to 20 MB of image data;
3. validates the image and reads its pixel dimensions;
4. recognizes text with a local Tesseract model;
5. orders paragraph blocks and optional words;
6. exports one typed dataset row with provenance.

OCR runs inside the Actor. Images are not sent to a third-party OCR API.

### Who is it for?

- **Document and RAG teams** turning scans or screenshots into indexable text.
- **Operations teams** processing recurring receipts, labels, forms, or evidence images.
- **Publishers and researchers** making image-only text searchable.
- **Developers** needing stable JSON coordinates instead of plain copied text.
- **QA and accessibility teams** checking visible strings in screenshots.

### Why use structured batch image OCR?

Plain OCR text loses layout and origin. This Actor keeps:

- input order and image identity;
- original anonymous URL when applicable;
- paragraph reading order;
- paragraph and optional word bounding boxes;
- confidence at image, block, and word level;
- image dimensions and recognition language;
- explicit `succeeded`, `no_text`, or `failed` status.

A failed image does not discard results from other images. If every image fails or contains no readable text, the Actor fails clearly after writing diagnostic rows.

### Supported image inputs

Use `startUrls` for directly downloadable public HTTP(S) images. The server must return an `image/*` content type without login or cookies.

Use `inlineImages` for bytes you already have. Each entry needs a filename and either raw base64 or an `image/...;base64,...` data URI. Inline input is useful for files stored by a previous automation step.

Supported decoding depends on Sharp and Tesseract and commonly includes PNG, JPEG, WebP, TIFF, BMP, and non-animated GIF images. Each image is limited to 20 MB.

### Input parameters

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `startUrls` | array | — | Anonymous direct public image URLs. |
| `inlineImages` | array | — | Objects with `name` and base64/data-URI `data`. |
| `language` | string | `eng` | OCR model: `eng`, `deu`, `fra`, `spa`, `ita`, `por`, or `nld`. |
| `includeWords` | boolean | `false` | Include ordered words, confidence, and word boxes. |
| `maxImages` | integer | `25` | Process at most 1–100 supplied images. |

Provide at least one URL or inline image. URLs and inline images are processed in that order.

### Getting started

1. Open the Actor input page.
2. Add one or more direct image URLs under **Image URLs**.
3. Select the language visible in the images.
4. Enable word details if your workflow needs token-level boxes.
5. Set a batch limit.
6. Start the run and open the **OCR results** dataset view.
7. Export the rows as JSON, CSV, Excel, XML, or RSS through Apify Dataset tooling.

Example input:

```json
{
  "startUrls": [
    { "url": "https://placehold.co/900x250/png?text=Invoice+ACME+Total+123.45" }
  ],
  "language": "eng",
  "includeWords": true,
  "maxImages": 1
}
```

### Output fields

| Field | Meaning |
| --- | --- |
| `imageIndex` | One-based source order. |
| `imageName` | Supplied name or URL filename. |
| `sourceUrl` | Original URL, or `null` for inline bytes. |
| `sourceType` | `url` or `inline`. |
| `status` | `succeeded`, `no_text`, or `failed`. |
| `text` | Complete recognized text in reading order. |
| `confidence` | Overall Tesseract confidence from 0 to 100. |
| `language` | Model used for recognition. |
| `width`, `height` | Image dimensions in pixels. |
| `blockCount` | Number of paragraph blocks. |
| `blocks` | Ordered text blocks, confidence, boxes, and optional words. |
| `error` | Concise per-image error, otherwise `null`. |
| `processedAt` | ISO 8601 processing time. |

Coordinates use source-image pixels. Each box contains `x`, `y`, `width`, and `height`.

### Output example

This is abbreviated from a real local run of the example input:

```json
{
  "imageIndex": 1,
  "imageName": "png",
  "sourceUrl": "https://placehold.co/900x250/png?text=Invoice+ACME+Total+123.45",
  "sourceType": "url",
  "status": "succeeded",
  "text": "Invoice ACME Total 123.45",
  "confidence": 95,
  "language": "eng",
  "width": 900,
  "height": 250,
  "blockCount": 1,
  "blocks": [{
    "order": 1,
    "text": "Invoice ACME Total 123.45",
    "confidence": 96,
    "boundingBox": { "x": 183, "y": 108, "width": 534, "height": 34 }
  }],
  "error": null,
  "processedAt": "2026-09-10T14:31:20.214Z"
}
```

### How much does it cost to extract text from images?

Pay-per-event pricing has a **$0.001 start fee** and charges only an image that produces readable text. Failed downloads and no-text images have no image event charge.

| Plan | Price per successful image |
| --- | ---: |
| Free | $0.003473 |
| Bronze | $0.00302 |
| Silver | $0.0023556 |
| Gold | $0.001812 |
| Platinum | $0.001812 |
| Diamond | $0.001812 |

At Bronze rates, 10 successful images cost about **$0.0312**, 100 cost about **$0.303**, and 1,000 cost about **$3.021**, including one start. Actual charges follow successful charge events shown on the run.

### Document ingestion and RAG workflow

A practical pipeline is:

1. store or discover authorized image assets;
2. run this Actor with stable source order;
3. keep `sourceUrl`, `imageIndex`, and bounding boxes as citations;
4. split `blocks[].text` for embeddings;
5. retain confidence for review thresholds;
6. send low-confidence blocks to human review;
7. index accepted text in your search or vector store.

Enable `includeWords` only when word-level highlights or citations justify the larger output.

### Scheduling and integrations

Use Apify Schedules for recurring authorized image batches. Connect dataset output to webhooks, Make, Zapier, Google Sheets, cloud storage, or your own ingestion service.

Inputs are stateless: the Actor does not compare historical runs or emit change alerts. Build comparisons downstream using stable provenance and your own asset IDs or filenames.

### Run from the Apify API

Replace `YOUR_TOKEN` with an Apify token.

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~batch-image-text-ocr/runs?token=YOUR_TOKEN&waitForFinish=300" \
  -H "Content-Type: application/json" \
  -d '{"startUrls":[{"url":"https://placehold.co/900x250/png?text=Invoice+Total+123.45"}],"language":"eng"}'
```

Fetch results using the run's `defaultDatasetId`:

```bash
curl "https://api.apify.com/v2/datasets/DATASET_ID/items?clean=true&format=json"
```

### JavaScript client

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/batch-image-text-ocr').call({
  startUrls: [{ url: 'https://placehold.co/900x250/png?text=Invoice+Total+123.45' }],
  language: 'eng',
  includeWords: true,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items[0].text);
```

### Python client

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run = client.actor("automation-lab/batch-image-text-ocr").call(run_input={
    "startUrls": [{"url": "https://placehold.co/900x250/png?text=Invoice+Total+123.45"}],
    "language": "eng",
    "includeWords": True,
})
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items[0]["text"])
```

### Use with Apify MCP

**Claude Code setup:** add the Actor with:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/batch-image-text-ocr"
```

**Claude Desktop setup:** add this remote server configuration. **Cursor setup** and **VS Code setup** use the same MCP server URL in their MCP settings:

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

Example prompts:

- “Extract ordered text blocks and confidence from these authorized image URLs.”
- “OCR this invoice batch and return word boxes for highlighting.”
- “Prepare these screenshot texts and provenance for a RAG index.”

### Reliability, security, and limits

- URL fetches reject credentials, localhost, private, link-local, and non-HTTP(S) destinations.
- Redirect destinations are checked before requesting them.
- Downloads retry transient network, 429, and selected 5xx failures at most twice.
- Images are processed serially to keep OCR memory stable.
- The hard input cap is 100 images, 20 MB per image, and 40 megapixels per image.
- OCR quality depends on resolution, contrast, orientation, typeface, handwriting, and language.
- The Actor does not perform translation, PDF rendering, handwriting specialization, table reconstruction, or semantic document classification.

### Legality and responsible use

Process only images you are authorized to access and use. Respect copyrights, confidentiality, contractual restrictions, and applicable privacy laws. Avoid placing sensitive image bytes directly in reusable public Tasks. This Actor does not bypass authentication or access controls.

### Troubleshooting

**Why did a URL fail with “did not return an image”?**

The URL may point to an HTML viewer rather than the image file. Use the direct asset URL that returns an `image/*` content type, or provide authorized bytes through `inlineImages`.

**Why is text missing or confidence low?**

Use a higher-resolution, upright image with stronger contrast. Confirm the selected language. Tesseract is optimized for printed text; stylized or handwritten text may need a specialized model.

**Why did the whole run fail after writing rows?**

No image produced readable text. Inspect each row's `status` and `error`; correct the URLs, bytes, language, or image quality, then rerun.

### FAQ

**Does it require an external OCR key?** No. Recognition runs locally with Tesseract.js.

**Can I process mixed languages?** Use one dominant supported language per run. Split mixed-language batches into separate runs for better accuracy.

**Are word boxes always returned?** No. Set `includeWords` to `true`; paragraph blocks are always returned when detected.

**Does it resize, crop, compress, or translate images?** No. Those related search phrases were not adopted because this Actor only extracts OCR data.

**Can I OCR PDFs?** Not directly. Render authorized PDF pages to images first, then supply those image files or URLs.

### Related scrapers

- [Bulk Image Format Converter](https://apify.com/automation-lab/bulk-image-format-converter) can normalize authorized image formats before OCR.
- [Bulk Image Compressor](https://apify.com/automation-lab/bulk-image-compressor) can reduce large image assets before a downstream workflow.
- [Web Image Downloader](https://apify.com/automation-lab/web-image-downloader) can collect authorized direct image assets and provenance.

These are separate automation-lab products; they are not automatically invoked by this Actor.

# Actor input Schema

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

Anonymous direct HTTP(S) URLs returning PNG, JPEG, WebP, TIFF, BMP, or GIF images. Private-network and authenticated URLs are rejected.

## `uploadedImage` (type: `string`):

Upload one image with Apify's file picker. Use URLs or inline data for larger batches.

## `inlineImages` (type: `array`):

Optional images supplied as base64 or image data URIs. Each image requires a name and data value; each decoded image is limited to 20 MB and 40 megapixels.

## `language` (type: `string`):

Language model used to recognize text.

## `includeWords` (type: `boolean`):

Include every recognized word with reading order, confidence, and bounding box inside each text block.

## `maxImages` (type: `integer`):

Maximum number of supplied images processed in this run.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://placehold.co/900x250/png?text=Invoice+ACME+Total+123.45"
    },
    {
      "url": "https://dummyimage.com/900x250/ffffff/000000.png&text=Safety+Inspection+Passed"
    }
  ],
  "inlineImages": [],
  "language": "eng",
  "includeWords": false,
  "maxImages": 25
}
```

# Actor output Schema

## `overview` (type: `string`):

Default dataset items displayed through the OCR results overview.

# 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://placehold.co/900x250/png?text=Invoice+ACME+Total+123.45"
        },
        {
            "url": "https://dummyimage.com/900x250/ffffff/000000.png&text=Safety+Inspection+Passed"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/batch-image-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 = { "startUrls": [
        { "url": "https://placehold.co/900x250/png?text=Invoice+ACME+Total+123.45" },
        { "url": "https://dummyimage.com/900x250/ffffff/000000.png&text=Safety+Inspection+Passed" },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/batch-image-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 '{
  "startUrls": [
    {
      "url": "https://placehold.co/900x250/png?text=Invoice+ACME+Total+123.45"
    },
    {
      "url": "https://dummyimage.com/900x250/ffffff/000000.png&text=Safety+Inspection+Passed"
    }
  ]
}' |
apify call automation-lab/batch-image-text-ocr --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/batch-image-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/CvIkw58Z1rYlzmheE/builds/TxXmzRnn0HYc8J31N/openapi.json
