Commerce CensusContactRequest access

Home / Barcode Lookup API — GTIN, UPC & EAN

Barcode Lookup API — GTIN, UPC & EAN

One numbering family at four lengths, why check digits matter, and measured coverage of where barcodes exist.

A barcode lookup API resolves a UPC, EAN or GTIN to a product. The formats are one family — a GTIN-12 is a GTIN-13 with a leading zero — so a lookup should normalise before matching. The real limit is coverage: barcode presence ranges from near-universal in packaged goods to entirely absent on some major sources.

One family, four lengths

UPC, EAN and GTIN are not competing standards, which is the first thing that trips people up. They are the same numbering scheme at different lengths, and the shorter forms are the longer ones with leading zeros. Normalise to a common length before comparing and an entire class of false negatives disappears.

NameDigitsRelationship
GTIN-8 / EAN-88Short form for very small packaging
GTIN-12 / UPC-A12Zero-pad to 13 and it is the EAN
GTIN-13 / EAN-1313The common international form
GTIN-14 / ITF-1414A case or carton containing inner units

GTIN-14 is the one that causes silent errors. It identifies a case, not the unit inside it, so treating a GTIN-14 as equivalent to the GTIN-13 it wraps will match a carton of twelve to a single item — and the price comparison that follows is off by an order of magnitude while looking perfectly well-formed.

Check digits catch real errors

The final digit is computed from the others: sum the digits with alternating weights of 3 and 1, then take what is needed to reach the next multiple of ten. It costs nothing to verify and catches transcription errors and truncation before they enter your catalogue as a product that will never match anything.

Check-digit validation
def gtin_valid(code: str) -> bool:
    d = [int(c) for c in code.zfill(14)]
    body, check = d[:-1], d[-1]
    # weights alternate 3,1 from the right of the body
    total = sum(v * (3 if i % 2 == 0 else 1) for i, v in enumerate(body))
    return (10 - total % 10) % 10 == check

Coverage is the real constraint

A lookup API is only as useful as the fraction of your catalogue that carries a barcode at all, and that fraction varies enormously by category. We measured field coverage across 1,993 products and 11,265 variants in nine industries. Electronics and grocery are close to universal. Apparel and jewellery are close to zero, because much of that catalogue is made by the seller and never enters a barcode registry.

Source matters as much as category. Public storefront endpoints expose a barcode field that is present in the schema and empty on every store we measured — the platform returns it only through the merchant's own credentials. One large retailer includes the barcode in its cheap bulk search response; another omits it there and carries it only on the detail page, which decides whether identity resolution is free or costs an extra request per product.

A barcode is not always a product

What a lookup should return

Not just a name. A useful resolution returns the normalised identifier at a canonical length, the product identity it maps to, the other identifiers that identity carries, and — critically — how confident the mapping is and when it was last confirmed. A barcode mapping is an observation like any other: it was true when it was made, and it can drift.

Where identifiers come from, and who assigns them

A barcode is not a fact about an object, it is a registration. A brand licenses a company prefix from a numbering authority and allocates item references within it, which has two consequences worth knowing. The prefix identifies the brand owner rather than the manufacturer, so a contract-manufactured product carries the brand's prefix and not the factory's. And allocation is the brand's choice, which is why the same physical item can carry different codes in different regions with no pattern you can infer.

Normalising before you match

Most apparent identifier mismatches are formatting rather than substance, and a short normalisation step removes them. Pad to a common length, strip separators and whitespace, validate the check digit, and reject anything that fails rather than storing it — a malformed identifier in a catalogue is worse than a missing one, because it will match nothing forever while looking like data.

Normalise, then match
def normalise(code: str) -> str | None:
    c = "".join(ch for ch in code if ch.isdigit())
    if len(c) not in (8, 12, 13, 14):
        return None            # not a GTIN shape
    c = c.zfill(14)            # one canonical length for comparison
    return c if gtin_valid(c) else None

# store the canonical form AND what you were given — the original is
# evidence about the source, the canonical form is what you join on.

What to do when there is no barcode

For a large share of real catalogues — apparel, jewellery, handmade goods, private label, most of what independent brands sell — there is no identifier and there never will be. The answer is not a better lookup service; it is to accept that matching becomes inference and to report it as such.

Questions

Common questions

What is the difference between UPC, EAN and GTIN?

They are the same numbering family at different lengths. A 12-digit UPC-A becomes a 13-digit EAN by adding a leading zero; GTIN is the umbrella term. Normalise to a single length before matching and most apparent mismatches resolve.

Can I look up any product by barcode?

Only products that have one. Coverage is near-universal in packaged consumer goods and close to zero in apparel, jewellery and handmade goods. Any lookup service claiming complete coverage is describing something the underlying data does not support.

Why does the same product have several barcodes?

Regional variants, packaging refreshes, multipacks and successor models each get their own code. This is why a product identity should hold a set of identifiers rather than a single one.

How do I validate a barcode?

Recompute the check digit — the last digit is derived from the others with alternating weights of 3 and 1. It is a few lines of code and it catches transcription and truncation errors before they enter your catalogue.

Related

hub

Product Data API

Measured field coverage across 1,993 products and 11,265 variants in nine industries: where GTINs exist, and which fields you cannot rely on.

→
hub

Price Comparison API & Tools

What separates a working price comparison engine from a list of prices: identity, landed cost, freshness and the honest answer when no equivalent exists.

→
hub

How to Track Prices Across Retailers

What price tracking really requires: where the data comes from, what each route costs, and why identity is harder than collection. A practical 2026 guide.

→