Arabic Receipt Reader (OCR + Saudi ZATCA QR)
Pricing
Pay per event
Arabic Receipt Reader (OCR + Saudi ZATCA QR)
Reads Arabic and English receipts and invoices, including Arabic-Indic digits: full OCR, total/VAT/subtotal/line-item candidates checked against each other, a needsReview flag, and exact fields from Saudi ZATCA e-invoice QR codes (signature not checked). For dense documents use Arabic Image OCR.
Pricing
Pay per event
Rating
0.0
(0)
Developer
saif alnuaimi
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
Read Arabic and English receipts and invoices, including Arabic-Indic digits (٠١٢٣٤٥٦٧٨٩) and Persian digits (۰-۹). For every image you get:
- the full text and lines, each with a confidence score, in right-to-left reading order;
- candidates for the total, subtotal, VAT, VAT number, date and line items, each with its confidence;
- a reconciliation check: do the line items + VAT add up to the total, does subtotal + VAT equal the total, does the total match the e-invoice QR?
- a
needsReviewflag that tells you which receipts a person should check, withreviewReasons; - the exact fields from a Saudi ZATCA e-invoice QR (seller, VAT number, timestamp, total, VAT), decoded from the QR, never guessed from the OCR text.
Dense documents, article screenshots or chat screenshots? Use Arabic Image OCR (
arabic-image-ocr) instead. Its plain-text mode is faster, cheaper and reads dense small print better. It also has a QR-only mode.
This tool reads and reports the fields on an invoice. It doesn't issue, send or receive e-invoices, isn't an accredited service provider, and doesn't confirm compliance with ZATCA or UAE rules.
How it reads
A neural OCR engine (EasyOCR, Arabic + English) reads the image, and every number is cross-checked by a dedicated Arabic-Indic digit classifier. When the two read a number differently, you get the more confident reading plus alternativeReading. Every image is downscaled to at most 4 megapixels before OCR; imageSize shows the original and processed sizes, and boxes are in original-image pixels.
The receipt object
totalCandidate,subtotalCandidate,vatCandidate: the value, the text as printed (textRaw), the line it came from, aconfidencefrom 0 to 100 andlowConfidence(belowconfidenceThreshold, default 80).vatNumberCandidateanddateCandidate.lineItems: the description and amount of each line, with their confidence.sumCheck: whether the line items add up to the subtotal, the total minus VAT, or the total, withinsumToleranceCents(default 0.05).vatCheck: whether subtotal + VAT equals the total.qrCrossCheck: whether the printed total and VAT agree with the invoice QR (tags 4 and 5), and the printed VAT number with tag 2:agree,mismatch,not_in_qrornot_found_in_text.reconciliation:{"reconciled": true/false, "by": [...]}, withbynaming what matched (lineItemsPlusVatEqualTotal,lineItemsEqualTotal,lineItemsEqualSubtotalAndSubtotalPlusVatEqualsTotal,subtotalPlusVatEqualsTotal,totalMatchesQr).needsReview,reviewReasonsandnotes.
When is needsReview true?
reviewReasons entry | Meaning |
|---|---|
line_items_do_not_add_up | The line items don't add up to the subtotal / total (within the tolerance) |
subtotal_plus_vat_differs_from_total | Subtotal + VAT is not the total |
printed_value_differs_from_qr: total / vat | The printed value disagrees with the e-invoice QR |
no_total_found | No total line was recognised |
total_low_confidence_and_nothing_to_reconcile | There are no line items, VAT or QR total to check the total against, and its confidence is below confidenceThreshold |
Low OCR confidence alone does not flag a receipt whose numbers reconcile. When the items + VAT add up to the total (or the total matches the QR), a low confidence score is only mentioned in notes. On our test set this cut the flag rate on correctly read receipts from 83% to 21%, while still flagging every misread or inconsistent receipt (see test-results/REPORT.md).
Totals are candidates, not facts. OCR can misread a digit, a decimal separator or which line is the total. Numbers that reconcile are much more likely to be right, but not guaranteed. Don't book, pay or file anything on OCR values alone. When an invoice has a ZATCA QR, exactQrFields.invoiceTotal is the value encoded by the seller's system and is the better source.
Exact QR fields
When an image contains a Saudi e-invoice QR (ZATCA TLV, base64), you get exactQrFields:
| Tag | Field |
|---|---|
| 1 | sellerName |
| 2 | vatNumber (plus vatNumberFormatValid: 15 digits, starts and ends with 3; a flag, never a rejection) |
| 3 | timestamp (plus timestampIso) |
| 4 | invoiceTotal (with VAT) |
| 5 | vatTotal |
| 6–9 | reported as present, with their lengths (hash, signature, public key, certificate signature) |
tagsFound lists the tags in the QR. qrFormat is informational: phase1, phase2-standard, phase2-simplified or other.
We decode the QR TLV but do NOT verify its cryptographic signature/stamp (signatureChecked is always false). A QR that decodes cleanly can still be forged or belong to another invoice. These are the exact fields stored in the QR, not proof that the invoice is genuine or registered.
qr.status is zatca_tlv, not_zatca_tlv (another kind of QR; its text is in qr.codes[].rawText) or no_qr_found.
Input
| Field | Default | Notes |
|---|---|---|
imageUrls | Public image links (JPEG, PNG, WebP, GIF, BMP, TIFF) | |
keyValueStoreRecords | Uploaded images: record-key for this run's store, or store-name-or-id/record-key | |
includeQrFields | true | Decode a ZATCA QR and cross-check against it (included in the price, no extra charge) |
confidenceThreshold | 80 | The lowConfidence threshold |
sumToleranceCents | 5 | Reconciliation tolerance, in 1/100 of the currency |
deskew, digitSecondPass, maskQrCodes | true | Image options |
outputLines, outputWords, stripTatweel | true, false, true | Output detail |
maxImageSizeMb | 20 | Limit per image |
No images in the input? The actor reads a small bundled sample receipt so you can see the output (sample: true, free). This is also what the input form's default run does. If the input links to an example image that can't be downloaded, the sample is read instead. Either way the run's status message and OUTPUT.note say so (for example "Input image failed to download; returned built-in sample instead").
{"imageUrls": [{"url": "https://example.com/receipt-1.jpg"}, {"url": "https://example.com/receipt-2.png"}],"confidenceThreshold": 85}
Memory: the OCR models need about 1.5–1.8 GB of RAM, so the actor runs at 4 GB (minimum and default 4096 MB) and reads one image at a time. If it is started with less than about 3 GB, it stops straight away with the message "increase memory to 4 GB" and charges nothing. A receipt screenshot takes a few seconds; a phone photo can take up to about a minute.
Output
A shortened row for a phase-2 receipt:
{"source": "https://example.com/receipt.png","sample": false,"status": "ok","mode": "full","text": "Al Waha Trading Co.\nSimplified Tax Invoice\nLaptop stand 1000.00\nVAT 15% 150.00\nTotal 1150.00","imageSize": {"original": [760, 747], "processed": [760, 747], "downscaledBy": 1.0, "maxProcessedMegapixels": 4.0},"qr": {"status": "zatca_tlv", "count": 1},"exactQrFields": {"sellerName": "Al Waha Trading Co. شركة الواحة", "vatNumber": "300000000000003", "vatNumberFormatValid": true,"timestamp": "2026-09-30T08:05:44", "invoiceTotal": "1150.00", "vatTotal": "150.00","qrFormat": "phase2-simplified", "tagsFound": [1, 2, 3, 4, 5, 6, 7, 8, 9], "signatureChecked": false},"receipt": {"totalCandidate": {"value": 1150.0, "textRaw": "1150.00", "confidence": 61.2, "lowConfidence": true},"vatCandidate": {"value": 150.0, "confidence": 90.3, "lowConfidence": false},"lineItems": [{"description": "Laptop stand", "value": 1000.0, "confidence": 72.5, "lowConfidence": true}],"sumCheck": {"itemsSum": 1000.0, "comparedTo": "totalCandidate minus vatCandidate", "difference": 0.0, "tolerance": 0.05, "addsUp": true},"qrCrossCheck": {"total": "agree", "vat": "agree", "vatNumber": "agree"},"reconciliation": {"reconciled": true, "by": ["lineItemsPlusVatEqualTotal", "totalMatchesQr"]},"needsReview": false,"reviewReasons": [],"notes": ["low OCR confidence on totalCandidate, lineItems, but the numbers reconcile, so not flagged"]},"chargedEvents": ["full-ocr-screenshot"]}
Images that can't be read get a row with status: "error" and an error message. Error rows are free. The OUTPUT record summarises the run: images processed, failed and skipped, rows needing review, sample rows, the events charged and how long it took.
Pricing (pay per event)
| Event | When | Price |
|---|---|---|
full-ocr-screenshot | Each image whose original is up to 4 megapixels (screenshots, scans, most receipts) | $0.025 |
full-ocr-photo | Each image whose original is over 4 megapixels (phone photos) | $0.05 |
- Photo or screenshot is decided by the original image's pixel count, before the 4 MP downscale. A 12 MP phone photo is billed as
full-ocr-photoeven though it is read at 4 MP. - The Saudi e-invoice QR fields and the QR cross-check are included in the price. A receipt with a ZATCA QR costs the same as one without: $0.025 or $0.05.
- Error rows and sample rows (
sample: true) are free. chargedEventsin each row lists only the events actually charged for that row.- Spending limit: before each image the actor checks that its price still fits under your run's maximum cost. If it doesn't, the actor stops cleanly and keeps every row saved so far (
OUTPUT.stoppedByChargeLimit,skippedSources). You are never charged for a row that wasn't saved.
Limitations
- OCR is not perfect. On our synthetic receipts this actor read about 90% of Arabic-Indic digits correctly. Real photos (blur, glare, crumpled or faded thermal paper) score lower. Use
needsReview. - Very dense pages (a full article screenshot) are slow and read worse than with Arabic Image OCR.
- Handwriting, decorative calligraphy and heavy diacritics are read poorly.
- Decimal separators:
٫,.and,are all used on Gulf receipts. Something like٢٢,٥٠is read as 22.50 and markedseparatorAmbiguous. - Field detection relies on keywords in Arabic and English (الإجمالي، المجموع، ضريبة القيمة المضافة, Total, VAT, Subtotal…). Unusual layouts may give no
totalCandidate(no_total_found). - Only Saudi ZATCA QR codes are decoded into fields.
- Only the first frame of multi-page TIFF and GIF files is read.
Privacy
Images are downloaded into the run's own container, processed in memory and not sent to any third-party service. All OCR runs inside the actor. Results are stored only in your run's dataset and key-value store, under your Apify account and its retention settings. Receipts and invoices can contain personal data (names, VAT numbers, sometimes card digits), so make sure you have the right to process the images you submit, and delete runs you no longer need.