Image to Text OCR API: Extract Text from Images in Bulk avatar

Image to Text OCR API: Extract Text from Images in Bulk

Pricing

from $2.00 / 1,000 image reads

Go to Apify Store
Image to Text OCR API: Extract Text from Images in Bulk

Image to Text OCR API: Extract Text from Images in Bulk

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.

Pricing

from $2.00 / 1,000 image reads

Rating

0.0

(0)

Developer

Rowcraft

Rowcraft

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

an hour ago

Last modified

Share

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

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

FieldWhat it does
startUrlsImage files (PNG, JPEG, GIF, WebP, BMP, TIFF) or web pages that show images, one address per line. Up to 10,000 per run.
languagesThe languages of the text, from the list of 16. Default English. More languages make a run a little slower.
maxItemsStop after this many images with text were delivered. 0 = no limit. Free rows without text do not count.
minWidth, minHeightSmallest image read, in pixels. Default 80 x 80. 0 turns the filter off.
maxImagesPerPageFor a page address: stop after this many images were read from it. Default 20, up to 1,000.
maxImageMegabytesLargest file read, default 15 MB, up to 30. Images above 40 megapixels are never read.
minConfidenceThe mean confidence from which an image counts as "text found". Default 40, from 0 to 100.
pageSegmentationModeHow the engine expects the text to be laid out: automatic (default), single column, single block, single line or sparse text. Under Advanced settings.
includeLinesAdd the column lines with the confidence of every line. Default off.
onlyNew, stateNameDeliver an image again only when its content changed since the last run with the same state name
notifyUrl, notifyWhenEmptyA webhook that receives one message when the run ends; see Automation below
requestIntervalMsPause between two requests to the same host, default 1000 ms, minimum 250 ms

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

{
"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.

{
"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.

FieldTypeExample
imageUrlstringhttps://tesseract.projectnaptha.com/img/eng_bw.png
pageUrlstringhttps://example.com/menu
sourcestringdirect
textFoundbooleanTrue
textstringMild Splendour of the various-vested Night!
linesstring92 | Mild Splendour of the various-vested Night!
lineCountinteger4
wordCountinteger21
characterCountinteger142
meanConfidencenumber94.2
languagesstringeng
widthinteger1024
heightinteger768
formatstringpng
bytesinteger48210
sha1string3e7b0b8c3a2c9f6c2f6e8f4c6b1a5d9e0f7a1b2c
ocrMsinteger412
notestringWhy the row has no text (no text found, or confidence below the minimum), or that the text was cut; null otherwise
checkedAtstring2026-10-18T19:30:00.000Z

Example rows

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

[
{
"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

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 planPrice per 1,000 rowsPrice 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 textFree planBusiness 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):

What this actor does not do

  • Read scanned PDFs: give it the pages as images, or use 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:

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.