DEV Community

How to Validate a GTIN / EAN-13 Barcode Check Digit (GS1 Mod-10, explained)

If you have ever uploaded a product feed to Amazon, Google Merchant Center or Shopify and been rejected with "invalid GTIN," the culprit is almost always the check digit - the last digit of the barcode. It is not random: it is calculated from all the other digits, and if it does not match, the platform rejects the code. Here is exactly how the check digit works, with a worked example you can follow by hand.

What is a GTIN check digit?

GTIN (Global Trade Item Number) is the umbrella term for the barcodes on products: GTIN-8, UPC-A (12 digits), EAN-13 (13 digits) and GTIN-14. The final digit of every one of them is a check digit computed with the GS1 Mod-10 algorithm. Its job is to catch typos: change one digit and the check digit almost always stops matching.

The GS1 Mod-10 algorithm, step by step

Take all the digits except the last (the check digit), then:

  • Starting from the rightmost of those digits, multiply every second digit by 3 and the rest by 1.
  • Add up all the results.
  • Find what you must add to reach the next multiple of 10. That number (0-9) is the check digit.

Formally: check = (10 − (sum mod 10)) mod 10.

A worked example - EAN-13 400638133393?

The first 12 digits are 4 0 0 6 3 8 1 3 3 3 9 3. Working right to left, alternate the weights ×3 and ×1:

digit 4 0 0 6 3 8 1 3 3 3 9 3
weight 1 3 1 3 1 3 1 3 1 3 1 3
product 4 0 0 18 3 24 1 9 3 9 9 9

Sum = 4+0+0+18+3+24+1+9+3+9+9+9 = 89.

89 mod 10 = 9, so check = (10 − 9) mod 10 = 1.

The full valid barcode is therefore 4006381333931. If a feed lists 4006381333930, it is wrong - and that is exactly the kind of error that gets a listing rejected.

In code (Python)

def gtin_check_digit(body: str) -> int:
    total = 0
    for i, ch in enumerate(reversed(body)):
        total += int(ch) * (3 if i % 2 == 0 else 1)
    return (10 - total % 10) % 10

print(gtin_check_digit("400638133393"))  # -> 1

The same algorithm works for UPC-A (12), EAN-13 (13), GTIN-8 and GTIN-14 - only the length changes.

Two things that trip people up

  • Excel eats leading zeros. A UPC like 036000291452 becomes 36000291452 the moment Excel treats the cell as a number, which changes the length and breaks validation. Format the column as Text before pasting.
  • Wrong length. UPC-A is 12 digits, EAN-13 is 13. Padding a UPC with a leading zero turns it into a valid EAN-13 - that is normal, not an error.

The fast way (no math)

If you just need to check or generate a check digit right now, paste the code into a free GTIN / UPC / EAN check-digit calculator - it validates GTIN-8 / UPC-A / EAN-13 / GTIN-14 in the browser and tells you the correct digit when it is wrong.

For bulk work - validating a whole product feed, or wiring this into your own app - there is a deterministic JSON API that checks up to 100 codes per call (and also handles IBAN, EU VAT, VIN, ISIN and more). Same input, same output, every time - no AI guessing.

Comments

No comments yet. Start the discussion.