# Barcode Validator (EAN, UPC, GTIN) (`opendatatools/ean-upc-validator`) Actor

Validates and normalises EAN, UPC and GTIN barcodes: check digit, format, GTIN-14 normalisation, UPC-E expansion and GS1 prefix allocation. No external lookups, runs offline and deterministically.

- **URL**: https://apify.com/opendatatools/ean-upc-validator.md
- **Developed by:** [Nico Poland](https://apify.com/opendatatools) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 valid barcode checkeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Barcode Validator (EAN, UPC, GTIN)

Validates and normalises EAN, UPC and GTIN barcodes: check digit, format,
GTIN-14 normalisation, UPC-E expansion and GS1 prefix allocation. No
external lookups; runs offline and deterministically.

### What it does

Send a list of barcodes, get one row back for each, in the same order, so
the output joins straight onto your input. For every barcode it tells you:

- whether the **check digit** is correct, and what it should have been;
- the **format**: GTIN-8, UPC-E, GTIN-12 (UPC-A), GTIN-13 (EAN-13) or
  GTIN-14;
- the **GTIN-14** form, zero-padded, the shape GS1 recommends storing, and
  the only reliable way to match `12345670` against `00000012345670`;
- the **UPC-A expansion** for UPC-E codes, which cannot be recovered by
  padding because the compression depends on the last digit's value;
- the **GS1 prefix** and what it is allocated to, including ISBN, ISSN,
  coupon and restricted-circulation ranges.

Everything is computed locally from the published GS1 specification. There
is no API behind this, nothing to rate-limit, and the same input always
gives the same output, so it is safe to call from an agent or a pipeline
at any volume.

**What it does not do.** It does not look products up. A valid barcode
means the number is well-formed and its check digit is correct; it does
not mean the code has been issued to anyone, or that a product exists.
Product lookup requires a licensed commercial database, which this Actor
deliberately does not use.

### Input

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `barcodes` | array of strings | yes | n/a | Up to 50,000 per run. Spaces and hyphens are ignored. |
| `failOnEmpty` | boolean | no | `false` | Operator health check; leave off. |

```json
{
  "barcodes": ["5449000000996", "036000291452", "9780306406157", "1234567890123"]
}
```

### Output

One row per input, in order. A real row from a real run:

```json
{
  "input": "5449000000996",
  "valid": true,
  "format": "GTIN-13",
  "gtin": "5449000000996",
  "gtin14": "05449000000996",
  "check_digit": 6,
  "expected_check_digit": 6,
  "gs1_prefix": "544",
  "prefix_allocation": "Belgium and Luxembourg",
  "prefix_type": "gs1_member_organisation",
  "is_restricted_circulation": false,
  "source": "GS1 General Specifications (computed locally)",
  "source_url": "https://www.gs1.org/standards/barcodes",
  "license": "Computed from the published GS1 specification; no third-party data",
  "retrieved_at": "2026-09-06T12:00:00+00:00"
}
```

And a rejection:

```json
{
  "input": "5449000000997",
  "valid": false,
  "check_digit": 7,
  "expected_check_digit": 6,
  "reason": "Check digit is 7; the GS1 modulo-10 algorithm gives 6 for these digits."
}
```

| Field | Type | Description |
|---|---|---|
| `input` | string | Your value, echoed back exactly. Present on every row, including empty ones, so nothing breaks the join. |
| `valid` | boolean | Well-formed and check digit correct. |
| `format` | string | `GTIN-8`, `UPC-E`, `GTIN-12`, `GTIN-13` or `GTIN-14`. |
| `gtin14` | string | Zero-padded to 14. Use this to match across sources. |
| `expected_check_digit` | integer | What GS1's algorithm computes. Differs from `check_digit` exactly when invalid. |
| `reason` | string | Why it was rejected. Absent when valid. |
| `gs1_prefix` | string | Prefix digits, read correctly for the detected format. |
| `prefix_allocation` | string | Which GS1 Member Organisation issued the range, or its special purpose. |
| `prefix_type` | string | `gs1_member_organisation`, `restricted_circulation`, `isbn`, `issn`, `coupon`, `refund_receipt`, `reserved` or `demonstration`. |
| `is_restricted_circulation` | boolean | `true` for internal codes. **These are not globally unique**. Do not treat them as product identifiers. |
| `upc_a` | string | 12-digit UPC-A form, for UPC-E codes. |
| `indicator_digit` | string | First digit of a GTIN-14: `0` base item, `1`–`8` packaging level, `9` variable measure. |
| `also_valid_as` | string | Set when an 8-digit code reads validly as both GTIN-8 and UPC-E, which is real ambiguity in the standard, surfaced rather than guessed at. |

### A note on prefixes and countries

GS1 is explicit that **prefixes do not identify a product's country of
origin**. A company can license a prefix from the GS1 organisation of its
choice and manufacture anywhere. So `prefix_allocation` names the GS1
Member Organisation that *issued* the number, and the field is
deliberately not called `country`. Tools that label it that way are
telling you something false about supply chains.

### Pricing

Pay per event. You are charged **$0.005 per valid barcode**, plus Apify's
standard $0.00005 Actor start event.

**Invalid barcodes are returned free.** You should not pay to be told your
own input was malformed.

**Example:** 1,000 barcodes of which 900 are valid = 900 × $0.005 +
$0.00005 = **$4.50**.

### Limits and refresh cadence

- **No external source.** Nothing to be down, nothing to rate-limit,
  no cache. Runs are deterministic: identical input, identical output.
- **Throughput:** up to 50,000 barcodes per run. 50,000 completes in about
  a minute and a half.
- **Coverage:** GTIN-8, UPC-E, GTIN-12 (UPC-A), GTIN-13 (EAN-13) and
  GTIN-14. SSCC-18 logistics labels and ISBN-10 are out of scope.
- **Prefix table:** GS1 reassigns and adds prefix ranges occasionally.
  Unassigned ranges are reported as such rather than guessed at.
- **Validity is arithmetic, not existence.** See *What it does not do*.

### Attribution

Implements the check digit and prefix scheme published in the
[GS1 General Specifications](https://www.gs1.org/standards/barcodes). No
third-party data is fetched, stored or redistributed, and no barcode you
submit leaves the run.

### Changelog

See the Changelog tab.

### Issues

Report problems on the Issues tab. Please include the barcode and what you
expected. Breakage is normally fixed within one working day.

# Actor input Schema

## `barcodes` (type: `array`):

Barcodes to check. Spaces and hyphens are ignored, so values pasted from a spreadsheet work as-is. Accepts GTIN-8, UPC-E, UPC-A (12), EAN-13 and GTIN-14.

## `failOnEmpty` (type: `boolean`):

Leave this off for normal use. The daily monitor run turns it on so that 'succeeded but returned nothing' shows up as a failure instead of passing silently.

## Actor input object example

```json
{
  "barcodes": [
    "5449000000996",
    "1234567890123"
  ],
  "failOnEmpty": false
}
```

# Actor output Schema

## `results` (type: `string`):

Every row this run produced, in the default dataset.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "barcodes": [
        "5449000000996",
        "036000291452",
        "9780306406157"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("opendatatools/ean-upc-validator").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "barcodes": [
        "5449000000996",
        "036000291452",
        "9780306406157",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("opendatatools/ean-upc-validator").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "barcodes": [
    "5449000000996",
    "036000291452",
    "9780306406157"
  ]
}' |
apify call opendatatools/ean-upc-validator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,opendatatools/ean-upc-validator"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/w3TGyDy8j03GhSwvw/builds/gaunhD5kSHKla5xjj/openapi.json
