{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreigt5jmoh4upje5gdxxqnwrqryttwai7mw6tughrypf7daibrmwbgq",
"uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3mpuvnowfczj2"
},
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreialg3qn4k3njo5wpfleqgn45irgxph55er3o56u3qmxgggqko6dei"
},
"mimeType": "image/webp",
"size": 72306
},
"path": "/rosariov25/how-to-validate-a-gtin-ean-13-barcode-check-digit-gs1-mod-10-explained-5boh",
"publishedAt": "2026-07-05T05:06:06.000Z",
"site": "https://dev.to",
"tags": [
"barcode",
"api",
"webdev",
"ecommerce",
"GTIN / UPC / EAN check-digit calculator",
"JSON API"
],
"textContent": "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.\n\nHere is exactly how the check digit works, with a worked example you can follow by hand.\n\n## What is a GTIN check digit?\n\nGTIN (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.\n\n## The GS1 Mod-10 algorithm, step by step\n\nTake all the digits **except** the last (the check digit), then:\n\n 1. Starting from the **rightmost** of those digits, multiply every second digit by **3** and the rest by **1**.\n 2. Add up all the results.\n 3. Find what you must add to reach the next multiple of 10. That number (0-9) is the check digit.\n\n\n\nFormally: `check = (10 − (sum mod 10)) mod 10`.\n\n## A worked example — EAN-13 `400638133393?`\n\nThe 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:\n\n\n digit: 4 0 0 6 3 8 1 3 3 3 9 3\n weight: 1 3 1 3 1 3 1 3 1 3 1 3\n product: 4 0 0 18 3 24 1 9 3 9 9 9\n\n\nSum = 4+0+0+18+3+24+1+9+3+9+9+9 = **89**.\n89 mod 10 = 9, so check = (10 − 9) mod 10 = **1**.\n\nThe 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.\n\n## In code (Python)\n\n\n def gtin_check_digit(body: str) -> int:\n total = 0\n for i, ch in enumerate(reversed(body)):\n total += int(ch) * (3 if i % 2 == 0 else 1)\n return (10 - total % 10) % 10\n\n print(gtin_check_digit(\"400638133393\")) # -> 1\n\n\nThe same algorithm works for UPC-A (12), EAN-13 (13), GTIN-8 and GTIN-14 — only the length changes.\n\n## Two things that trip people up\n\n * **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.\n * **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.\n\n\n\n## The fast way (no math)\n\nIf 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.\n\nFor **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.",
"title": "How to Validate a GTIN / EAN-13 Barcode Check Digit (GS1 Mod-10, explained)"
}