API overview & auth
degen.zone runs a public REST API. Most of it is open, read-only data - prices, tokens, stats, distribution history. A small, wallet-scoped part lets a creator or bot claim the fees they have earned, and it is strictly non-custodial: you authenticate by signing a message with your own wallet, and every response can only ever move money to that same wallet.
Base URL & versioning
Every endpoint lives under one host with a global /v1 version prefix. Paths in this section are written relative to it.
- Base URL
- api.degen.zone/v1
- Versioning
- /v1 prefix
- Format
- JSON
- Auth scheme
- Bearer JWT (SIWE)
- Health
- GET /health
- Rate limit
- 120 req / min
The read endpoints (tokens, stats, equities, the creator stats widget) need no auth and are CDN-cacheable. Only the wallet-scoped fee-claim endpoints require a token.
Rate limits
A global throttle protects the API; a few sensitive endpoints are tighter. Limits are per IP, per rolling 60-second window.
| Scope | Limit | Window |
|---|---|---|
| Global default | 120 | 60s |
POST /auth/login | 12 | 60s |
POST /fees/claim-tx | 5 | 60s |
Over-limit requests return 429 Too Many Requests. Back off and retry; the read data is cached, so a well-behaved client rarely comes close.
Errors
Errors use standard HTTP status codes and a consistent JSON envelope (the NestJS default):
{
"statusCode": 400,
"message": "address must equal the authenticated wallet",
"error": "Bad Request"
}400- a malformed or contradictory request (bad address, wrong wallet).401- missing/expired JWT on a wallet-scoped endpoint.404- unknown token/resource.429- rate-limited.
Auth: SIWE → JWT
Authentication is Sign-In With Ethereum. There are no API keys and the platform never holds your private key: you prove wallet ownership once by signing a server-issued message, and receive a short-lived JWT to send on subsequent requests.
Step 1 - get a nonce + message. The server builds the exact SIWE message and stores it against the nonce.
curl -s "https://api.degen.zone/v1/auth/nonce?address=0xYourWallet"
# → { "nonce": "…", "message": "degen.zone wants you to sign in…" }Step 2 - sign the message with your wallet (any standard personal_sign / EIP-191 signer) and post it back with the nonce.
curl -s https://api.degen.zone/v1/auth/login \
-H 'content-type: application/json' \
-d '{
"address": "0xYourWallet",
"signature": "0x…",
"nonce": "…"
}'
# → { "token": "<JWT>", "user": { … } }Step 3 - call wallet-scoped endpoints with the token in the Authorization header:
curl -s "https://api.degen.zone/v1/fees/claimable?address=0xYourWallet" \ -H 'authorization: Bearer <JWT>'
Every claim response is built for the address inside the JWT - never a value from the request body. A stolen or shared token cannot redirect a payout to a different wallet; the worst it can do is claim your fees to you.
Metadata commitment
A launch binds its off-chain metadata (name, symbol, description, image and socials) to the chain by committing a single metadataHash in the launch transaction. Anyone - a partner, an indexer, a scanner - can re-derive that hash from public data and prove the metadata a token shows today is exactly what it launched with. The rule is deterministic and has no hidden inputs, so it is safe to reimplement in any language.
The hash is the keccak256 of a canonical JSON serialization:
metadataHash = keccak256( utf8Bytes( canonicalMetadataJson(m) ) )
canonicalMetadataJson(m) = JSON.stringify({
name, symbol, description, image,
website, twitter, telegram, discord,
})Exact serialization rules - every one of them matters for a matching hash:
- Fixed key order, exactly as above:
name, symbol, description, image, website, twitter, telegram, discord. No other keys, ever. - Every value is a string. A field that is absent or null serializes as the empty string
""- notnull, not omitted. - No whitespace and standard JSON escaping - it is a plain
JSON.stringifyof the object above (ECMA-262 escaping), nothing pretty-printed. - No Unicode normalization. Each string is hashed exactly as stored, then UTF-8 encoded to bytes. Do not NFC/NFKC it.
imageis the uploaded image key - thekeyreturned byPOST /uploads/image, exposed asimageKeyon the token record - not a resolved CDN URL.nameandsymbolare the exact strings passed tolaunch()(and readable on-chain via the token'sname()/symbol()).
metadataHash is fixed at launch. A creator can later edit the description, image and socials (PUT /tokens/:address), and that edit intentionally does not re-bind the hash - so a token's current metadata can legitimately differ from what the launch committed. Verify against launch-time fields when you need to reproduce the hash.
Verification recipe. Read the public fields from GET /v1/tokens/<address> (name, symbol, description, imageKey and the socials), rebuild the object in the order above, JSON.stringify it, UTF-8 encode, take the keccak256, and compare it to the metadataHash carried by the token's DirectLaunched event.
input: name = "RUNTIME", symbol = "RUN", everything else = ""
json: {"name":"RUNTIME","symbol":"RUN","description":"","image":"","website":"","twitter":"","telegram":"","discord":""}
bytes: 114 (UTF-8)
hash: 0x4596458049ff16e0dd3b39c5fa063e67916db0ddf23a6d30ee89de25fc1c65f7What else is in this section
- Fee-claim API - list everything a wallet can claim across all six fee rails, and fetch the unsigned claim transaction for each.
- Creator stats API & widget - a public distribution-stats feed for any token, and the drop-in iframe widget that renders it.
