Image to Text OCR: Arabic, Scans and Screenshots
Pricing
$5.00 / 1,000 image reads
Image to Text OCR: Arabic, Scans and Screenshots
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.
Pricing
$5.00 / 1,000 image reads
Rating
0.0
(0)
Developer
Mohammed Samaan
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
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), optionalblocksand word boxes,wordCount,format,width,heightandwarnings. - A
qualityreport 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, andneedsReview. needsReviewflags the conditions listed in the quality report. It does not guarantee that an image withneedsReview: falsewas read correctly. It istruewhen 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
{"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:
{ "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.
{"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):
{ "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:
{ "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:
{ "urls": ["https://example.org/receipt.jpg"], "ocrLanguages": ["eng"], "outputFormat": "text" }
Read these fields from each dataset item:
statusmust beok. If it iserror, readerroranderrorKind.no_text_foundmeans there was nothing to read;unreliablemeans text came back but should not be trusted.quality.needsReview: whentrue, tell the user whichquality.lowConfidenceLinesorwarningsto check before relying on the text. Whenfalse, the checks listed above found nothing; that is not proof the text is right.textfor the content; withoutputFormat: "lines"uselines[].bboxto 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 thepossibleHandwritingflag 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:
engandaraship with the Actor and were benchmarked. Every other language file (1 to 12 MB, official Tesseracttessdata_fast) is downloaded at the start of the run and its output is experimental: it was not benchmarked,quality.experimentalistrueand 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_imagebefore 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: scanned and text PDFs to Markdown with the same OCR engine.
- Document to Markdown for RAG: 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.