# GET /01/{gtin}

Resolve a GS1 Digital Link that carries only a GTIN, to the public page of the model's reference passport. 302 response, no API key.

Source: https://docs.sealtrust.io/en/reference/get-gs1-gtin/

---

You send a GTIN and you receive a redirect to the public page of the reference
passport of the matching model. The GTIN, Global Trade Item Number, is the
trade item number printed under the barcode. By the time you leave this page,
you will know how to read the redirect, choose the language of the landing
page, and recognize the error responses.

Full URL:

```http
GET https://api.sealtrust.io/01/{gtin}
```

The same endpoint also answers under the `/v1` prefix, at
`https://api.sealtrust.io/v1/01/{gtin}`. Both URLs call the same code. A GS1
Digital Link carries the path `/01/{gtin}` without a prefix, so that is the
form a code reader encounters.

> [!INFO] This endpoint designates a model
> The path carries a GTIN and no serial number. It therefore names a trade
> reference. The passport targeted is the one attached to a model and to no
> unit. A passport attached to a single item is never served here, even when
> its model carries this GTIN: showing data specific to one item for another
> item of the same model would be false.
>
> The ESPR regulation allows a passport at the model, batch or item level. A
> model passport covers every item that shares the same product code.
>
> To designate a physical item, use the link that also carries the serial
> number, `/01/{gtin}/21/{serial}`.

## Authorization

None, public endpoint. This endpoint answers without an API key, without an
account and without a session cookie.

A partner API key presented here is not read. It changes neither the response
nor the quotas of your plan.

## Rate limit

No ceiling of its own for this endpoint. It falls under the general counter of
the API, shared by every endpoint without a dedicated ceiling and counted per
calling IP address over a 60 second window. This counter is also a single one
for every GTIN value: walking through a thousand different GTINs consumes a
thousand calls from the same budget.

Every response carries three headers that describe this counter.

| Header | Content |
| --- | --- |
| `X-RateLimit-Limit` | the ceiling of the counter over the window |
| `X-RateLimit-Remaining` | what is left to you in the current window |
| `X-RateLimit-Reset` | the reset time, in seconds since January 1, 1970 |

`X-RateLimit-Remaining` stops at zero. Slow down before reaching it, and handle
the 429 code in your client from your very first integration.

This call touches neither the daily quota of an API key, nor the monthly
product quota of your plan.

> [!ATTENTION] Do not build on the value of the general ceiling
> It is a fallback counter, shared by every endpoint that has no ceiling of its
> own. Its value can change without this path being modified. Read the
> `X-RateLimit-*` headers of your responses and hard-code no number in your
> code.

## Path and query parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `gtin` | `string` | yes | The GTIN of the model. The GTIN-8, GTIN-12, GTIN-13 and GTIN-14 formats are accepted, with or without separators. Its last digit must be the check digit of the preceding ones: see below. |

This endpoint reads no query parameter. Everything you add after the `?` is
ignored, including `linkType`, and is not carried over into the redirect. The
`linkType` parameter has an effect only on the links that designate a single
item, `/p/{serial}` and `/01/{gtin}/21/{serial}`. Here the destination is
already the passport.

### Writing the GTIN

The server brings your GTIN back to its canonical form of fourteen digits
before the lookup. It removes every character that is not a digit, then it pads
the result with zeros on the left up to fourteen digits.

These three spellings therefore designate the same model: `3701234567891`,
`03701234567891` and `3-701234-567891`. It is always the fourteen digit form
that appears in the redirect.

A value that contains no digit returns 400. A value that contains more than
fourteen of them returns 400 as well: `0003701234567891` counts sixteen digits
and returns 400.

The lookup finds the model even if the brand recorded its GTIN in a shorter
form, in GTIN-8, GTIN-12 or GTIN-13.

### The check digit of the GTIN

The last digit of a GTIN is its check digit: the GS1 modulo 10 rule computes it
from the digits that precede it. The server recomputes it and compares it
before looking for the model.

So send 8, 12, 13 or 14 digits, the four lengths a GTIN can have, and check
that the last one really is the check digit of the preceding ones. A GTIN that
falls outside this rule gets a 400 code, with the body
`{"detail": "Invalid GTIN: the check digit does not match."}`.

Copy the GTIN from the barcode of the reference. A typing mistake on a single
digit then shows up as soon as you call.

### Choosing the language of the landing page

The redirect points to a page whose URL starts with the language. The server
picks that language from the `Accept-Language` header you send: it keeps the
first value of the header whose main language is `fr` or `en`. With no header,
or with no matching value, it picks `fr`.

The server serves two languages, `fr` and `en`.

### Request headers

| Header | Required | Description |
| --- | --- | --- |
| `Accept-Language` | no | Picks the language of the landing page. `fr` by default. |

## Request body

None. This request has no body.

## Example request

Resolution of the GTIN `03701234567891`. The redirect is not followed, so that
the `Location` header can be read.

:::onglets
```bash title="curl"
curl -i "https://api.sealtrust.io/01/03701234567891"
```
```typescript title="TypeScript (fetch)"
const gtin = "03701234567891";

const response = await fetch(
  `https://api.sealtrust.io/01/${encodeURIComponent(gtin)}`,
  { method: "GET", redirect: "manual" },
);

