Public API
A read-only, versioned API for programs: every live token with its pair and status, one token by address, the counts, and the Early Creators race standings. No key, no account, no rate you can buy.
https://www.winr.games/api/v1/tokens
https://www.winr.games/api/v1/tokens/{address}
https://www.winr.games/api/v1/stats
https://www.winr.games/api/v1/campaigns/early-creatorsThe same four operations are described in an OpenAPI document at the gateway’s /api/docs path, if you would rather generate a client (operation ids launchpadV1Tokens, launchpadV1Token, launchpadV1Stats, launchpadV1EarlyCreators).
The API makes no chain reads. Every field is served from the platform’s database behind a cache. That is why it carries identity and status, and not price, market cap, liquidity, volume or holders: each of those needs a chain call or a per-token fan-out, and a public endpoint’s cost would be set by whoever polls it hardest.
Quick Start
curl -s https://www.winr.games/api/v1/tokens | jq '.data.tokens[0]'{
"address": "0xa1bbdf595c7f9df47f2ff9e560569c8055dbce27",
"name": "Paper Moonboy",
"ticker": "PMB",
"pairAddress": "0x…",
"pairFeeBps": 10000,
"quoteTokenAddress": "0x0bd7d308f8e1639fab988df18a8011f41eacad73",
"status": "LIVE",
"launchedAt": "2026-09-09T18:44:02.000Z",
"graduatedAt": null,
"gameSlug": "paper-moonboy",
"gameUrl": "https://just.bet/game/paper-moonboy",
"pageUrl": "https://www.winr.games/token/0xa1bbdf595c7f9df47f2ff9e560569c8055dbce27"
}Envelope
Every response is one of two shapes, told apart by which key is present:
{ "data": { … } } // 200
{ "error": { "statusCode": 503, "type": "FeedUnavailable", "message": "…" } } // 400 / 404 / 429 / 503An empty list is a success ("tokens": [], "count": 0). A 503 means the read failed, and there is never a partial list: you will not receive a short array that looks complete. Retry, and do not treat a 503 as “every token was delisted”.
GET /api/v1/tokens
Every launched token whose game is published and which the operator has not blocked, newest first, tie-broken by address so an unchanged list never reshuffles between polls.
{
"data": {
"tokens": [ … ],
"count": 43,
"chainId": 4663,
"generatedAt": "2026-09-15T12:04:15.407Z"
}
}| Field | Type | Meaning |
|---|---|---|
address | string | Token contract. Full 0x form, lowercase. |
name | string | Display name. |
ticker | string | Symbol, without a leading $. |
pairAddress | string | The Uniswap V3 pool. Full 0x form, lowercase. |
pairFeeBps | integer | Pool fee tier in basis points. 10000 is the 1% tier every launch uses today. |
quoteTokenAddress | string | The other side of the pair: WETH on this chain. |
status | enum | LIVE, GRADUATED or INACTIVE. See below. |
launchedAt | string | Launch block timestamp, ISO-8601 UTC. |
graduatedAt | string or null | Graduation timestamp, ISO-8601 UTC, or null. |
gameSlug | string | The just.bet game this token is. |
gameUrl | string | Where the game is played. |
pageUrl | string | The token’s page on winr.games. |
Status
| Value | Meaning |
|---|---|
LIVE | Launched and trading. Has not reached the graduation threshold. |
GRADUATED | Reached 4.2 ETH of real TWAP pool depth. One-way. |
INACTIVE | Graduated, then no trade for 30 days. Returns to GRADUATED on the next trade. |
Graduating is not a status. The Explore page’s Graduating tab is a view over LIVE tokens whose depth has passed half the threshold. Computing it needs live pool depth, which this API does not carry. Treat an unknown value as “not tradeable” rather than failing.
Which tokens appear
A token is listed when its game is published and an operator has not blocked it. Two consequences:
- Test tokens are excluded. Two tokens exist to test the launchpad itself. They have real pools and appear on the Explore page. They are not in this API, and they should not be traded.
- Unclaimed drafts are excluded. A token whose game has not yet been claimed and published is on chain and tradeable, but is not listed until it is.
The list is therefore a subset of the Explore page. That is the whole difference.
GET /api/v1/tokens/{address}
The same token object for one address, in any case. It is answered out of the cached list, so it is exactly as fresh as the list and costs nothing beyond it.
200with the token.400ifaddressis not0xfollowed by 40 hex characters.404if no listed token has this address: unknown, a test token, or an unclaimed draft.
GET /api/v1/stats
{
"data": {
"tokens": { "total": 43, "live": 43, "graduated": 0, "inactive": 0 },
"chainId": 4663,
"generatedAt": "2026-09-15T12:04:15.407Z"
}
}Counts from the same cached list.
GET /api/v1/campaigns/early-creators
The Early Creators race: window, prize ladder and standings, ranked by indexed swap volume on each token’s pool inside the window.
{
"data": {
"campaign": "early-creators",
"phase": "RUNNING", // BEFORE | RUNNING | ENDED
"windowStartUtc": "2026-09-12T17:00:00.000Z",
"windowEndUtc": "2026-09-19T17:00:00.000Z",
"final": false, // the window has closed
"frozen": false, // the standings are the recorded result
"prizePoolUsd": 1000,
"prizesUsd": [300, 200, 150, 100, 75, 50, 40, 35, 30, 20],
"volumeDefinition": "…",
"entrantCount": 40,
"rankedCount": 12,
"indexedThroughUtc": "2026-09-15T12:03:58.000Z",
"standings": [
{
"rank": 1, // null for an entrant with no trade in the window
"tokenAddress": "0x…",
"ticker": "PMB",
"tokenName": "Paper Moonboy",
"gameSlug": "paper-moonboy",
"volumeEth": "1.234567", // fixed 6 decimal places, as a string
"tradeCount": 12,
"prizeUsd": 300,
"excluded": false
}
],
"generatedAt": "2026-09-15T12:04:00.000Z"
}
}final and frozen are different claims. final means the window has shut. frozen means the result has been recorded. Between the two, the standings are still a live query and may move. An entrant with no trade is unranked, not last.
Caching and Freshness
Use generatedAt in the body to tell how fresh a response is, not the headers.
The application caches each body for 60 seconds (30 for the race), and a CDN edge in front of it caches the same body for the time left on that clock. Both hostnames reach the same edge. On an edge hit you receive a body built earlier together with the headers frozen at that moment, so Age, X-Origin-Cache and X-RateLimit-Remaining may describe an earlier request. generatedAt travels inside the body and cannot be replayed to mean something else.
Plan for a worst case of about two minutes of staleness. A newly launched token is normally visible within a minute and always within two. Polling faster does not change this; most of those requests are answered by an edge and never reach the application.
Rate Limit
30 requests per minute per caller, and 600 per minute for the API as a whole, across all four endpoints. The intended use is one request an hour.
Over either limit returns 429 with X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Scope (which signal identified you) and Retry-After in seconds. Honour Retry-After.
Stability
The path is versioned and the shape is a promise. Fields may be added; none will be renamed, retyped or removed. status may gain a value. A breaking change would be published under /api/v2, and /v1 would keep working.
Example: Watch for New Listings
import time, requests
URL = "https://www.winr.games/api/v1/tokens"
seen = set()
while True:
r = requests.get(URL, timeout=20)
if r.status_code == 429:
time.sleep(int(r.headers.get("Retry-After", 60))); continue
if r.status_code != 200:
time.sleep(300); continue # a failed read; the last list still stands
for t in r.json()["data"]["tokens"]:
if t["address"] not in seen:
seen.add(t["address"])
print(t["ticker"], t["address"], t["pairAddress"], t["status"], t["gameUrl"])
time.sleep(3600)