# Image to Text OCR API: Extract Text from Images in Bulk (`rowcraft/image-ocr-text-extractor`) Actor

Read the text in images given by link or found on web pages, with a local OCR engine and no third-party API. Cleaned text, confidence and image size per row, robots.txt obeyed, and you pay only for images where text was found.

- **URL**: https://apify.com/rowcraft/image-ocr-text-extractor.md
- **Developed by:** [Rowcraft](https://apify.com/rowcraft) (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 $2.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.
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 API: Extract Text from Images in Bulk

Turn a list of **images** into clean text. Give the actor links to image files, or links to web pages that show images; it downloads each picture once, politely, reads the text in it with a local OCR engine and returns one row per image: the text, the mean confidence, the language, the number of words and lines, and the real size of the file, as JSON, CSV or Excel.

The OCR engine is Tesseract, an open-source engine, running inside your own run. No picture is sent to a third-party OCR service, you need no API key, and you pay only for the images in which text was found: a photo without text, a tiny icon, a file that cannot be read or a page that refuses robots costs nothing.

![Input form of Image to Text OCR API: Extract Text from Images in Bulk](https://api.apify.com/v2/key-value-stores/7hbQFQdGq61v2OZ2h/records/image-ocr-text-extractor-input.png)

### What you get

One row per image **that was read**:

- **Where it came from:** the address of the image (`imageUrl`), the page it was found on (`pageUrl`, null for a direct link) and how it was found (`source`: `direct`, `img`, `srcset`, `picture` or `og`)
- **The text:** `text`, with one line of the picture per line, spaces cleaned and empty lines collapsed; at most 100,000 characters per image
- **How sure the engine is:** `meanConfidence` from 0 to 100 for the whole image and, if you switch it on, the confidence of each line in `lines`
- **How much text:** `lineCount`, `wordCount` and `characterCount`
- **Which languages were used:** `languages`, the codes of the languages you asked for
- **The file:** width and height in pixels, format (`png`, `jpeg`, `gif`, `webp`, `bmp` or `tiff`), size in bytes and the SHA-1 of the file, all read from the file itself
- **Status:** `textFound` (true = charged), a `note` that says why when no text was returned, `ocrMs` (milliseconds the engine needed) and `checkedAt`

The run also writes two records in the key-value store: `COVERAGE` (one line per address you gave and one per image that was **not** read, with the reason) and `RUN_SUMMARY` (counts, words delivered, the filters used, memory and why the run stopped).

### Why this actor

- **You pay only when text was found.** An image counts as "text found" when the engine reads at least a few letters or digits and its mean confidence reaches your minimum (40 by default). A photo of a landscape, a blank page or a blurred picture comes back as a free row with `textFound = false`, so you can see which pictures held nothing.
- **Cheap per image.** $3.00 per 1,000 images on the Free plan and $2.00 per 1,000 on the Business plan. One of the most used image-OCR actors in the Store lists $5.00 per 1,000 images at its Free price (checked on 18 October 2026).
- **A local engine, no third party.** Tesseract runs inside the run. The pictures are not uploaded anywhere, there is no per-call fee from another service and no rate limit from one.
- **Pictures from pages, not only files.** Paste a page address and the actor finds the images the page shows (`img`, `srcset`, `picture`, the `og:image` preview and the lazy-loading attributes many shops use), skips tracking pixels and icons with a size filter, and reads the rest.
- **16 languages, together.** English, German, French, Spanish, Italian, Portuguese, Dutch, Polish, Romanian, Swedish, Czech, Turkish, Russian, Ukrainian, Chinese (simplified) and Japanese. Pick every language that appears in the picture and the engine reads them together, for example English and German on the same sign.
- **Text you can use.** Spaces and odd characters are cleaned, and e-mail addresses, phone numbers and contact names next to a cue are replaced by `[email removed]`, `[phone removed]` and `[name removed]`, so the dataset does not become a list of contact details by accident.
- **Duplicates are free.** The same address twice, or two addresses with the same file (same SHA-1), are read and charged once.
- **Polite by design.** robots.txt is read and obeyed on the page host and on the host of every image (a CDN has its own), one request per second per host by default, a site's Crawl-delay is honoured up to 10 seconds, no proxies, no CAPTCHA solving.
- **Made for schedules.** Turn on **Only new rows** and an image is read and charged again only when its content changed at that address.

### How to use it

1. Open the Actor and fill in **Image or page URLs** (the other fields are optional filters).
2. Set **Maximum images** to a small number for a first try (the default is a safe start).
3. Click **Start**. The status line shows progress; the run stops cleanly at your limit or at the charge limit you set in the run options.
4. Open the **Dataset** tab and download JSON, CSV, Excel, XML or HTML, or send the rows to Google Sheets, Slack, Make, n8n or Zapier with the Integrations tab.
5. To run it on a schedule, use **Schedules** in Apify Console and turn on **Only new rows since the last run** below: every run then costs only the new rows, and a webhook URL tells you when something new came in.

A few practical notes:

- Put image links and page links in the same list; the actor tells them apart by what the address answers with.
- For a first try keep the list short, leave the filters at their defaults and look at the `text` and `meanConfidence` columns.
- Choose the language of the text, not the language of the page. Text in a language you did not pick is usually read as nonsense.
- Open the `COVERAGE` record in the key-value store to see which images were not read and why.

### Input

| Field | What it does |
|---|---|
| `startUrls` | Image files (PNG, JPEG, GIF, WebP, BMP, TIFF) or web pages that show images, one address per line. Up to 10,000 per run. |
| `languages` | The languages of the text, from the list of 16. Default English. More languages make a run a little slower. |
| `maxItems` | Stop after this many images with text were delivered. 0 = no limit. Free rows without text do not count. |
| `minWidth`, `minHeight` | Smallest image read, in pixels. Default 80 x 80. 0 turns the filter off. |
| `maxImagesPerPage` | For a page address: stop after this many images were read from it. Default 20, up to 1,000. |
| `maxImageMegabytes` | Largest file read, default 15 MB, up to 30. Images above 40 megapixels are never read. |
| `minConfidence` | The mean confidence from which an image counts as "text found". Default 40, from 0 to 100. |
| `pageSegmentationMode` | How the engine expects the text to be laid out: automatic (default), single column, single block, single line or sparse text. Under **Advanced settings**. |
| `includeLines` | Add the column `lines` with the confidence of every line. Default off. |
| `onlyNew`, `stateName` | Deliver an image again only when its content changed since the last run with the same state name |
| `notifyUrl`, `notifyWhenEmpty` | A webhook that receives one message when the run ends; see **Automation** below |
| `requestIntervalMs` | Pause between two requests to the same host, default 1000 ms, minimum 250 ms |

Example: two images, one in each of two languages.

```json
{
  "startUrls": [
    "https://shop.example.com/img/notice-en.png",
    "https://shop.example.com/img/notice-de.png"
  ],
  "languages": ["eng", "deu"]
}
```

Example: every image of a page that is at least 300 x 200 pixels, with the confidence of each line.

```json
{
  "startUrls": ["https://shop.example.com/menu"],
  "minWidth": 300,
  "minHeight": 200,
  "maxImagesPerPage": 50,
  "includeLines": true
}
```

#### Which layout setting to choose

- **Automatic** (default) suits documents, screenshots, signs and posters; the engine finds the blocks of text itself.
- **Single column** is for a text laid out in one narrow column of lines of different size, such as a receipt.
- **Single block** is for one paragraph, such as a cropped caption.
- **Single line** is for a picture that holds exactly one line, such as a cropped serial number or a licence plate strip.
- **Sparse text** is for words scattered over the picture without a reading order, such as the labels of a diagram or a map.

If the result looks wrong, the layout setting is the first thing to try, before changing the language.

#### Where the actor looks for images

- `<img src>` and the lazy-loading attributes `data-src`, `data-lazy-src` and `data-original`
- `srcset` of an image and of the `<source>` tags inside `<picture>`: the largest version listed is read, once
- the `og:image` preview picture of the page
- never: pictures drawn by JavaScript after the page loads, inline `data:` pictures (they are counted as skipped), CSS backgrounds, favicons, videos, and anything in `mailto:`, `tel:` or `sms:` links

### Output

19 fields per row. Empty values are `null`, never missing, so CSV and Excel columns are stable.

| Field | Type | Example |
|---|---|---|
| `imageUrl` | string | `https://tesseract.projectnaptha.com/img/eng_bw.png` |
| `pageUrl` | string | `https://example.com/menu` |
| `source` | string | direct |
| `textFound` | boolean | True |
| `text` | string | Mild Splendour of the various-vested Night! |
| `lines` | string | 92 | Mild Splendour of the various-vested Night! |
| `lineCount` | integer | 4 |
| `wordCount` | integer | 21 |
| `characterCount` | integer | 142 |
| `meanConfidence` | number | 94.2 |
| `languages` | string | eng |
| `width` | integer | 1024 |
| `height` | integer | 768 |
| `format` | string | png |
| `bytes` | integer | 48210 |
| `sha1` | string | 3e7b0b8c3a2c9f6c2f6e8f4c6b1a5d9e0f7a1b2c |
| `ocrMs` | integer | 412 |
| `note` | string | Why the row has no text (no text found, or confidence below the minimum), or that the text was cut; null otherwise |
| `checkedAt` | string | 2026-10-18T19:30:00.000Z |

#### Example rows

Two real rows from a run (long texts shortened here, complete in the dataset):

```json
[
  {
    "imageUrl": "https://tesseract.projectnaptha.com/img/eng_bw.png",
    "pageUrl": "https://tesseract.projectnaptha.com/",
    "source": "img",
    "textFound": true,
    "text": "Mild Splendour of the various-vested Night!\nMother of wildly-working visions! hail\nI watch thy gliding, while with watery light\nThy weak…",
    "lines": null,
    "lineCount": 8,
    "wordCount": 58,
    "characterCount": 351,
    "meanConfidence": 92,
    "languages": "eng",
    "width": 1486,
    "height": 668,
    "format": "png",
    "bytes": 26854,
    "sha1": "6c93ec93db5baf5ac220ef8aed97c95891c5742c",
    "ocrMs": 602,
    "note": null,
    "checkedAt": "2026-10-18T16:59:05.227Z"
  },
  {
    "imageUrl": "https://raw.githubusercontent.com/tesseract-ocr/tessdoc/main/images/eurotext.png",
    "pageUrl": null,
    "source": "direct",
    "textFound": true,
    "text": "The (quick) [brown] {fox} jumps!\nOver the $43,456.78 <lazy> #90 dog\n& duck/goose, as 12.5% of E-mail\nfrom [email removed] is spam.\nDer „s…",
    "lines": "96 | The (quick) [brown] {fox} jumps!\n95 | Over the $43,456.78 <lazy> #90 dog\n94 | & duck/goose, as 12.5% of E-mail\n94 | from [email remo…",
    "lineCount": 12,
    "wordCount": 65,
    "characterCount": 407,
    "meanConfidence": 92,
    "languages": "eng, deu, fra, spa, ita",
    "width": 640,
    "height": 500,
    "format": "png",
    "bytes": 31481,
    "sha1": "04a6017f050052e8f772a7d140694e7c083bbfd7",
    "ocrMs": 980,
    "note": null,
    "checkedAt": "2026-10-18T17:09:04.028Z"
  }
]
```

Results can be downloaded as JSON, CSV, Excel, XML or HTML from the Dataset tab, or read through the Apify API.

![Results of Image to Text OCR API: Extract Text from Images in Bulk in the Apify dataset view](https://api.apify.com/v2/key-value-stores/7hbQFQdGq61v2OZ2h/records/image-ocr-text-extractor-results.png)

The second example row was read with five languages at once and with **Add the confidence of each line** on: `lines` holds one line of the picture per line as `confidence | text`. The e-mail address in the picture is replaced by `[email removed]`.

#### How to read the confidence

`meanConfidence` is the engine's own estimate, from 0 to 100. In our test runs:

- clean printed text, such as a sign, a screenshot or a scanned page: 88 to 96
- small, compressed or slightly blurred text: 60 to 85; read it, but check the numbers
- a photo without any text: usually below 30, which is why 40 is the default minimum
- faint, blurred and noisy text: around 30 to 40; lower **Minimum confidence** if you want these rows anyway

The estimate says how sure the engine is, not whether the words are right. Check a sample of rows from a new kind of image before you rely on a whole run.

### Pricing

**Pay per result.** You pay for each image delivered to your dataset, nothing else: no subscription, no minimum, no charge for rows your filters drop or for the requests behind them.

| Your Apify plan | Price per 1,000 rows | Price per row |
|---|---|---|
| Free plan | **$3.00** | $0.003 |
| Starter plan | **$2.70** | $0.0027 |
| Scale plan | **$2.40** | $0.0024 |
| Business plan and above | **$2.00** | $0.002 |

Example: 1,000 images cost $3.00 on the Free plan and $2.00 on the Business plan. Platform usage of the run (compute, storage) is included in that price and is not billed to you separately (pay per event: the actor covers it).
A start event of $0.00005 is charged once per run (Apify default).
**Cap your spend:** set *Maximum total charge* in the run options. The actor stops cleanly when the cap is reached and you keep everything delivered until then. The run summary (`RUN_SUMMARY` in the key-value store) says why a run stopped.

An image is charged when text was found in it: the engine read at least three letters or digits and its mean confidence reached **Minimum confidence**. Nothing is charged for images without readable text, images below your size filter, files above the size limit, formats the engine cannot read (SVG, AVIF), repeated addresses and repeated files, images that could not be downloaded or whose server answered 403, 404 or with an anti-bot page, images closed by robots.txt, addresses that are not images or pages, images the engine failed on, and images skipped by **Only new rows** because they did not change.

What a run costs follows the pictures that hold text:

| Images with text | Free plan | Business plan |
|---|---|---|
| 10 | $0.03 | $0.02 |
| 100 | $0.30 | $0.20 |
| 1,000 | $3.00 | $2.00 |
| 10,000 | $30.00 | $20.00 |

A page with 30 pictures of which 6 hold text costs $0.018 on the Free plan, whatever the other 24 are.

### Automation: only new rows, notifications, schedules

**Only new rows since the last run.** Turn on `onlyNew` and the actor remembers every image it delivered to you (in a key-value store named `rowcraft-image-ocr-text-extractor-state` in your Apify account). The next run with the same `stateName` skips what you already have and you pay only for new rows. The first run delivers everything that matches. Use a different `stateName` for each schedule or search so they keep separate memories.

**Notification when the run ends.** Put a webhook address in `notifyUrl` and the actor sends one JSON POST with the number of new rows, links to the dataset (JSON and CSV) and the run. It works out of the box with Slack and Discord incoming webhooks (the message is in `text` and `content`), with Make, n8n and Zapier catch hooks, and with your own endpoint. By default it is sent only when there is at least one new row; `notifyWhenEmpty` sends it every time.

**Schedules.** Create a schedule in Apify Console (hourly, daily, weekly or cron) with `onlyNew` on. A daily schedule gives you a feed of new images for the price of the new rows only.

**Integrations.** Send the results to Make, n8n, Zapier, Google Sheets, Slack, Google Drive, Airtable or any webhooks from the Integrations tab of the Actor, with no code. Apify also offers native actions for Make, n8n and Zapier.

**API, SDKs and AI agents.** Every run is available through the Apify API and the official JavaScript and Python clients; the Actor can also be called by AI agents through the Apify MCP server. The example input in the API tab is a working call.

**How "new" is decided here.** The actor remembers each image address together with the SHA-1 of the file it delivered. On the next run with the same `stateName`, an image is read and charged again only if its content changed at that address; a re-uploaded identical file does not count. The actor still has to download an image to compare it, so the requests are made; only the reading and the charge are skipped. Images without text are remembered too, so a schedule does not read the same blank picture every day.

### Use cases

- **Product and packaging pictures:** read the ingredients, sizes and codes printed on pictures of products, to fill a catalogue from supplier images.
- **Screenshots and scanned pages:** turn screenshots of tables, receipts, forms or scanned pages into text you can search.
- **Banners and posters:** collect the prices, dates and slogans that a shop or an event puts in its banner images instead of in its HTML.
- **Accessibility and SEO audits:** find pictures that carry text a visitor needs but that has no text alternative.
- **Text in memes and social images you own:** search a folder of your own pictures by what is written in them.
- **Document pipelines:** feed the text to a language model, a translation step or a spreadsheet through Make, n8n, Zapier or your own code.

Ready-made examples, each with the input filled in (open one and press Start, or save it as your own task):

- [Extract text from a list of image links](https://apify.com/rowcraft/image-ocr-text-extractor/examples/text-from-image-links)
- [Read the text in the pictures of a web page](https://apify.com/rowcraft/image-ocr-text-extractor/examples/text-in-pictures-of-a-web-page)
- [Read a multilingual image with line confidence](https://apify.com/rowcraft/image-ocr-text-extractor/examples/multilingual-text-with-line-confidence)

### What this actor does not do

- Read scanned PDFs: give it the pages as images, or use [PDF Text Extractor](https://apify.com/rowcraft/pdf-text-extractor) for PDFs that already hold text.
- Recognise poor handwriting: printed text is the target; clear block handwriting may be read, joined-up or untidy handwriting usually is not.
- Translate the text, or fix its spelling: you get what the engine reads.
- Describe what a picture shows: a photo of a dog returns no text, not "a dog".
- Read pictures of identity documents, medical or financial records: do not use it for that (see Privacy).
- Log in: images and pages behind a login, a paywall or a cookie wall are not read.
- Run JavaScript or a browser: pictures that a script adds after the page loads are not found.
- Read SVG, AVIF or ICO files: they are listed as `unsupported_format` in `COVERAGE` and not charged.
- Work around protection: a server that answers with an anti-bot page, 401, 403 or 429 is reported in `COVERAGE` and not charged; the actor does not use proxies, solve CAPTCHAs or send a fake referrer.

### Limits and fair use

- **One request per second per host** by default (minimum 250 ms). A page and the server that holds its images are different hosts, so they are paced separately. A longer Crawl-delay in a site's robots.txt is applied instead, up to 10 seconds.
- **Speed:** the engine needs about 0.03 to 1 second for a small picture and 5 to 6 seconds for a 24-megapixel page of text. When the pictures come from one server, the pause between requests sets the pace, about one image per second.
- **Addresses per run:** up to 10,000. A page is read up to its first 5 MB of HTML.
- **Images per page:** 20 by default (`maxImagesPerPage`, up to 1,000); at most five times as many candidates (and never fewer than 100) are tried, so a page with thousands of pictures cannot use up a run.
- **File size:** 15 MB per image by default, up to 30 MB. A server that announces a larger file is not even asked for it. Images above 40 megapixels are never read, because the engine needs more memory than the run has.
- **Memory:** the default is 1,024 MB. In our tests, reading two 24-megapixel and two 39-megapixel pictures of text in one run peaked at about 590 MB, because the engine is restarted after every large picture to give its memory back.
- **Time per image:** an image that the engine cannot finish within 150 seconds is dropped, reported as an error and not charged; the run goes on.
- **Language data:** 1 to 5 MB per language is downloaded once at the start of each run from a public package CDN, and kept only for that run. If it cannot be loaded the run stops with a clear message and charges nothing.
- **Text per image:** at most 100,000 characters; the note says when the text was cut.
- **Refused images:** some image servers refuse a request that does not come from a page (hotlink protection). These images are listed as `blocked`. After 10 refused addresses in a row the rest of the list is skipped.
- **robots.txt is obeyed** for every address the actor touches, including redirect targets and the image servers. If robots.txt disallows a path, that page or image is `robots_disallowed`; if robots.txt answers with a server error, the host is not read, as the robots.txt standard (RFC 9309) requires.
- **Private addresses** (localhost, internal networks) are refused on the platform.
- **Time limit:** the run stops 90 seconds before its timeout and keeps everything delivered until then.

### Data source and licence

The text comes from the pictures you choose. The actor reads a web page once, after checking robots.txt, and downloads the image files that the page itself shows, as a visitor's browser would. The rows contain the text the engine read, facts about the file (size, format, hash) and the addresses.

**The rights to the pictures and to the text in them stay with their owners.** A picture on the web, and the words printed in it, may be protected by copyright or by a licence. The actor does not grant, check or transfer any right to use them. You decide what you may do with the text.

The OCR engine is Tesseract, an open-source project released under the Apache-2.0 licence, here in its WebAssembly build for JavaScript (tesseract.js); its language data comes from the same project. Image to Text OCR API is independent and not affiliated with or endorsed by the Tesseract project, its maintainers, or the websites it reads.

### Privacy: no personal data

Rowcraft actors are built not to collect personal data:

- The rows hold the text of the picture and facts about the file. They have no field for names, e-mail addresses or phone numbers.
- E-mail addresses, phone numbers, links to personal profiles and contact names next to a cue (Mr, Ms, "contact", "recruiter") in the recognised text are replaced by `[email removed]`, `[phone removed]`, `[profile link removed]` and `[name removed]`. Other names written in free text cannot be removed reliably.
- Image addresses with a user name or password inside are used without them, and an e-mail address inside an address is removed.
- The actor sends no cookies, fills no forms and never logs in.

The pictures you choose can show or contain personal data (a photo of a person, a letter, a form). Choose only pictures you are allowed to process, and do not use the actor on identity documents, health records or financial statements. You are responsible for using the text in line with the law that applies to you.

### Other Rowcraft actors

All Rowcraft actors read official or public sources, deliver flat rows without personal data and support only-new runs and notifications:

- [Ashby Jobs Scraper – Job Postings with Salary Ranges](https://apify.com/rowcraft/ashby-jobs-scraper) — Job postings from Ashby job boards (jobs.ashbyhq.com) by company: title, department, team, locations with country, employment and workplace type, published salary range, full text.
- [Broken Link Checker – Find 404s and Dead Links API](https://apify.com/rowcraft/broken-link-checker) — Pay per crawled page, not per link: crawl a site within robots.txt and check every link on each page for 404s, 5xx errors, DNS and TLS failures and redirects.
- [Bulk Image Downloader API: Save Page Images with Sizes](https://apify.com/rowcraft/bulk-image-downloader) — Download the images of any web pages, or direct image links, into a key-value store with width, height, format, size and a download link per file.
- [Career Site Jobs Scraper: Greenhouse, Lever, Ashby, Workday](https://apify.com/rowcraft/career-site-jobs-scraper) — Job listings from the career pages of the companies you choose on Greenhouse, Lever, Ashby, Workday, Workable, Personio, Recruitee, Teamtailor and Gem, in one run and one format: title, locations, department, salary when published, full text.
- [Chrome Web Store Scraper: Users, Ratings & Versions](https://apify.com/rowcraft/chrome-web-store-scraper) — Reads public Chrome Web Store listing pages with no login, no proxy and no personal data: users, rating, category, version, update date, permissions and company developers.
- [Domain Email and DNS Checker API: MX, SPF, DMARC, Providers](https://apify.com/rowcraft/domain-email-dns-checker) — Find which email provider, sending services and DNS host each domain uses, from public DNS records only.
- [SIRENE France Company Scraper: SIREN, NAF, Revenue, Status](https://apify.com/rowcraft/france-company-register) — French companies from the official Recherche d'entreprises API (INSEE SIRENE + INPI): SIREN and SIRET, legal form, NAF activity, head-office address, employee band, revenue and net income, labels, creation date and VAT number.
- [Gem Jobs Scraper – Job Board Listings from jobs.gem.com](https://apify.com/rowcraft/gem-jobs-scraper) — Job listings from Gem job boards (jobs.gem.com) by company: title, department, offices with country, employment type, remote, hybrid or on-site, requisition ID, publication date and full ad text, from the public Gem Job Board API.
- [Google Play Apps Scraper: App Details, Installs, Ratings](https://apify.com/rowcraft/google-play-apps-scraper) — Public Google Play app details as flat rows: installs, rating, number of ratings, price, version, last update, minimum Android, ads and in-app purchases.
- [Greenhouse Jobs Scraper – Job Listings by Company Board](https://apify.com/rowcraft/greenhouse-jobs-scraper) — Job listings from Greenhouse job boards by company: title, departments, offices, publication date, full ad text, published pay ranges.
- [Hacker News Scraper API: Top, New, Best, Ask, Show Stories](https://apify.com/rowcraft/hacker-news-scraper) — Read Hacker News stories from the official Firebase API: top, new, best, Ask HN, Show HN and job lists with score, comments, domain and age.
- [Himalayas Jobs Scraper: Remote Jobs API with Salaries](https://apify.com/rowcraft/himalayas-jobs-scraper) — Remote jobs from Himalayas through its public jobs API: title, company, salary range, seniority, categories, the countries and time zones open to candidates, full description and job link.
- [KRS Poland Companies Scraper: Register Data, PKD, Status](https://apify.com/rowcraft/krs-poland-companies) — Polish companies from the official KRS register API of the Ministry of Justice: name, KRS, NIP and REGON numbers, legal form, status, address, PKD activity codes, share capital, branches and registration dates.
- [LEI Records Scraper: Legal Entity Identifier Lookup API](https://apify.com/rowcraft/lei-records-scraper) — Legal Entity Identifier (LEI) records from the Global LEI Index, read through the public GLEIF API: legal name, legal form, register number, addresses, status, renewal date and parent companies.
- [Lever Jobs Scraper – Job Postings from jobs.lever.co](https://apify.com/rowcraft/lever-jobs-scraper) — Job postings from Lever career pages (jobs.lever.co) by company: title, team, department, location, commitment, workplace type, salary range when published, description, requirements and benefits.
- [Lighthouse Audit API: Core Web Vitals and Performance Scores](https://apify.com/rowcraft/lighthouse-performance-audit) — Run Lighthouse on any public page and get Performance, Accessibility, Best Practices and SEO scores, lab Core Web Vitals and the biggest savings.
- [Norway Company Register Scraper: Brønnøysund Business Data](https://apify.com/rowcraft/norway-company-register-scraper) — Norwegian companies from the Central Coordinating Register for Legal Entities (Brønnøysund, NLOD open data): organisation number, legal form, industry, employees, share capital, addresses, website, VAT and bankruptcy status.
- [PDF Text Extractor: Text, Pages and Metadata from PDF Links](https://apify.com/rowcraft/pdf-text-extractor) — Get the text of every PDF you link, page by page, with page count, language and word count.
- [Personio Jobs Scraper: Job Listings, Salaries, Departments](https://apify.com/rowcraft/personio-jobs-scraper) — Job ads from any company's Personio careers feed: title, department, office, employment type, schedule, seniority, salary when published, keywords and the full description in the language you choose.
- [Platsbanken Scraper: Sweden Jobs, Employers, Org Numbers](https://apify.com/rowcraft/platsbanken-jobs-scraper) — Every job ad on Platsbanken (Arbetsförmedlingen) from the official JobTech API: headline, employer and organisation number, occupation codes, municipality, coordinates, salary type, deadline and full text.
- [Recruitee Jobs Scraper – Job Listings by Company Career Site](https://apify.com/rowcraft/recruitee-jobs-scraper) — Job listings from Recruitee career sites by company: title, department, locations with country, employment type, remote flag, published salary and full text, one request per company.
- [Remote Jobs Aggregator API: RemoteOK, WeWorkRemotely, Jobicy](https://apify.com/rowcraft/remote-jobs-aggregator) — Remote jobs from RemoteOK, We Work Remotely and Jobicy in one clean list, read from their official feeds and APIs, merged without duplicates.
- [Robots.txt and AI Crawler Access Checker API](https://apify.com/rowcraft/robots-ai-crawler-checker) — Check which AI and search crawlers a website lets in: GPTBot, ClaudeBot, PerplexityBot, Google-Extended and more, read from robots.txt by the RFC 9309 rules.
- [RSS Feed Finder and Reader – Blog and News Article API](https://apify.com/rowcraft/rss-feed-finder-reader) — Find the RSS, Atom and JSON feeds of any website and read their newest articles, from a domain name alone.
- [SEO Audit Tool: Titles, Meta Tags, Headings and Links API](https://apify.com/rowcraft/seo-audit-tool) — Audit the on-page SEO of any list of pages or websites: titles, meta descriptions, headings, canonicals, indexing, links, images and structured data, with a score and the issues found per page.
- [Shopify Products Scraper: Store Catalog Export to JSON, CSV](https://apify.com/rowcraft/shopify-products-scraper) — Products from any Shopify store's public catalog: title, vendor, type, tags, description, variants with price, compare-at price, SKU and stock, images, options, store currency.
- [Sitemap URL Extractor: Every Page with Last-Modified Dates](https://apify.com/rowcraft/sitemap-url-extractor) — Get every page a site lists in its sitemaps, with last-modified dates, from robots.txt through sitemap indexes and .gz files.
- [SSL Certificate and Security Headers Checker API](https://apify.com/rowcraft/ssl-security-headers-checker) — Check the TLS certificate and the HTTP security headers of every domain in one run, with an A+ to F grade and the exact issues to fix.
- [Store Products Scraper – Shopify, WooCommerce, VTEX](https://apify.com/rowcraft/store-products-scraper) — Export the product catalogue of any Shopify, WooCommerce or VTEX store from its public catalogue endpoint: title, brand, prices, discounts, stock, categories, images and links, one row per product, the same columns on every platform.
- [Structured Data Extractor: JSON-LD, Schema.org, Open Graph](https://apify.com/rowcraft/structured-data-extractor) — Pull the schema.org JSON-LD, Microdata, RDFa and Open Graph of any page list into flat rows: types, product price, availability, ratings, organization, breadcrumbs, with validation issues.
- [Teamtailor Jobs Scraper – Career Site Jobs by Company](https://apify.com/rowcraft/teamtailor-jobs-scraper) — Job listings from Teamtailor career sites by company: title, department, role, locations with city and country, remote or hybrid status, publication date and full ad text, from the official job feed.
- [TED Tenders Scraper: EU Public Procurement Notices API](https://apify.com/rowcraft/ted-tenders-scraper) — EU public procurement notices from TED (Tenders Electronic Daily), the official EU tenders portal, through its public Search API: title, buyer, country, CPV codes, values, deadlines, lots and document links.
- [UK Companies House Scraper: Company Search & Profiles API](https://apify.com/rowcraft/uk-companies-house-scraper) — UK companies from the official Companies House API with your free key: search by name, status, type, SIC code, date or location, or look up company numbers.
- [UK Tenders Scraper: Find a Tender + Contracts Finder API](https://apify.com/rowcraft/uk-tenders-scraper) — UK public procurement notices from Find a Tender and Contracts Finder, the official UK government portals, through their open OCDS APIs: title, buyer, region, CPV codes, values, deadlines, notice type and links.
- [Bulk URL Status and Redirect Chain Checker API](https://apify.com/rowcraft/url-status-redirect-checker) — Check the status code, redirect chain, canonical and indexing signals of every URL in a list, with the final destination and a loop flag.
- [Video Frame Extractor: Thumbnails and Frames from Video URLs](https://apify.com/rowcraft/video-frame-extractor) — Extract JPG, PNG or WebP frames from direct video links (MP4, WebM, MOV, MKV, OGV): evenly spaced, every N seconds, at exact times or at scene changes.
- [VTEX Products Scraper – Public Catalog Export](https://apify.com/rowcraft/vtex-products-scraper) — Export the catalogue of any VTEX store (Auchan, Chedraui and thousands of retailers in Europe and Latin America) from its public search endpoint: title, brand, EAN, prices, list prices, stock, categories, specifications and images, one row per product.
- [Website to Markdown Extractor: Clean Text for LLMs and RAG](https://apify.com/rowcraft/website-markdown-extractor) — Turn a list of web page addresses into clean Markdown and plain text for LLMs and RAG: the main content only, with headings, links, images and tables.
- [Website Screenshot API: Full-Page PNG, JPEG and PDF Captures](https://apify.com/rowcraft/website-screenshot-api) — Capture full-page or viewport screenshots of any public web page as PNG, JPEG or WebP, with an optional A4 PDF.
- [Website Tech Stack Detector – Technology Lookup API](https://apify.com/rowcraft/website-tech-stack-detector) — Find the technologies behind a list of websites: CMS, e-commerce platform, analytics, tag managers, ad pixels, consent tools, chat, CDN and payments, from one polite home-page request per domain.
- [WooCommerce Products Scraper – Store API Export](https://apify.com/rowcraft/woocommerce-products-scraper) — Export every product of a WooCommerce store through its public Store API: title, SKU, prices and sale prices, stock, categories, tags, attributes, ratings, images and links, one row per product.
- [Workable Jobs Scraper: Job Listings from Career Pages](https://apify.com/rowcraft/workable-jobs-scraper) — Job listings from any company's Workable career page: title, department, location, remote or on-site, employment type, description, requirements, benefits and salary when published.
- [Workday Jobs Scraper – Jobs from myworkdayjobs.com Sites](https://apify.com/rowcraft/workday-jobs-scraper) — Job listings from Workday career sites (myworkdayjobs.com) by company: title, locations, country, posting date, time type, requisition ID and the full description.

### FAQ

**Is it legal to read text from images?** The actor reads public pages, obeys robots.txt and processes the pictures a page shows to every visitor. Whether you may copy, store or reuse the text depends on its copyright or licence and on the law that applies to you. The actor does not check licences.

**Does it collect personal data?** It does not look for it and has no field for it: e-mail addresses, phone numbers, profile links and contact names next to a cue are masked in the text. A picture can still contain other personal data, so read the Privacy section and choose your pictures with care.

**Why is the result empty?** Check `COVERAGE` and `RUN_SUMMARY`. The usual reasons: the page loads its pictures with JavaScript, every image was below the minimum size (lower `minWidth` and `minHeight`), robots.txt disallows the page or the images (`robots_disallowed`), the site answered with an anti-bot page (`blocked`), the pictures hold no text (free rows with `textFound = false`), or with **Only new rows** on nothing had changed. Nothing is charged for those, apart from the start event.

**Why did a picture with clear text return nothing?** Look at `note` in the free row. If it says the mean confidence was below the minimum, lower **Minimum confidence**. If the text is very small, find a larger version of the picture: letters below about 20 pixels high are rarely read. If the layout is unusual, try another **Layout of the text**.

**Why is some of the text wrong?** OCR is an estimate. Compressed, small, curved, stylised or low-contrast text is read less well; a language you did not select is read as nonsense. Pick the right languages, use the largest picture you can get, and use `meanConfidence` and `lines` to find the rows that need a look.

**Which languages and formats does it read?** Sixteen languages (English, German, French, Spanish, Italian, Portuguese, Dutch, Polish, Romanian, Swedish, Czech, Turkish, Russian, Ukrainian, Chinese simplified, Japanese) and the formats PNG, JPEG, GIF, WebP, BMP and TIFF. The format is read from the file's own header, so a `.jpg` that is really a PNG is reported as `png`.

**Why does my text contain \[phone removed] or \[email removed]?** The cleaning step masks anything that looks like a phone number or an e-mail address, including long digit groups that are really part numbers or postal codes. The masking cannot be switched off, because the actor is built not to deliver contact details.

**Can it read a scanned PDF?** Not directly. Export the pages as images and give the image links to this actor, or use PDF Text Extractor for PDFs that already hold a text layer; it reads that text without OCR.

**What does it cost for a page with many pictures?** You pay for each image in which text was found: $3.00 per 1,000 on the Free plan and $2.00 per 1,000 on the Business plan. Pictures without text, icons, duplicates and failed downloads are not charged.

**Do I pay for images that were already delivered?** With **Only new rows** on, an image whose content did not change since your last run with the same state name is not read and not charged.

**Can I read the images of a whole website?** Not by itself: it reads the pages you list and does not follow links. Use a sitemap or link-list tool to build the list of pages first, then paste the addresses here.

### Support and reviews

**Problems, missing fields, questions:** open an issue on the **Issues** tab of this Actor. We answer within one working day and fix confirmed bugs in the next release; the changelog lists every change.

If this Actor saved you time, a short review on its page helps other users find it and helps us keep improving it.

# Changelog

This Actor's version history is a separate document: https://apify.com/rowcraft/image-ocr-text-extractor/changelog.md

# Actor input Schema

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

One address per line. A link to an image file (PNG, JPEG, GIF, WebP, BMP, TIFF) is read directly. A link to a web page is searched for the images it shows (img, srcset, picture, og:image) and each image is read. Duplicates and invalid entries are skipped and never charged.

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

The languages of the text. Pick every language that appears: the engine then reads them together (for example English and German on the same sign). More languages make a run a little slower. Default: English.

## `maxItems` (type: `integer`):

Stop after this many images with text were delivered in the whole run. Images without text are delivered free and do not count. 0 = no limit.

## `minWidth` (type: `integer`):

Images narrower than this are skipped and not charged: icons, tracking pixels, spacers. Text in very small images is rarely readable. 0 = no limit.

## `minHeight` (type: `integer`):

Images lower than this are skipped and not charged. 0 = no limit.

## `maxImagesPerPage` (type: `integer`):

For a web page address: stop after this many images were read from that page (with or without text). The rest are listed as not read in COVERAGE. Direct image links are not limited by this.

## `maxImageMegabytes` (type: `number`):

Files bigger than this are skipped and not charged; a file that announces its size is not even downloaded. Images above 40 megapixels are never read. Maximum 30.

## `pageSegmentationMode` (type: `string`):

How the engine expects the text to be laid out. Automatic suits documents, signs and screenshots. Use a single block for one paragraph, a single line for a cropped line, and sparse text for scattered words (labels on a diagram, a map).

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

An image counts as "text found" (and is charged) when the engine's mean confidence is at least this value. Photos without text usually score below 30; clear text scores 80 to 95. Lower it for faint or low-quality scans, raise it to be stricter.

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

Off (default): only the text and the mean confidence. On: the column `lines` lists every line of text with its own confidence as plain text (`92 | OPEN DAILY 9 AM TO 6 PM`), one line per row of the picture.

## `onlyNew` (type: `boolean`):

Remember the rows already delivered to you (in a key-value store in your Apify account) and deliver only rows that appeared since the last run with the same state key. The first run delivers everything that matches; later runs cost only the new rows. Ideal for schedules.

## `stateName` (type: `string`):

Name of the memory used by "Only new rows". Use a different name for each schedule or search (for example `stockholm-python`) so they do not share one memory.

## `notifyUrl` (type: `string`):

A webhook address that receives one JSON POST with the number of new rows and links to the dataset: a Slack or Discord incoming webhook, a Make, n8n or Zapier catch hook, or your own endpoint. The message is also in the `text` (Slack) and `content` (Discord) fields.

## `notifyWhenEmpty` (type: `boolean`):

By default the notification is sent only when at least one row was delivered.

## `requestIntervalMs` (type: `integer`):

Pause between two requests to the same host (a page and its image server are separate hosts). Default 1000 ms; minimum 250. A longer Crawl-delay in a site's robots.txt is applied instead, up to 10 s.

## Actor input object example

```json
{
  "startUrls": [
    "https://tesseract.projectnaptha.com/img/eng_bw.png",
    "https://raw.githubusercontent.com/naptha/tesseract.js/master/tests/assets/images/simple.png"
  ],
  "languages": [
    "eng"
  ],
  "maxItems": 500,
  "minWidth": 80,
  "minHeight": 80,
  "maxImagesPerPage": 20,
  "maxImageMegabytes": 15,
  "pageSegmentationMode": "auto",
  "minConfidence": 40,
  "includeLines": false,
  "onlyNew": false,
  "stateName": "default",
  "notifyWhenEmpty": false,
  "requestIntervalMs": 1000
}
```

# Actor output Schema

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

One row per image read, in the default dataset (JSON, CSV, Excel and other formats via the dataset API).

## `runSummary` (type: `string`):

Counts, filters used, warnings and why the run stopped (RUN_SUMMARY record in the default key-value store).

## `coverage` (type: `string`):

One line per address, and one per image that was not read or had no text, with the reason: too_small, unsupported_format, duplicate, unchanged, too_large, not_image, no_text, not_found, blocked, robots_disallowed, error or not_checked (COVERAGE record).

# 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": [
        "https://tesseract.projectnaptha.com/img/eng_bw.png",
        "https://raw.githubusercontent.com/naptha/tesseract.js/master/tests/assets/images/simple.png"
    ],
    "languages": [
        "eng"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("rowcraft/image-ocr-text-extractor").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": [
        "https://tesseract.projectnaptha.com/img/eng_bw.png",
        "https://raw.githubusercontent.com/naptha/tesseract.js/master/tests/assets/images/simple.png",
    ],
    "languages": ["eng"],
}

# Run the Actor and wait for it to finish
run = client.actor("rowcraft/image-ocr-text-extractor").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": [
    "https://tesseract.projectnaptha.com/img/eng_bw.png",
    "https://raw.githubusercontent.com/naptha/tesseract.js/master/tests/assets/images/simple.png"
  ],
  "languages": [
    "eng"
  ]
}' |
apify call rowcraft/image-ocr-text-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,rowcraft/image-ocr-text-extractor"
        }
    }
}
```

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/N7E6LAZyaXcoF50No/builds/EMCpO9KvTxqtDdKoc/openapi.json
