API reference · v1
One request.
The real token, or a copy.
Send SEAL a token. It answers with the label, the rule that decided it and the evidence behind it, written so your product can show it as it is.
Real answers from the SEAL engine, recorded on 13 November 2026, shown without evidence. Requests need an API key: see the Quickstart.
- Interface
- REST, JSON over HTTPS
- Version
- v1
- Coverage
- Elysium launchpads
- Access
- By request
Quickstart
From your key to a label in your product, in four steps. Everything below works the same way for every token on an Elysium launchpad.
-
Keep your key on your server
Access comes with a base URL and an API key that starts with
seal_live_. Store both as environment variables on your server. Never put the key in a web page or an app.export SEAL_API="<base URL from onboarding>" export SEAL_API_KEY="seal_live_…"
-
Ask about one token
Name the token by its chain and contract address, and send the key as a bearer token. This one is Pantheon, the largest project launch SEAL confirms.
curl "$SEAL_API/v1/tokens/elysium:0x892723bf41f2e1af70adb1ff19036810888473a3" \ -H "Authorization: Bearer $SEAL_API_KEY"
const res = await fetch( `${process.env.SEAL_API}/v1/tokens/elysium:0x892723bf41f2e1af70adb1ff19036810888473a3`, { headers: { Authorization: `Bearer ${process.env.SEAL_API_KEY}` } }, ); const seal = await res.json();import os, requests seal = requests.get( f"{os.environ['SEAL_API']}/v1/tokens/elysium:0x892723bf41f2e1af70adb1ff19036810888473a3", headers={"Authorization": f"Bearer {os.environ['SEAL_API_KEY']}"}, ).json() -
Read three fields
coveredsays whether SEAL labels the token at all.labelis the verdict.basisis the reason, one sentence you can show as it is. Whencoveredisfalse, show nothing.curl -s "$SEAL_API/v1/tokens/elysium:0x892723bf41f2e1af70adb1ff19036810888473a3" \ -H "Authorization: Bearer $SEAL_API_KEY" | jq '{covered, label, basis}'if (!seal.covered) return; // memes, farms, unconfirmed: show nothing if (seal.label === "Copy") { warn(seal.basis, seal.identity.original); // offer the real token instead } else { badge(seal.label, seal.basis); }if not seal["covered"]: return # memes, farms, unconfirmed: show nothing if seal["label"] == "Copy": warn(seal["basis"], seal["identity"]["original"]) # offer the real token instead else: badge(seal["label"], seal["basis"]) -
Check whole lists at once
For search results, trending lists and portfolios, ask for up to 100 tokens in one request with Retrieve many. Then read Showing a label before it goes live.
elysium:0x892723bf41f2e1af70adb1ff19036810888473a3Pantheon · Likely originalelysium:0x678fb3ab55ea769e18ce06ae5bc34201f50fcdddPANTHEON · Copyelysium:0xca0b28e9aabeee7cee96fb21d3906e3092fbc4e1DEATHBOX · Unresolvedelysium:0xcde473802659ffce784ff89a6d6d42035d4a603fpepe · not covered
Introduction
The SEAL API answers one question per token: is this the token the project launched, a copy of it, or something SEAL does not label. Every answer carries its reason in one sentence, the rule behind it, and the onchain facts it rests on.
The API is read-only. Every endpoint is a GET, and every response is JSON.
Conventions
- EncodingUTF-8 JSON. Field names are
snake_case. - TimesISO 8601 in UTC, to the second:
2026-11-08T14:02:11Z. - AddressesAccepted in any case, always returned in lowercase hex.
- Absent valuesFields that do not apply are
null, never left out, except the objects that only covered tokens carry.
$SEAL_API/v1
The base URL and your API key are issued with access. Examples on this page read them from SEAL_API and SEAL_API_KEY.
Access
The API is opening to a small number of platforms first. Access is not self-serve: the base URL, credentials and request limits are set up with each platform during onboarding, together with the guidelines for showing the SEAL mark.
Every platform reads the same labels under the same rules. Access never changes what a token is called, and no platform gets different rules for its own tokens.
Authentication
Every request carries your API key as a bearer token in the Authorization header. Keys start with seal_live_. A missing key returns 401 missing_api_key; a wrong or revoked one returns 401 invalid_api_key. Keep the key on your server: a key shipped in a web page or an app can be read by anyone who opens it.
Limits
Each key has a request limit per minute, agreed during onboarding. Every response says where you stand in three headers: X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset, the Unix time the window resets. Over the limit, requests return 429 rate_limited with a Retry-After header in seconds. Use Retrieve many for lists: one request answers up to 100 tokens.
curl -i "$SEAL_API/v1/tokens/elysium:0x892723bf41f2e1af70adb1ff19036810888473a3" \
-H "Authorization: Bearer $SEAL_API_KEY"
HTTP/1.1 200 OK
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 119
X-RateLimit-Reset: 1794595740
Token references
A token is named by its chain and its contract address, separated by a colon. The same form is used in requests and in every response field that points to another token.
| Chain | Prefix | Covers |
|---|---|---|
| Elysium | elysium | Every token launched through an Elysium launchpad since they opened on 2 November 2026. |
Other chains return unsupported_chain. A reference that is not a chain, a colon and a 40-character hex address returns invalid_reference.
elysium:0x892723bf41f2e1af70adb1ff19036810888473a3
└─ chain ─┘ └──────────── contract address ────────────┘
The token object
What SEAL knows about one token. Tokens SEAL does not label carry the first five fields and covered: false; covered tokens add identity and evidence.
tokenobjectThe token itself.
chainstringAlways elysium in v1.
addressstringThe contract address, lowercase.
namestringThe name the token was launched with.
symbolstringThe ticker it was launched with.
coveredbooleanWhether SEAL labels this token. false for memes, tweet plays, launch farms, and project launches whose own channels do not show any of their tokens yet. Show nothing when it is false.
labelstring or nullThe verdict: Original, Likely original, Copy, Same creator or Unresolved. null when the token is not covered. See Labels and rules.
basisstringWhy the token has this label, in one sentence written to be shown to your users as it is. Present on uncovered tokens too, where it says why there is no label.
rulestringThe rule that decided the label, as a stable id. Use it for logic; use basis for display.
rules_versionstringThe version of the rules the label was decided under, dated: 2026-11-13.1. A label keeps the version it was decided under until it changes.
updated_attimestampWhen the label, its rule or its basis last changed.
identityobjectThe token’s identity: every token launched with the same name and ticker, after normalization (case, spacing, lookalike characters).
tokensintegerHow many tokens share the identity.
walletsintegerHow many different wallets launched them.
positionintegerThis token’s place in launch order, from 1.
earliestreferenceThe first token launched with the identity. Launch order is a fact, not a verdict: copies are often launched before the project’s token.
originalreference or nullThe token the project’s own channels point to. Offer it in place of a copy. null while the evidence is unresolved.
evidenceobjectThe onchain facts behind the label, each one checkable on an explorer.
creation_txstringThe transaction that created the token.
blockintegerThe block it was created in.
created_attimestampThe block time.
creatorstringThe wallet that launched it.
launchpadstringThe launchpad it was created through.
{
"token": {
"chain": "elysium",
"address": "0x678fb3ab55ea769e18ce06ae5bc34201f50fcddd",
"name": "PANTHEON",
"symbol": "PANTHEON"
},
"covered": true,
"label": "Copy",
"basis": "Uses the name and ticker of 0x8927…73a3, the token the project's website pantheonperps.com points to. Launched after it by a different wallet (0x9267…0ac6).",
"rule": "copy",
"rules_version": "2026-11-12.1",
"updated_at": "2026-11-12T17:18:14Z",
"identity": {
"tokens": 31,
"wallets": 29,
"position": 31,
"earliest": "elysium:0xd336e95c6cef4bf56919b23a39303f1e8de0d452",
"original": "elysium:0x892723bf41f2e1af70adb1ff19036810888473a3"
},
"evidence": {
"creation_tx": "0x39b6dc23b51efccd3a92a2e64c57e53323700f90f08385c7787668ce2ab410d0",
"block": 5392322,
"created_at": "2026-11-12T14:38:15Z",
"creator": "0x9267589d3d7cdb8b189dab38901d6c965be90ac6",
"launchpad": "Launchpad A"
}
}
A real response: the 31st token named Pantheon, a copy. Read on 13 November 2026.
Labels and rules
Labels apply to identities, never to single tokens in isolation: once the project’s own channels confirm one token, every other token in its identity is labelled against it. Within an identity, the first rule that holds decides. The Method sets out each rule in full.
channels
The project’s own website and its X account both show this contract address. Not returned yet: the X check is in development, so no token reaches Original today.
The SEAL mark.
website
The project’s own website shows this contract address. One channel so far.
The outlined mark with the words Likely original. Never as Original.
copy
Same identity as the token the project’s channels point to, launched by a different wallet, before or after it.
A warning, the basis, and identity.original as the alternative.
linked
Launched by the wallet behind the project’s token. A relaunch or a test, not a copy.
A neutral note. No mark.
conflicting
The project’s channels point to tokens launched by different wallets. No original is named until the evidence settles it.
The word Unresolved and the basis. No mark on any token.
scope
Not a project launch: memes, tweet plays, wallets that launch tokens in bulk.
Nothing.
unconfirmed
A project launch whose own website and X account do not show any of its identity’s tokens yet.
Nothing.
own-channels
A token with its own, different website and X account: possibly a separate project with the same name.
Nothing.
Treat an unknown label as no label and an unknown rule as text: new ones can be added without a new API version. History entries from earlier rules versions can carry rule ids that are no longer issued.
GET/v1/tokens/{reference}
Retrieve a token
Returns the token object for one token: its label, the rule and basis behind it, its identity and its evidence.
Path parameters
referencestringrequiredThe token, as elysium:0x…. See Token references.
Returns
A token object. 404 token_not_found when SEAL has no record of the address, for example when it was not created through an Elysium launchpad.
curl "$SEAL_API/v1/tokens/elysium:0x892723bf41f2e1af70adb1ff19036810888473a3" \ -H "Authorization: Bearer $SEAL_API_KEY"
const res = await fetch(`${SEAL_API}/v1/tokens/elysium:${address}`, {
headers: { Authorization: `Bearer ${SEAL_API_KEY}` },
});
const seal = await res.json();
// show nothing for tokens SEAL does not cover
if (seal.covered && seal.label === "Copy") {
warn(seal.basis, seal.identity.original);
}
import requests
auth = {"Authorization": f"Bearer {SEAL_API_KEY}"}
seal = requests.get(f"{SEAL_API}/v1/tokens/elysium:{address}", headers=auth).json()
# show nothing for tokens SEAL does not cover
if seal["covered"]:
show_label(seal["label"], seal["basis"])
{
"token": {
"chain": "elysium",
"address": "0x892723bf41f2e1af70adb1ff19036810888473a3",
"name": "Pantheon",
"symbol": "PANTHEON"
},
"covered": true,
"label": "Likely original",
"basis": "The project's website pantheonperps.com shows this contract address. One channel so far: SEAL has not yet checked the project's X account.",
"rule": "website",
"rules_version": "2026-11-12.1",
"updated_at": "2026-11-12T15:13:15Z",
"identity": {
"tokens": 31,
"wallets": 29,
"position": 19,
"earliest": "elysium:0xd336e95c6cef4bf56919b23a39303f1e8de0d452",
"original": "elysium:0x892723bf41f2e1af70adb1ff19036810888473a3"
},
"evidence": { … }
}
A real response: Pantheon’s own token, 19th of 31 with the name. See the case study.
GET/v1/tokens?ids={references}
Retrieve many
Returns up to 100 tokens in one request, for search results, lists and portfolios.
Query parameters
idsstringrequiredToken references separated by commas, at most 100. Duplicates are answered once.
Returns
dataarrayToken objects, in the order asked.
not_foundarrayReferences SEAL has no record of. They do not fail the request.
A malformed reference fails the whole request with 400, so a typo is never mistaken for an unknown token.
curl "$SEAL_API/v1/tokens?ids=elysium:0x892723bf41f2e1af70adb1ff19036810888473a3,elysium:0x678fb3ab55ea769e18ce06ae5bc34201f50fcddd" \ -H "Authorization: Bearer $SEAL_API_KEY"
const ids = results.map((r) => `elysium:${r.address}`).join(",");
const res = await fetch(`${SEAL_API}/v1/tokens?ids=${ids}`, {
headers: { Authorization: `Bearer ${SEAL_API_KEY}` },
});
const { data } = await res.json();
const verdicts = new Map(data.map((t) => [t.token.address, t]));
ids = ",".join(f"elysium:{r['address']}" for r in results)
body = requests.get(f"{SEAL_API}/v1/tokens", params={"ids": ids}, headers=auth).json()
verdicts = {t["token"]["address"]: t for t in body["data"]}
{
"data": [
{ "token": { … }, "covered": true, "label": "Likely original", … },
{ "token": { … }, "covered": true, "label": "Copy", … }
],
"not_found": []
}
GET/v1/tokens/{reference}/history
Label history
Every verdict the token has had, newest first. Labels are never overwritten in place: when the label, its rule or its basis changes, a new entry is recorded and the old one stays.
Returns
tokenreferenceThe token asked about.
historyarrayEntries, newest first. Each has covered, label, rule, basis, rules_version and at, the time it was recorded.
The history is how a platform, a project or a trader can see why a label changed, and when.
curl "$SEAL_API/v1/tokens/elysium:0x892723bf41f2e1af70adb1ff19036810888473a3/history" \ -H "Authorization: Bearer $SEAL_API_KEY"
const res = await fetch(`${SEAL_API}/v1/tokens/elysium:${address}/history`, {
headers: { Authorization: `Bearer ${SEAL_API_KEY}` },
});
const { history } = await res.json();
history = requests.get(f"{SEAL_API}/v1/tokens/elysium:{address}/history", headers=auth).json()["history"]
{
"token": "elysium:0x892723bf41f2e1af70adb1ff19036810888473a3",
"history": [
{
"covered": true,
"label": "Likely original",
"rule": "website",
"basis": "The project's website pantheonperps.com shows this contract address. One channel so far: SEAL has not yet checked the project's X account.",
"rules_version": "2026-11-12.1",
"at": "2026-11-12T15:13:15Z"
},
…
]
}
A real response, shortened. Pantheon’s full label history, from Likely original by launch order to Copy, Unresolved and its current label, is told in the case study.
Errors
Errors use standard HTTP status codes and one shape: a stable code to branch on, and a message written for the developer reading the log. Messages can be reworded; codes do not change within v1.
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_reference | The reference is not chain:0x followed by 40 hex characters. |
| 400 | unsupported_chain | The chain is not covered. v1 covers Elysium only. |
| 400 | missing_ids | Retrieve many was called without ids. |
| 400 | too_many_ids | More than 100 references in one request. |
| 401 | missing_api_key | The request has no Authorization: Bearer header. |
| 401 | invalid_api_key | The key is not valid or has been revoked. |
| 404 | token_not_found | SEAL has no record of the token. |
| 404 | not_found | There is no endpoint at this path. |
| 429 | rate_limited | The key’s limit per minute is used up. Wait the seconds in Retry-After. |
| 500 | internal_error | Something failed on SEAL’s side. Retry the request. |
A token launched in the last few seconds can return token_not_found before SEAL has recorded it. Retry rather than caching the miss.
{
"error": {
"code": "token_not_found",
"message": "SEAL has no record of elysium:0x00c4…7e21. It may not be a token launched through an Elysium launchpad, or it launched in the last few seconds."
}
}
Freshness
A label follows the evidence, so it can change: a project adds its contract address to its website, a website goes offline, or a second team claims the name. SEAL re-reads the evidence on a schedule set by how much it is likely to move.
Responses are sent with Cache-Control: no-store. Read the label when you show it. If you keep a copy, keep it briefly, and never show a label the API has replaced.
Versioning
The API version is in the path. Within /v1, SEAL only makes additive changes: new fields, new rule ids, new endpoints. Ignore fields you do not know. Anything that would break a working integration ships as a new version, and /v1 keeps running alongside it.
The rules are versioned separately. rules_version on every label says which rules decided it, and the Method sets out the rules in force.
/v1The shape of requests and responses.2026-11-13.1How labels are decided. Current today.Showing a label
The label is only as useful as the way it is shown. Four rules cover almost every surface; the full guidelines, with the mark’s sizes and colours, are in Showing the SEAL mark.
- Show nothing when
coveredis false.No mark, no “unverified” tag. Silence is the correct answer for tokens SEAL does not label. - Show the basis as it is.It is written for your users, and it is what makes the label checkable.
- Never show Likely original as Original.One channel is not two. The outlined mark and the word Likely always stay.
- Never imply safety.The mark says where a token came from, not whether it is a good trade.
Building on Elysium?
Access is by request, and opening to a small number of platforms first.