Public API v1
Two endpoints. One reads a completed review, the other asks for a token to be
reviewed. Responses are JSON and carry "api_version": "v1".
Before you integrate
A token that has never been reviewed returns 404 with an error
body, not an empty result. Do not treat a missing scan, an empty
response or a failed request as a favourable answer. Every response carries
"unknown_is_safe": false for the same reason.
When part of the supported check set could not be completed, the score is
withheld: score is null, score_withheld is
true and the band reads Insufficient data. The API never
publishes a number the token page would hold back.
Keys and limits
A key is optional. Anonymous requests are served on the tighter public rate
limit; sending a valid key in an X-API-Key header moves you to the
higher one. Reads do not consume credits. Keys are stored as hashes only and
cannot be shown again after they are issued — keep yours somewhere safe.
GET /api/v1/scan/{chain}/{address}
Returns the completed review. chain is one of
ethereum, bsc, polygon, arbitrum, base, solana.
curl -s https://vetmytoken.com/api/v1/scan/ethereum/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 \
-H "X-API-Key: your-key-here"
A response looks like this. Values are illustrative.
{
"api_version": "v1",
"scanned": true,
"chain": "ethereum",
"address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"token": { "name": "Example Token", "symbol": "EXM" },
"scan_id": "…",
"scan_status": "ok",
"verdict": {
"band": "Guarded",
"score": 21,
"score_withheld": false,
"plain_summary": "This token is centrally administered - someone can change how it behaves - but it is established and widely held."
},
"findings": [
{
"title": "An administrator can replace the token's code",
"detail": "An administrator address still controls the upgradeable proxy and can point it at entirely different code without asking holders.",
"severity": "high"
},
{
"title": "Transfers can be switched off",
"detail": "An administrator can pause the token, which stops everyone from moving it until the pause is lifted.",
"severity": "medium"
},
{
"title": "This covers contract controls only",
"detail": "Team identity, promises made off chain, market behaviour and the intentions of whoever holds these powers are not part of this result.",
"severity": "info"
}
],
"checks": {
"erc1967_administrator": { "state": "verified", "value": "0x…" },
"solana_permanent_delegate": { "state": "not_found", "value": null },
"holder_concentration": { "state": "could_not_check", "value": null }
},
"checks_completed": 24,
"checks_unavailable": 3,
"deployer": {
"address": "0x…",
"source": "external_feed",
"activity_seen": null,
"activity_count_is_a_minimum": false
},
"evidence": {
"block": 20431122,
"block_hash": "0x…",
"observed_at": 1754812800,
"completeness": 1.0,
"rules_version": 3
},
"limitations": [
"holder_concentration: not part of the verified control assessment on this chain"
],
"unknown_is_safe": false,
"web_url": "https://vetmytoken.com/token/example-token"
}
Reading the fields
- verdict — the contextual verdict, the same one the token page shows. A young token and a two-year-old token with the same controls do not get the same band.
- findings — at most five items, ordered by what matters to a decision. This is a summary, not the complete evidence list.
- checks — every supported check, keyed by name. The state
is
verified(confirmed present),not_found(not present among the standards supported) orcould_not_check(no usable answer). A check that could not run has not passed. - evidence — the block the reading was pinned to, when it was taken, how much of the check set completed, and the rules version.
- limitations — what could not be established, unedited.
Errors
{
"api_version": "v1",
"scanned": false,
"error": { "code": "scan_not_found", "message": "This token has not been scanned. No result exists for it, which is not the same as a clean result." },
"chain": "ethereum",
"address": "0x…",
"verdict": null,
"findings": null,
"checks": null,
"deployer": null,
"evidence": null,
"limitations": null,
"unknown_is_safe": false
}
Codes: invalid_address (400), scan_not_found (404).
Rate limiting returns 429.
POST /api/v1/scan
Asks for a token to be reviewed. The review runs in the background; this call returns immediately and never contains a result.
curl -s -X POST https://vetmytoken.com/api/v1/scan \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key-here" \
-d '{"chain":"ethereum","address":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"}'
{
"api_version": "v1",
"status": "queued",
"scan_url": "https://vetmytoken.com/api/v1/scan/ethereum/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"web_url": null,
"poll_after_seconds": 30,
"unknown_is_safe": false
}
status is queued when a new review was started, or
exists when one is already on record — in which case
poll_after_seconds is 0 and you can read
scan_url straight away. Wait poll_after_seconds before
the first read; a review that is still running has no row yet, and that read
will correctly return scan_not_found rather than an empty result.
What this is not
These endpoints report supported contract controls at a pinned block. They do not detect fraud, guarantee that a token can be sold, establish who is behind a project, or predict what an administrator will do with the powers listed. Read About for the scope and Data disclosures for where the data comes from.