# Image to Text OCR: Arabic, Scans and Screenshots (`leadharbor/image-text-ocr`) Actor

Read text from image links or base64: screenshots, scans and photos (receipt photos read at lower confidence and are flagged for review). English and Arabic built in, line boxes, a quality report per image. No browser. Empty and unreliable images are not charged.

- **URL**: https://apify.com/leadharbor/image-text-ocr.md
- **Developed by:** [Mohammed Samaan](https://apify.com/leadharbor) (community)
- **Categories:** AI, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$5.00 / 1,000 image reads

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: Arabic, Scans and Screenshots

Give this Actor image links, or the images themselves as base64 text, and get back the text in reading order, a box for every line, and a quality report that tells you which results to look at. Screenshots, scanned pages and pictures with Arabic or mixed Arabic and English text are the target; phone photos of paper (receipts, for example) are read too, at a lower confidence, and are flagged for review. English and Arabic are built in, and Arabic comes back in the right reading order.

It was built for people who feed images to a document pipeline, a search index or an LLM, and who need two things most OCR services do not give: a signal for which results are shaky, and a guarantee in the pricing that images with nothing readable on them cost nothing. Everything runs over plain HTTP inside the Actor: no browser, no proxy, no login, and your images are not sent to any other service.

### What you get

- One dataset row per image with `text`, `lines` (each with a confidence and a box on **your** picture, even when the picture had to be turned or cropped before reading), optional `blocks` and word boxes, `wordCount`, `format`, `width`, `height` and `warnings`.
- A **`quality` report on every image**: mean confidence, whether the picture was turned (`orientationCorrected`) or straightened (`skewCorrected`), whether the strong clean-up was used, the scale factor, the lines under 60 confidence, the result of a content check that does not use the engine's confidence, whether an unreliable image may be handwriting, how many lines mix right-to-left and left-to-right text, whether the language is experimental, and `needsReview`.
- **`needsReview` flags the conditions listed in the quality report. It does not guarantee that an image with `needsReview: false` was read correctly.** It is `true` when the mean confidence is under 70, when the text fails the content check (many tokens that cannot be words, words stuck together, many single characters, many tiny lines), when a quarter or more of the lines are under 60 confidence, when the image was judged unreliable, or when a reading attempt failed. It cannot see an error that produces plausible text, so read the images that matter.
- **You pay only for images that were read.** An image with no text, an image whose result is unreliable and an image that fails are never charged. The unreliable ones are still delivered, so you can look at them.
- **Turned and tilted pictures:** text that runs sideways or upside down is turned back (90, 180 or 270 degrees), and a picture tilted by more than half a degree (up to 7) is straightened. JPEG files with an EXIF orientation are shown the way the camera meant them to be.
- **Photos on a dark table:** a pale page on a dark background is cropped to the page before reading. The result says so and the boxes stay on the picture you sent.
- **Clean-up only when needed:** the picture is read as it is first. If that result is poor, it is read again after clean-up (contrast, speckle removal, adaptive black and white for shadows and low light, light text on dark is inverted), enlarged when the text is small, and turned when the result looks like garbage. At most 5 readings per image; the best one is delivered.
- **Right-to-left text:** Arabic (and other right-to-left scripts) comes back in logical order, first word first. A line that mixes Arabic and Latin words or numbers keeps each run in its own order. Tables whose columns are read one at a time are put back into rows.
- **Formats:** JPEG, PNG, WebP, GIF (first frame), BMP and TIFF (first page; only uncompressed TIFF was tested). Transparent backgrounds are put on white.
- **Share links:** Google Drive file links and Dropbox links are turned into direct download links. Any other link must point straight at the image.

### Sample input

```json
{
  "urls": ["https://upload.wikimedia.org/wikipedia/commons/b/bc/Grocery_Store_Receipt_in_Vienna.jpg"],
  "ocrLanguages": ["eng"],
  "outputFormat": "lines",
  "preprocess": "auto"
}
```

This is the prefilled input (a public-domain photo of a crumpled receipt). For Arabic or mixed Arabic and English use `"ocrLanguages": ["ara", "eng"]`. To send images without hosting them, put them in `imageBase64` as plain strings or data URLs:

```json
{ "imageBase64": ["data:image/png;base64,iVBORw0KGgo..."], "ocrLanguages": ["eng"] }
```

### Sample output

Real output of this Actor (a local run with a stand-in for the platform's billing call) on a generated test image of printed text. Only the first two lines are shown.

```json
{
  "schemaVersion": 1,
  "index": 0,
  "url": "https://example.org/q3-printed.png",
  "status": "ok",
  "format": "png",
  "width": 1242,
  "height": 278,
  "text": "Optical character recognition turns photographs of documents into text.\nInvoice 2026-1043 was issued on 21 October 2026 for 123.50 dollars.\nPayment is due within thirty days of the date shown above.\n\nQuestions about this invoice can be sent to the billing office.",
  "lines": [
    { "text": "Optical character recognition turns photographs of documents into text.", "confidence": 95.9, "bbox": { "x0": 32, "y0": 32, "x1": 1032, "y1": 62 }, "rtl": false },
    { "text": "Invoice 2026-1043 was issued on 21 October 2026 for 123.50 dollars.", "confidence": 95.9, "bbox": { "x0": 33, "y0": 86, "x1": 1014, "y1": 109 }, "rtl": false }
  ],
  "wordCount": 42,
  "quality": {
    "meanConfidence": 95.9,
    "orientationCorrected": 0,
    "skewCorrected": 0,
    "preprocessApplied": "none",
    "scaleFactor": 1,
    "lowConfidenceLines": [],
    "sanity": { "judged": true, "ok": true, "score": 1, "reasons": [] },
    "possibleHandwriting": false,
    "mixedDirectionLines": 0,
    "experimental": false,
    "needsReview": false,
    "warnings": []
  },
  "warnings": [],
  "error": null,
  "errorKind": null,
  "charged": true
}
```

An image with nothing on it keeps its place in the dataset, says so and is not charged (real output, shortened):

```json
{ "status": "no_text_found", "format": "png", "width": 900, "height": 600, "text": "", "lines": [], "wordCount": 0, "warnings": ["No text was found in the image."], "error": null, "errorKind": null, "charged": false }
```

An image that cannot be read says why and is not charged:

```json
{ "status": "error", "url": "https://example.org/login-page", "text": null, "error": "The link returned an HTML page, not an image (often a login, error or consent page).", "errorKind": "not_an_image", "charged": false }
```

### Input

| Field | Type | Default | What it does |
|---|---|---|---|
| `urls` | list of links | none | Public links to images. Up to 100 per run in total with `imageBase64`; 25 MB and 50 megapixels (20,000 pixels on a side) each. Duplicates are read once. A `data:` URL in this list is treated as base64 data. |
| `imageBase64` | list of **strings** | none | Images as plain base64 text or data URLs. Each item is a string, never an object. At least one of `urls` and `imageBase64` is required. A bad item becomes an error row for that item; the rest of the run is not affected. |
| `ocrLanguages` | list of codes, at most 3 | `["eng"]` | Language of the text, for example `["ara", "eng"]`, `["deu"]`, `["chi_sim"]`. Put the main language first. `eng` and `ara` are built in; any other official Tesseract `tessdata_fast` code is downloaded when needed and its output is marked experimental. An unknown code stops the run with a message naming it. |
| `outputFormat` | `lines`, `text`, `blocks`, `all` | `lines` | `text`: only `text`. `lines`: adds `lines` with confidence and box. `blocks`: adds `blocks` (paragraphs with boxes) and no lines. `all`: lines with a box and confidence for every word, and blocks. Fields you do not ask for are `null`. |
| `preprocess` | `auto`, `none`, `strong` | `auto` | `auto`: read as it is, clean up only when the result is poor. `none`: never clean up. `strong`: always clean up first. |

### Output fields

| Field | Meaning |
|---|---|
| `schemaVersion` | Always `1` for this layout. |
| `index` | Position of the image in your input after duplicates were removed. Rows are written as images finish, so sort by `index` to get input order. |
| `url`, `finalUrl`, `source` | The link you gave (`null` for base64 items), the address after redirects, and what the image came from: the direct link that was fetched (differs from `url` when a share link was rewritten) or `imageBase64[n]`. |
| `status` | `ok`, `no_text_found`, `unreliable`, `error`, `skipped_charge_limit` or `skipped_repeated_failures`. |
| `format`, `width`, `height`, `frames` | What the file is, its size in pixels (as shown, after any EXIF turn) and the number of frames or pages it holds. |
| `text` | All the text in reading order, one line per row, blocks separated by a blank line. A wide gap between neighbouring words (a table column) is written as two spaces. `null` on an error row, empty when nothing was found. |
| `lines` | `text`, `confidence` (0 to 100), `bbox` (`x0`, `y0`, `x1`, `y1` in pixels of the picture you sent), `rtl`; with `outputFormat: "all"` also `words` (each with its own box and confidence). |
| `blocks` | `index`, `text`, `bbox`, `lineIndexes`, `confidence`. |
| `wordCount`, `languages` | Words read, and the OCR languages used. |
| `quality` | `meanConfidence`, `orientationCorrected`, `skewCorrected`, `preprocessApplied` (`none` or `strong`), `scaleFactor` (above 1 enlarged, below 1 reduced), `lowConfidenceLines` (indexes of lines under 60), `sanity` (`judged`, `ok`, `score`, `reasons`), `possibleHandwriting`, `mixedDirectionLines`, `experimental`, `experimentalLanguages`, `needsReview`, `warnings`. |
| `warnings` | Anything uncertain or done to the picture: turned, straightened, cropped, low confidence, possible handwriting (unreliable images only), table or multi-column layout, animated GIF read from its first frame, a server that mislabelled the file, `billing_error`. |
| `error`, `errorKind` | Specific reason when `status` is `error`, else `null`. Kinds: `http_error`, `refused_address`, `download_failed`, `file_too_large`, `empty_file`, `not_an_image`, `unsupported_format`, `image_too_large_pixels`, `corrupt_image`, `invalid_base64`, `language_unavailable`, `ocr_unavailable`, `ocr_failed`, `conversion_timeout`, `conversion_memory`, `conversion_crashed`, `conversion_failed`, `charge_limit`, `circuit_open`, `unexpected_failure`. |
| `charged` | Whether the `image-read` event was billed for this image. |

A run summary (`SUMMARY` in the key-value store) counts images read, without text, unreliable, failed and skipped, and how many were billed.

### Pricing

Pay per event, one event:

| Event | Price | When |
|---|---|---|
| `image-read` | **$0.005** | each image that was read, produced text and passed the content check (status `ok`) |

Worked examples: 1,000 clean screenshots or scans cost $5.00. 100 images of which 12 are blank or unreadable cost 88 x $0.005 = $0.44. 20 images cost $0.10.

Not charged: links that fail (HTTP errors, refused addresses, files over 25 MB or 50 megapixels, damaged or non-image files), images with no text (`no_text_found`), and images whose text is unreliable (`unreliable`: delivered, flagged, not billed).

How billing works: an image is charged once, after it was read. If the charge fails (an error from the platform) the text is still delivered in full with a `billing_error` warning and `charged: false`, and the run carries on. If you set a maximum cost per run, the Actor stops when it is reached: the remaining images get a row with `status: "skipped_charge_limit"`, and an image that was read but could not be billed because the limit was reached is not delivered for free. If pay-per-event pricing is not active for a run, images are still delivered, `charged` stays `false`, and the run status says so once.

### Use with AI agents / MCP

Send this input:

```json
{ "urls": ["https://example.org/receipt.jpg"], "ocrLanguages": ["eng"], "outputFormat": "text" }
```

Read these fields from each dataset item:

1. `status` must be `ok`. If it is `error`, read `error` and `errorKind`. `no_text_found` means there was nothing to read; `unreliable` means text came back but should not be trusted.
2. `quality.needsReview`: when `true`, tell the user which `quality.lowConfidenceLines` or `warnings` to check before relying on the text. When `false`, the checks listed above found nothing; that is not proof the text is right.
3. `text` for the content; with `outputFormat: "lines"` use `lines[].bbox` to point at where a line is on the picture.

Images can also be sent as base64 strings in `imageBase64` (an array of strings).

### Benchmark

Settings: the Actor's **default input** (`preprocess: auto`, `outputFormat: lines`) with `ocrLanguages: ["ara", "eng"]` for the two Arabic files and `["eng"]` for the others, one image per run, 21 Oct 2026, the same code on Windows and in the Linux image (identical results). 17 files, so treat the numbers as a measurement of these files only. Character error rate (CER) compares the text with the exact text drawn into the image after lower-casing, removing punctuation and spaces, and folding Arabic diacritics and alef variants. The synthetic files are drawn by us with known text (Liberation Sans and Noto Naskh Arabic); the three photos are real, with key words read by eye.

| File | What it tests | Result |
|---|---|---|
| printed English, small text, low contrast, screenshot, low light, warped invoice on a table | clean-up, upscaling, cropping | CER 0 on all six |
| turned 90 and 180 degrees | orientation | CER 0 on both, turned back automatically |
| tilted 3.5 degrees with 3,000 specks of noise | skew and noise | CER 0.098, `needsReview: true` |
| three Arabic lines | Arabic accuracy and order | CER 0, word F1 1.0 |
| four mixed Arabic and English lines | line and word order | all 4 lines in order, words in order on 3 of 4 lines, CER 0.164 (one line came back as "VAT 6" plus a stray token), `needsReview: true` |
| a table of 5 rows | rows kept | rows in order, CER 0.123: the middle column was skipped by the engine on this file |
| crumpled phone photo of a receipt (13 megapixels, dark table) | real photo recall | local run: 29 of 32 key tokens, mean confidence 57.8, `needsReview: true`; on the Apify platform the same photo came back `unreliable` (confidence 60.9) and was not charged |
| receipt photo hanging on a tree (blurry, 640 x 480) | very poor photo | nothing read, not charged (0 of 4 words) |
| handwritten note | handwriting | nothing read, not charged (0 of 12 words). **Handwriting is not supported.** |
| blank white image, random noise image | empty input | no text, not charged |

Honest notes: the table result shows that a sparse column can be dropped while the confidence of what was read stays high, which is why a table or multi-column layout is flagged `needsReview` with a warning. The receipt reads at a low confidence, which is why it is flagged. Photos that Tesseract cannot read at all come back empty and free; they are not read badly and billed. Speed is in the Limits section.

### Limits

- **OCR quality depends on the picture.** The built-in models are Tesseract's fast LSTM models. Clean printed text reads very well; photographed receipts read at a lower confidence and the checks flag them; handwriting, strong blur, glare, curved or crumpled paper and very small print read badly or not at all. A result can pass every check and still contain errors, and a line can be missing without any warning, and a column can be dropped (tables and multi-column layouts are flagged for review, but the flag is a layout check, not proof).
- **Handwriting is not supported.** Only an image that fails the checks (status `unreliable`, not charged) carries the `possibleHandwriting` flag and the handwriting warning, and the flag is a guess. A charged read at low confidence says "check it by eye" and is flagged for review.
- **Orientation, skew, crop:** turning is tried only when the result looks like garbage or the text runs sideways; skew is measured up to 7 degrees; photos taken at an angle (perspective), mixed orientations on one picture and strongly curved pages are not corrected. The crop to a pale page assumes one page on a darker background; it reads such a page as a single column of lines, so a photographed page with two text columns is read less well.
- **Mixed-direction lines:** a line that holds both Arabic and Latin words looks the same whichever way it was written, so its word order is decided from the layout of the whole picture. The quality report counts these lines (`mixedDirectionLines`) and warns; check them.
- **Languages:** `eng` and `ara` ship with the Actor and were benchmarked. Every other language file (1 to 12 MB, official Tesseract `tessdata_fast`) is downloaded at the start of the run and its output is **experimental**: it was not benchmarked, `quality.experimental` is `true` and the row carries a warning. Up to 3 languages at once; more languages make OCR slower. Set the language you expect: Arabic text read with `["eng"]` can come back as unreliable.
- **Formats:** JPEG, PNG, WebP, GIF, BMP, TIFF. Not supported: HEIC, AVIF, SVG, ICO, PDF (use a PDF tool). An animated GIF (tested), WebP or PNG is flagged in the warnings and read as the decoder shows it (a GIF from its first frame), a multi-page TIFF from its first page, and the row says so. Files with an invalid header or a failed checksum are refused as `corrupt_image` before decoding, because the image decoder can crash on them; if it still stops on a damaged file, the row says the file is probably damaged.
- **Size:** 25 MB and 50 megapixels per image (20,000 pixels on a side). Larger pictures are refused with a message. Pictures are reduced to at most about 3,800 pixels on the long side for reading.
- **Speed and memory.** One image is read at a time per CPU core (the platform gives one core per 4 GB; the default memory is 2 GB, half a core), while the next images download. Measured in a Linux container with Node 20 at 0.5 core and 2 GB: 0.1 to 3 seconds for clean images, about 9 seconds for a 13 megapixel receipt photo, peak memory under 500 MB; at 1 core and 4 GB about half of those times. A reader is stopped if it reports no progress for 30 seconds before reading has started, or for 120 seconds (or 12 times its median step time, when longer) while reading, and in any case after 300 seconds; the image gets an error row and the run goes on. If 10 images in a row have to be stopped, the run ends early and the rest get `status: "skipped_repeated_failures"`.
- **Public links only.** The Actor does not log in, use cookies or fetch addresses on private networks. Only ports 80 and 443 are used.
- **Not supported:** layout analysis beyond lines, paragraphs and table rows, form fields, barcodes and QR codes, translating, describing pictures.

### FAQ

**Do I pay for images with no text?** No. Only status `ok` is charged: images with no text, unreliable results and failures are free.

**How do I know if a result can be trusted?** Read `quality.meanConfidence`, `quality.lowConfidenceLines`, `quality.sanity` and `quality.needsReview`. They flag the conditions described above. A `false` is not a guarantee.

**Can I send base64?** Yes: `imageBase64` takes plain base64 strings and data URLs. Every item is a string.

**Which languages?** English and Arabic are built in and benchmarked. Set `ocrLanguages` to another official Tesseract code (for example `fra`, `deu`, `spa`, `rus`, `chi_sim`, `hin`) and its language file is downloaded for the run; the result is marked experimental.

**Is the Arabic in the right order?** Arabic lines in the benchmark come back in logical order, so a search for the first word of a sentence finds the start of the sentence. Lines that mix Arabic with English words or numbers are ordered by a layout heuristic and counted in the quality report.

**Why did my photo come back as `unreliable` or empty?** The text could not be read reliably: blur, glare, handwriting or a language that was not set. Try a sharper picture, set `ocrLanguages`, or set `preprocess` to `strong`.

**Why do the boxes not match a turned photo?** They do: boxes are on the picture exactly as you sent it (with its EXIF orientation applied), even when the Actor turned, straightened or cropped it to read it.

### Related Actors

- [PDF to Markdown with OCR, Tables and RAG Chunks](https://apify.com/leadharbor/pdf-to-markdown-ocr): scanned and text PDFs to Markdown with the same OCR engine.
- [Document to Markdown for RAG](https://apify.com/leadharbor/document-to-markdown): text-layer PDFs plus DOCX, XLSX, HTML, CSV and text files, no OCR.

### Open-source credits

Licences were read from each package's and font's `LICENSE` file in the installed version, and the production tree of the Linux image was scanned with `tools/licence-scan.mjs`.

| Package or file | Version | Licence | Used for |
|---|---|---|---|
| tesseract.js | 7.0.0 | Apache-2.0 | OCR engine for Node |
| tesseract.js-core | 7.0.0 | Apache-2.0 | Tesseract compiled to WebAssembly |
| tessdata_fast (eng, ara, others on demand) | main, 21 Oct 2026 | Apache-2.0 (LICENSE of the repository) | OCR language models |
| @napi-rs/canvas and its Linux binary | 0.1.65 | MIT. The prebuilt binary is built on Google Skia (BSD-3-Clause upstream); the Linux binary package ships no licence or notice file, so the Skia notice is not included here | Decoding JPEG, PNG, WebP, GIF and BMP; turning, scaling and writing the pictures that are read |
| utif | 3.1.0 | MIT | Decoding TIFF |
| pako | 1.0.11 | MIT AND Zlib (the package's declared licence; its `LICENSE` file holds the MIT text) | Dependency of utif |
| apify | 3.7.2 | Apache-2.0 | Actor runtime |
| bmp-js, is-url, node-fetch, regenerator-runtime, zlibjs | 0.1.0, 1.2.4, 2.7.0, 0.13.11, 0.3.1 | MIT | Dependencies of tesseract.js |
| idb-keyval, wasm-feature-detect | 6.3.0, 1.9.0 | Apache-2.0 | Dependencies of tesseract.js |
| Liberation Sans and Noto Naskh Arabic fonts (test fixtures only, used to draw the benchmark and test images) | as shipped in pdfjs-dist 4.10.38 and an earlier fixture | SIL Open Font License 1.1 | Not used by the running Actor |

Scan of the shipped Linux image (`npm install --omit=dev`): 200 packages. Of those 142 are MIT, 22 Apache-2.0, 15 BSD-2-Clause, 8 ISC, 5 BSD-3-Clause, 2 (MIT OR CC0-1.0), 0BSD, BlueOak-1.0.0 (`sax`), (MIT AND Zlib) (`pako`), CC-BY-4.0 (`caniuse-lite` browser data inside the Apify SDK; attribution given here) and 2 with no licence field in their metadata (`map-stream`, `pause-stream`, inside the Apify SDK), which ship licence files. Ten packages declare a permissive licence but ship no licence file (the Skia binary above and nine others, mostly inside the Apify SDK tree). No GPL, AGPL, LGPL or OpenRAIL component is installed. The `sharp` image library (Apache-2.0) was not used because its prebuilt binaries bundle libvips under the LGPL.

# Actor input Schema

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

Public links to JPEG, PNG, WebP, GIF, BMP or TIFF images (up to 100 per run, links and base64 together, 25 MB and 50 megapixels each). Google Drive and Dropbox share links are turned into direct download links; any other link must point straight at the image. Links to private networks, localhost and pages that need a login are refused.

## `imageBase64` (type: `array`):

Images sent as text instead of links: each item is a plain base64 string, or a data URL such as data:image/png;base64,AAAA... Every item is a string (not an object). Counts towards the 100 images per run and the 25 MB limit per image.

## `ocrLanguages` (type: `array`):

Language codes of the text in the images, at most 3, for example \["eng"], \["ara", "eng"] or \["deu", "fra"]. English (eng) and Arabic (ara) are built in; every other official Tesseract 'tessdata_fast' language is downloaded when needed. List the main language first. Right-to-left text is returned in logical reading order. Only English and Arabic were benchmarked: any other language is marked experimental in the quality report.

## `outputFormat` (type: `string`):

'text' returns only the text. 'lines' (default) adds one entry per line with its confidence and a box on your picture. 'blocks' adds paragraphs with boxes. 'all' returns lines with a box and confidence for every word, and blocks. Fields you do not ask for are null.

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

'auto' reads the picture as it is and, only when the result is poor, again after clean-up (contrast, noise removal, adaptive black and white for photos, shadows and low light). 'none' never cleans up. 'strong' always cleans up first.

## Actor input object example

```json
{
  "urls": [
    "https://upload.wikimedia.org/wikipedia/commons/b/bc/Grocery_Store_Receipt_in_Vienna.jpg"
  ],
  "ocrLanguages": [
    "eng"
  ],
  "outputFormat": "lines",
  "preprocess": "auto"
}
```

# Actor output Schema

## `images` (type: `string`):

Table of images: status, words, confidence, review flag and warnings. Open a row to read the text and the line boxes.

## `summary` (type: `string`):

Counts of read, no-text, unreliable, failed and skipped images and how many were billed.

# 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://upload.wikimedia.org/wikipedia/commons/b/bc/Grocery_Store_Receipt_in_Vienna.jpg"
    ],
    "ocrLanguages": [
        "eng"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("leadharbor/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 = {
    "urls": ["https://upload.wikimedia.org/wikipedia/commons/b/bc/Grocery_Store_Receipt_in_Vienna.jpg"],
    "ocrLanguages": ["eng"],
}

# Run the Actor and wait for it to finish
run = client.actor("leadharbor/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 '{
  "urls": [
    "https://upload.wikimedia.org/wikipedia/commons/b/bc/Grocery_Store_Receipt_in_Vienna.jpg"
  ],
  "ocrLanguages": [
    "eng"
  ]
}' |
apify call leadharbor/image-text-ocr --silent --output-dataset

```

## MCP server setup

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