GTIN Validation
Validate, normalize, and troubleshoot GTIN barcodes before API submission.
Every product in EUlabel requires a valid GTIN (Global Trade Item Number). This guide covers the supported formats, validation rules, and common errors.
At a glance
- Accepted lengths: 8, 12, 13, or 14 digits.
- Normalization: EUlabel stores all GTINs as 14 digits, left-padded with zeros.
- Most common bug: incorrect check digit (last digit).
Supported GTIN formats
GTIN-8
8 digits — small items (e.g., 96385074)
GTIN-12 (UPC-A)
12 digits — North America (e.g., 614141000036)
GTIN-13 (EAN-13)
13 digits — Europe/global (e.g., 5601012012200)
GTIN-14
14 digits — cases/pallets (e.g., 15601012012207)
The API normalizes all GTINs to 14 digits by left-padding with zeros. 5601012012200 becomes 05601012012200.
Normalization is purely formatting — it does not change the identifier. Always compute/validate the check digit against the GTIN value after padding to 14 digits.
Validation rules
Check the length
The GTIN must be exactly 8, 12, 13, or 14 digits. Strip any spaces, dashes, or leading/trailing whitespace first.
function isValidLength(gtin) {
const cleaned = gtin.replace(/[\s-]/g, '');
return [8, 12, 13, 14].includes(cleaned.length) && /^\d+$/.test(cleaned);
}Verify the check digit
The last digit is a check digit calculated using the GS1 algorithm:
function validateCheckDigit(gtin) {
const digits = gtin.padStart(14, '0').split('').map(Number);
const checkDigit = digits.pop();
let sum = 0;
for (let i = 0; i < digits.length; i++) {
sum += digits[i] * (i % 2 === 0 ? 3 : 1);
}
const expected = (10 - (sum % 10)) % 10;
return checkDigit === expected;
}Normalize to 14 digits
Before sending to the API, normalize your GTIN:
function normalizeGtin(gtin) {
return gtin.replace(/[\s-]/g, '').padStart(14, '0');
}Common errors
Full validation example
function validateGtin(input) {
const gtin = input.replace(/[\s-]/g, '');
if (!/^\d+$/.test(gtin)) {
return { valid: false, error: 'GTIN must contain only digits' };
}
if (![8, 12, 13, 14].includes(gtin.length)) {
return { valid: false, error: `Invalid length: ${gtin.length}. Expected 8, 12, 13, or 14.` };
}
const padded = gtin.padStart(14, '0').split('').map(Number);
const checkDigit = padded.pop();
let sum = 0;
for (let i = 0; i < padded.length; i++) {
sum += padded[i] * (i % 2 === 0 ? 3 : 1);
}
const expected = (10 - (sum % 10)) % 10;
if (checkDigit !== expected) {
return { valid: false, error: `Check digit invalid. Expected ${expected}, got ${checkDigit}.` };
}
return { valid: true, normalized: gtin.padStart(14, '0') };
}def validate_gtin(raw: str) -> dict:
gtin = raw.replace(" ", "").replace("-", "")
if not gtin.isdigit():
return {"valid": False, "error": "GTIN must contain only digits"}
if len(gtin) not in (8, 12, 13, 14):
return {"valid": False, "error": f"Invalid length: {len(gtin)}"}
padded = gtin.zfill(14)
digits = [int(d) for d in padded]
check_digit = digits[-1]
total = sum(d * (3 if i % 2 == 0 else 1) for i, d in enumerate(digits[:-1]))
expected = (10 - (total % 10)) % 10
if check_digit != expected:
return {"valid": False, "error": f"Check digit invalid. Expected {expected}, got {check_digit}."}
return {"valid": True, "normalized": padded}Test GTINs
Use these valid GTINs in the sandbox: