# Bulk OCR: Images and Scanned PDFs to Text (`venture_ace/bulk-ocr`) Actor

- **URL**: https://apify.com/venture_ace/bulk-ocr.md
- **Developed by:** [Venture Ace](https://apify.com/venture_ace) (community)
- **Categories:** AI, Automation
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-usage

## 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

## Bulk OCR: images and scanned PDFs to text

OCR for lists of image and PDF URLs, using Tesseract (Apache-2.0) on CPU, with pypdfium2 (Apache-2.0/BSD) rendering PDF pages and Pillow for images.

- Input: `files` (direct URLs, or keys in the run's key-value store), `languages` (default `eng`; also deu, fra, spa, ita, por, nld), `minConfidence` (0.5), `maxPages` (200, hard cap 1,000), `pdfDpi` (200), `outputFormats` (`text`, `lines`, `markdown`).
- Output: one dataset row per page: `source, page, status, error, language, confidence, text, width, height` (plus `lines` and `markdown` if asked). Status is `ok`, `no_text`, `unreadable` (mean word confidence under `minConfidence`), `too_big` or `fetch_failed`.
- One bad file (dead link, not an image, page URL, too large, corrupt PDF) gives a row with the reason; the run continues and succeeds. Nothing is kept after the run beyond the dataset you asked for.
- Limits: 100 MB per file, 60 s per download and 60 s per page, 60 megapixels per page. Localhost, private, link-local and CGNAT addresses are refused on every request and redirect hop.
- It is OCR, not vision: it reads printed text; handwriting, low-resolution scans and skewed photos read poorly. No tables, no layout analysis; `markdown` is paragraphs only. Every PDF page is rendered and read; a PDF text layer is not used.
- Confidence is Tesseract's own mean word confidence, not a measure of accuracy: a blurred page can score high and still drop words or misread figures.
- Speed: one page at a time, about 2.5 s per page on the platform at 2 GB, so a 200-page run takes about 8 minutes. Up to 1,000 files per run (extra files are skipped with a log line).
- Pricing: pay per usage of the platform (no actor fee yet). A page that is read but comes back `unreadable` or `no_text` still uses compute.

Local test: from this folder with Pillow, pypdfium2, httpx, apify and tesseract-ocr installed, `python -m unittest src.test_guard src.test_ocr`.

# Actor input Schema

## `files` (type: `array`):

Direct links to images (png, jpg, tiff, bmp, webp, gif) or PDFs, one per line, max 100 MB each. A line that is not a URL is read as a key in this run's default key-value store. Localhost and private addresses are refused, redirects included. Web-page links are refused with a per-file status.

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

Tesseract languages to read with (several are combined). Installed: English, German, French, Spanish, Italian, Portuguese, Dutch.

## `minConfidence` (type: `number`):

A page whose mean word confidence is below this is returned with status unreadable instead of ok.

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

Hard cap on pages read in this run (images count as one page). Pages past the cap get a too_big row and are not read.

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

Pages of a PDF are rendered at this resolution before reading. Higher reads small print better and runs slower.

## `outputFormats` (type: `array`):

Plain text is the default. Add lines (text, confidence and box per line) or markdown (text split into paragraphs).

## Actor input object example

```json
{
  "languages": [
    "eng"
  ],
  "minConfidence": 0.5,
  "maxPages": 200,
  "pdfDpi": 200,
  "outputFormats": [
    "text"
  ]
}
```

# Actor output Schema

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

One row per page: status, confidence, text, optional lines and markdown.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("venture_ace/bulk-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("venture_ace/bulk-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 '{}' |
apify call venture_ace/bulk-ocr --silent --output-dataset

```

## MCP server setup

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