How to Verify an EAN-13 Barcode Scan (Check Digit Formula)
An EAN-13 scan is correct when its last digit matches the check digit of the first 12. Formula, JS and Python code, and why scans go wrong.
4 min readA scanned EAN-13 is correct when its 13th digit equals the check digit computed from the first 12. Multiply the digits alternately by 1 and 3 starting from the left, add them up, and the check digit is (10 − sum mod 10) mod 10. If it does not match, the scan or the print is wrong: rescan, do not guess.
The check-digit rule in one example
The weights are 1 and 3 so that two common mistakes are caught: one wrong digit always changes the sum, and swapping two neighbours changes it too unless their difference is a multiple of 5 (for example 0 and 5). That is why a check digit catches most, not all, errors. It detects a bad read; it does not prove the product is the one you expect.
The same rule covers EAN-8 and UPC-A. Count the weights from the right instead: the digit next to the check digit gets weight 3, then 1, 3, 1... For EAN-13 that is the same result as counting from the left. A UPC-A code is an EAN-13 with a leading 0.
Code: validate EAN-13, EAN-8 and UPC-A
Tested against 4006381333931 (EAN-13), 96385074 (EAN-8) and 036000291452 (UPC-A): all valid. Changing the last digit of any of them makes it invalid.
Both return false for the right length with a wrong check digit, and for anything that is not 8, 12 or 13 digits. (GTIN-14 uses the same rule; add 14 to the lengths if you need it.)
Why scans fail
- Blur or low resolution: bars merge, so a digit is read as its neighbour.
- Glare or a curved surface: part of the symbol is lost.
- Damaged or low-contrast print: thin bars disappear.
- A missing quiet zone: no white margin around the symbol, so the scanner cannot find its edges.
- Human typing: the digits under the bars retyped by hand. The check digit catches this too.
A failed check is the signal to rescan or retype. Do not "fix" the last digit by replacing it with the computed one: that makes the number valid but not necessarily the right product.
Decode from a photo instead of a scanner
Veralens is a document extraction API (OCR plus schema-typed JSON). If your input is a photo or a PDF page rather than a live scanner, declare the field with format: "barcode" and Veralens returns the decoded payload as printed. QR, Micro QR and the linear families EAN, UPC, Code 128, Code 39 and ITF are supported, and a page with no readable code returns null (Schemas and formats).
Run the check digit on the returned value with the functions above. The API documentation does not list a check-digit validation for barcode fields (unlike iban, which is mod-97 verified), so do not rely on it; validate in your own code. Step-by-step request and job polling: Getting started. More on reading codes from documents: Extract QR codes and barcodes from documents. Using EANs in an app: barcode pantry tracker.
FAQ
- Does a valid check digit mean the product exists? No. It only means the digits are internally consistent. Look the code up in a product database to confirm it.
- Is a UPC-A the same as an EAN-13? A UPC-A is an EAN-13 with a leading 0, and the check digit is the same.