console.log(response.status);
console.log(response.headers.get("location"));
```
```python
import requests

gtin = "03701234567891"

response = requests.get(
    f"https://api.sealtrust.io/01/{gtin}",
    allow_redirects=False,
    timeout=30,
)

print(response.status_code)
print(response.headers["Location"])
```
:::

To get the page in English, add the language header.

:::onglets
```bash title="curl"
curl -i \
  -H "Accept-Language: en" \
  "https://api.sealtrust.io/01/03701234567891"
```
```typescript title="TypeScript (fetch)"
const gtin = "03701234567891";

const response = await fetch(
  `https://api.sealtrust.io/01/${encodeURIComponent(gtin)}`,
  {
    method: "GET",
    redirect: "manual",
    headers: { "Accept-Language": "en" },
  },
);

console.log(response.status);
console.log(response.headers.get("location"));
```
```python
import requests

gtin = "03701234567891"

response = requests.get(
    f"https://api.sealtrust.io/01/{gtin}",
    headers={"Accept-Language": "en"},
    allow_redirects=False,
    timeout=30,
)

print(response.status_code)
print(response.headers["Location"])
```
:::

> [!INFO] The TypeScript SDK does not cover this endpoint
> `@sealtrust-io/sdk` exposes no method for this URL. The examples above use
> `fetch`, available without a dependency. The tab is therefore titled
> "TypeScript (fetch)".

## Example response

HTTP code `302`. The response has no body. All the information is in the
`Location` header.

```http
HTTP/1.1 302 Found
Location: https://sealtrust.io/fr/passport/01/03701234567891
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 599
X-RateLimit-Reset: 1786000020
Content-Length: 0
```

With the `Accept-Language: en` header, the same request returns this.

```http
HTTP/1.1 302 Found
Location: https://sealtrust.io/en/passport/01/03701234567891
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 598
X-RateLimit-Reset: 1786000020
Content-Length: 0
```

| Header | Content |
| --- | --- |
| `Location` | The full URL of the public page of the reference passport, at the path `/{locale}/passport/01/{gtin}`. The GTIN appears there in its fourteen digit form. |

Read the `Location` header and follow it. Do not hard-code the destination host
in your code: it changes with the domain the call arrives on, and it is the
response that is authoritative.

### A single redirect

This endpoint emits one hop only. The language is already resolved in the URL
returned, so the landing page does not redirect you a second time. The European
registry of digital passports fetches identifier URLs in order to validate
them, and penalizes redirect chains.

### On a brand's custom domain

When a brand serves this endpoint on its own domain name, verified with us, the
redirect stays on that domain. A consumer who scans a product therefore never
leaves the brand's domain.

On such a domain, only the trade references of that brand answer. A GTIN that
belongs to another brand returns 404, with the same message as every other
absence.

## Errors

The body of an error response contains a single field, `detail`.

```json
{
  "detail": "Unknown GS1 Digital Link"
}
```

| Code | Condition | What to do |
| --- | --- | --- |
| 400 | The value sent contains no digit, or contains more than fourteen of them. Message `Invalid GTIN`. | Fix the value. A valid GTIN counts at most fourteen digits, separators excluded. |
| 400 | The value sent does not count 8, 12, 13 or 14 digits, or its last digit is not the check digit of the preceding ones. Message `Invalid GTIN: the check digit does not match.` | Copy the GTIN from the barcode of the reference, then call again. |
| 404 | No public reference passport page answers for this GTIN on this domain. Message `Unknown GS1 Digital Link`. | Check the GTIN, then check in the console that the reference passport of this model is published and that its visibility is public. A passport attached to a single item never answers here. |
| 429 | More than 60 calls in 60 seconds from the same IP address, across every `/01/` path. Message `Rate limit exceeded: 60 requests per 60s`. The response carries a `Retry-After` header in seconds. | Wait the number of seconds given by `Retry-After`, then try again. Follow your consumption with the `X-RateLimit-*` headers and spread your calls out over time. |
| 500 | Unexpected server error. Fixed body `{"detail": "Internal Server Error"}`. | Try again. The `X-Request-Id` header identifies the call, pass it on to us if it repeats. |

This endpoint does not return a 422. An unusable value comes out as a 400, and
a GTIN with no public page comes out as a 404.

> [!ATTENTION] A 404 does not say whether the GTIN exists
> Every absence returns the same code and the same message. That is
> deliberate: different messages would let an automated code reader separate
> the real GTINs from the others. So build no logic that would infer the
> existence of a reference from a 404.

## See also

- [`GET /01/{gtin}/21/{serial}`](/en/reference/get-gs1-gtin-serial/),
  resolve a GS1 link that carries a GTIN and a serial number.
- [`GET /passport/01/{gtin}`](/en/reference/get-passport-gtin/),
  read the published passport of a model, from its GTIN.
- [`GET /p/{serial}`](/en/reference/get-p-serial/),
  translate the printed serial number into a consumer page URL.
- [Physical identification, QR and NFC](/en/identification-physique/),
  choose the physical carrier and the exact form of the GS1 Digital Link.
