# GS1 GTIN Check Digit and UDI Element String Python Generator

> Source: https://netray.co/tools/gs1-gtin-check-digit-udi-element-string-python-generator
> Publisher: Netray (netray.co), AI and integration engineering for ERP and manufacturing systems.
> Section: Free Tools

## Short answer

Fill in your GTIN base, lot, expiry and serial and get a ready Python script that calculates the check digit and prints the GS1 human readable and encoded strings.

## Category

developer

## Industry

Medical Devices

## Audience

- ERP developer
- Regulatory affairs
- Packaging engineer

## Tool

**Kind:** template

**Output:** code

**Language:** python

**Fields:**

#### GTIN base (13 digits, without check digit)

**Key:** gtin_base

**Type:** text

**Default:** 0123456789012

**Help:** Indicator digit plus company prefix plus item reference, 13 digits in total

#### Expiration date (YYMMDD)

**Key:** expiry

**Type:** text

**Default:** 281031

#### Lot number

**Key:** lot

**Type:** text

**Default:** LOT24A017

#### Serial number (blank if lot controlled)

**Key:** serial

**Type:** text

**Blocks:**

#### gtin_udi.py

**Body:** # GS1 GTIN-14 check digit and UDI element string builder
# Application identifiers: 01 GTIN, 17 expiry (YYMMDD), 10 lot, 21 serial

GTIN_BASE = "{{gtin_base}}"
EXPIRY = "{{expiry}}"
LOT = "{{lot}}"
SERIAL = "{{serial}}"
FNC1 = "\x1d"  # group separator used between variable length elements in the encoded data

def gs1_check_digit(digits):
    """Return the GS1 mod 10 check digit for a string of digits.
    Weights alternate 3, 1, 3, 1 starting from the rightmost digit of the input."""
    if not digits.isdigit():
        raise ValueError("GTIN base must contain digits only")
    total = 0
    for position, char in enumerate(reversed(digits)):
        weight = 3 if position % 2 == 0 else 1
        total += int(char) * weight
    return (10 - total % 10) % 10

def build_gtin(base):
    if len(base) != 13:
        raise ValueError("GTIN base must be 13 digits to make a GTIN-14")
    return base + str(gs1_check_digit(base))

def build_strings(gtin, expiry, lot, serial):
    parts_hri = ["(01)" + gtin]
    parts_data = ["01" + gtin]
    if expiry:
        parts_hri.append("(17)" + expiry)
        parts_data.append("17" + expiry)
    if lot:
        parts_hri.append("(10)" + lot)
        parts_data.append("10" + lot)
    if serial:
        if lot:
            parts_data[-1] = parts_data[-1] + FNC1
        parts_hri.append("(21)" + serial)
        parts_data.append("21" + serial)
    return "".join(parts_hri), "".join(parts_data)

if __name__ == "__main__":
    gtin = build_gtin(GTIN_BASE)
    hri, data = build_strings(gtin, EXPIRY, LOT, SERIAL)
    print("GTIN-14      :", gtin)
    print("Human readable:", hri)
    print("Encoded data  :", data.replace(FNC1, "<GS>"))

**Notes:** Variable length elements such as lot (10) and serial (21) need a separator after them when another element follows. Fixed length elements (01, 17) do not. Element order and symbology are set by your labeler and the issuing agency rules, so check the GS1 General Specifications and your UDI issuing agency requirements. The GUDID device identifier is the GTIN, the production identifiers are lot, expiry and serial.

## Content

**Intro:** UDI labels built on GS1 carry a device identifier (the GTIN) and production identifiers (lot, expiry, serial) in application identifier format. This generator produces a small Python script that calculates the GTIN check digit and assembles the human readable and encoded strings.

**Sections:**

#### How to use it

**Body:** Enter the first 13 digits of your GTIN-14, an expiry date in YYMMDD, the lot number and optionally a serial number. Copy the script into a file and run it with Python 3. It prints the full GTIN-14, the human readable string with parentheses, and the encoded string with a visible group separator marker. Feed the encoded value to your barcode printing software or label system.

**Bullets:**

- 13 digit GTIN base, no check digit
- Expiry in YYMMDD, blank to omit
- Serial blank for lot controlled products
- Run with Python 3, no external libraries

#### The check digit method

**Body:** GS1 check digits use modulo 10. Starting from the rightmost digit of the data, multiply digits alternately by 3 and 1, add the products, and subtract the sum from the next multiple of ten. For base 0123456789012 the weighted sum is 3 times (2+0+8+6+4+2) plus 1 times (1+9+7+5+3+1) which equals 66 + 26 = 92, so the check digit is 8. The script automates this and rejects non digit input.

#### Element string rules

**Body:** In the human readable form, application identifiers are shown in parentheses. In the encoded form no parentheses are used, and variable length elements that are not last are followed by a separator character (FNC1 in the barcode, shown as group separator code 29 in text). Fixed length elements such as the GTIN and expiry need none. Many UDI regulations require both a human readable and an automatic identification form on the label.

#### Common mistakes

**Body:** Typical errors are including the check digit in the base so the result is 15 digits, using a four digit year in the expiry, putting the serial before the lot when the label standard specifies another order, and leaving out the separator after lot. Validate printed barcodes with a verifier or scanner and check them against the GUDID record.

**Cta:** If your ERP master data should generate compliant UDI strings automatically at pack-out, Netray can discuss how to connect that to your label system.

**Faq:**

#### Is the GTIN the same as the UDI?

**Answer:** The UDI has two parts. The device identifier (UDI-DI) is the GTIN, and the production identifier (UDI-PI) is the lot, expiry, serial or manufacture date. Together they form the UDI carried on the label.

#### Does this support GS1 DataMatrix?

**Answer:** It builds the data string that goes into a GS1 DataMatrix or GS1-128 symbol. You still need barcode software or a font to draw the symbol, and a leading symbology identifier is added by the encoder.

#### Which date format should I use?

**Answer:** GS1 element 17 uses YYMMDD. A day of 00 can be used where a day is not applicable in GS1 rules, but check what your regulator and GUDID submission expect.

#### Can I use this for HIBC or ICCBBA?

**Answer:** No. This generates GS1 strings only. HIBC and ICCBBA use different data structures and check character schemes.

## Search terms

gtin check digit calculator python, gs1 udi element string, udi di pi barcode generator, gtin-14 check digit, gs1 application identifiers 01 17 10 21, udi label data string, gs1 128 udi python, gudid di gtin

---
Talk to an engineer: https://netray.co/contact?intent=erp_ai | Machine catalog: https://netray.co/agent/catalog.json
