Fetch the complete documentation index at: https://eulabel.eu/docs/llms.txt Use this file to discover all available pages before exploring further. Full content: https://eulabel.eu/docs/llms-full.txt Append .md to any page URL for markdown, or send Accept: text/markdown.

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

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:

On this page