Method GET/certificate /{identifier}
Read a product's certificate of authenticity from its certificate number, its UID hash, its token id or its printed serial number. Public endpoint.
On this page
You are reading a product's certificate of authenticity. When you leave this page, you will know which identifier to send, how to read the certificate status, and which responses to expect when the product or the certificate does not exist.
Full address:
GET https://api.sealtrust.io/v1/certificate/{identifier}The same endpoint also answers without the /v1 prefix, at
https://api.sealtrust.io/certificate/{identifier}. Both addresses call the
same code. Use the /v1 form for a new integration.
#
None, public endpoint. Send neither an API key nor a session token.
The response carries no internal brand or product identifier. It gives the certificate number, the status, the dates, the product name, the brand name and its styling. It gives no brand number, no product number, no contract address and no owner address.
#Rate limit
600 calls per 60-second window, counted per IP address.
This counter applies to reading the certificate, with or without the /v1
prefix. Downloading its PDF has its own counter, of 60 calls a minute: building
the file costs far more than a read.
Every accepted response carries three headers.
| Header | Content |
|---|---|
X-RateLimit-Limit | the limit applied over the window, here 600 |
X-RateLimit-Remaining | what is left to you in the current window |
X-RateLimit-Reset | the timestamp for the end of the window, in seconds |
A refusal returns 429 with the same three headers, X-RateLimit-Remaining at
0, and Retry-After equal to 60.
#Path and query parameters
| Name | Type | Required | Description |
|---|---|---|---|
identifier | string | yes | The product or the certificate to read. Four forms are accepted, described below. |
This endpoint has no query parameter.
#The four identifier forms
| Form | What it looks like |
|---|---|
| Certificate number | A string that starts with ST-CERT-, followed by 12 characters. It is the certificate_number field this same response returns. |
| UID hash | 0x followed by 64 hexadecimal characters, that is 66 characters in all. Case does not matter. |
| Token id | The token's whole number, written in digits. It has 77 or 78 digits. |
| Printed serial number | The 12 characters carried on the product label, the ones found in the /p/{serial} address. |
The server first looks for a certificate number. If it finds none, it looks at
the shape of the string: 0x followed by 64 hexadecimal characters is treated
as a UID hash, any other shape as a token id. As a last resort it looks for a
printed serial number.
The server strips leading and trailing spaces before the lookup, whatever the form.
The server ignores the case of the serial number and brings the characters that
are confused when read back to their canonical form before the lookup: I and
L count as 1, O counts as 0. So you still find a number copied by hand
from a label even if the person typed the letter O where the label carries the
digit 0.
#Headers
| Name | Type | Required | Description |
|---|---|---|---|
Accept | string | no | If it contains text/html, the response is a 307 redirect to the public certificate page, readable by a human. Any other value, including */* and application/json, gives the JSON described below. |
curl, requests and fetch send */* by default and therefore receive the
JSON. The redirect exists so that a certificate link shared and opened in a
browser displays the public certificate page.
#Request body
None. This request has no body.
#Example request
Reading the certificate whose number is ST-CERT-000000000000.
curl -i -H "Accept: application/json" https://api.sealtrust.io/v1/certificate/ST-CERT-000000000000const response = await fetch(
"https://api.sealtrust.io/v1/certificate/ST-CERT-000000000000",
{
method: "GET",
headers: {
Accept: "application/json",
},
},
);
console.log(response.status);
console.log(response.headers.get("X-RateLimit-Remaining"));
console.log(await response.json());import requests
response = requests.get(
"https://api.sealtrust.io/v1/certificate/ST-CERT-000000000000",
headers={
"Accept": "application/json",
},
timeout=30,
)
print(response.status_code)
print(response.headers["X-RateLimit-Remaining"])
print(response.json())The TypeScript SDK @sealtrust-io/sdk does not cover this endpoint. The example
above uses fetch, available with no dependency.
The three other identifier forms are written the same way.
# UID hash
curl -i -H "Accept: application/json" https://api.sealtrust.io/v1/certificate/0x0000000000000000000000000000000000000000000000000000000000000000
# Token id
curl -i -H "Accept: application/json" https://api.sealtrust.io/v1/certificate/10000000000000000000000000000000000000000000000000000000000000000000000000000
# Printed serial number
curl -i -H "Accept: application/json" https://api.sealtrust.io/v1/certificate/00000000ABCD#Example response
HTTP code 200.
{
"certificate_number": "ST-CERT-000000000000",
"status": "active",
"issued_at": "2026-03-04T10:22:07.415000Z",
"expires_at": null,
"issuer_name": "Exemple SAS",
"product_name": "Tote bag model 1",
"brand_name": "Exemple SAS",
"brand_logo_url": "https://example-sas.test/logo.svg",
"brand_primary_color": "#1F2937",
"brand_hide_powered_by": false,
"brand_public_host": null,
"brand_served_hosts": [],
"custom_fields": {
"atelier": "Atelier 3",
"matiere": "cuir pleine fleur"
}
}The response has twelve fields and nothing else.
| Field | Type | Description |
|---|---|---|
certificate_number | string | The number of the certificate. It is the value to reuse as identifier to find this certificate directly. |
status | string | The status of the certificate. See below. |
issued_at | string | Issue date and time, in universal time, in ISO 8601 format. |
expires_at | string or null | End of validity date. No certificate issued by the platform carries one today, the value is always null. Do not build your integration on an end date. |
issuer_name | string or null | The name of the brand that issued the certificate. The server computes this field at read time and always puts the brand name in it. null when the certificate is attached to no brand. |
product_name | string or null | The name of the product covered by the certificate. |
brand_name | string or null | The name of the issuing brand. |
brand_logo_url | string or null | The address of the brand logo, to display the certificate in the brand's colors. |
brand_primary_color | string or null | The primary color of the brand. |
brand_hide_powered_by | boolean | true when the "Powered by SealTrust" wording must disappear. Two conditions, and both are required: the brand's plan includes white labeling and the brand has chosen to hide the wording in its console. By default the wording stays, including for a brand that has just subscribed to Inside and changed nothing. |
brand_public_host | string or null | The domain name under which the brand publishes its public pages, for example verify.example-sas.test. Populated only when the brand has white labeling and that domain has been verified, or, for a reseller's client brand with no domain of its own, when that reseller's shared domain is verified. Otherwise null. A page displaying this certificate uses it to declare its canonical address and its sharing previews under the brand's domain rather than ours. |
brand_served_hosts | array of string | Every verified domain name the brand's pages are served on: its own and its reseller's shared one. brand_public_host is only the preferred one. A page uses it to accept both, so that a label already printed on the reseller's domain keeps opening its page once the brand adopts a domain of its own. Empty list when the brand is served only on ours. |
custom_fields | object or null | The free fields the brand filled in when issuing the certificate. null when it filled in none. The content is specific to each brand, no key is imposed. |
#How to read status
The server computes the status at read time. The value stored in the database is not copied as is.
status is either active or revoked.
- A revoked certificate is returned as
revokedforever. Revocation is a deliberate act and it prevails over every other rule. No endpoint returns a revoked certificate in theactivestatus. - Every other certificate is returned as
active.
The vocabulary of the API contains a third value, expired. The server computes
it at read time from expires_at. Since no certificate carries an end date
today, the API does not return it. Accept it in your code to stay robust if it
appears one day, and build no business rule on its presence.
#Redirect to the readable page
If your request announces text/html in the Accept header, the response is a
307 whose Location header points to the public certificate page. The server
returns no JSON body in that case.
The destination follows the brand: the domain the brand has declared and had
verified when it has one, sealtrust.io otherwise. A certificate that cannot be
found also redirects, to sealtrust.io, where the page says plainly that it
found nothing. A browser therefore never receives a JSON 404 from this
endpoint.
#Errors
The body of an error response carries a detail field.
| Code | Condition | What to do |
|---|---|---|
| 404 | The requested path matches no route, for example because the identifier contains an unencoded slash. detail is Not Found. | Encode the identifier before placing it in the address. |
| 404 | No product matches this identifier, or the matching product has been destroyed, replaced or archived. detail is Product not found. | Check the form of the identifier. A serial number is typed as it appears on the label, over 12 characters. |
| 404 | The product exists, but no certificate has ever been issued for it. detail is No certificate found for this product. | The product can be genuine without carrying a certificate. Use the product passport to display it. |
| 404 | The call arrives on a brand's verified domain name, and the certificate belongs to another brand. detail is Product not found. | Call this address on the domain of the brand concerned, or on api.sealtrust.io. The message is the same as for an unknown identifier, so as not to confirm that it exists. |
| 429 | The limit of 600 calls per 60 seconds is reached for your IP address. detail is Rate limit exceeded: 600 requests per 60s. The Retry-After header and the X-RateLimit-* family accompany the response. | Wait the number of seconds indicated by Retry-After, then try again. The PDF download has its own counter. |
| 500 | An unexpected error occurred while your call was being processed. detail is Internal Server Error. | Try again. If the error persists, contact support giving the time of the call. |
#See also
GET /certificate/{identifier}/download, download the certificate of authenticity as a PDF.GET /p/{serial}, translate the printed serial number into a consumer page address.GET /resolve/{identifier}, read in one call everything a product page displays.- Core concepts, tell a model, a batch and an item apart before ordering a single label.
Was this page helpful?
Your answer opens a pre-filled email in your mail app, addressed to contact@sealtrust.io. You read it over before sending it.