Leaderboard
Rankings are computed from a periodic snapshot, refreshed every 60 seconds. Numbers can be up to ~60 seconds stale.
GET /api/v1/leaderboard
Returns the ranked top traders for a category and time window.
Auth: None
Rate limit: v1: 60 req / 60 s per IP
Cache: no-store (privacy flags are evaluated on every request)
Query parameters
| Param | Type | Default | Description |
|---|---|---|---|
category | string | "all" | "all" or a category slug from /categories |
window | string | "all" | "day" | "week" | "month" | "ytd" | "all" |
limit | integer | 50 | Number of rank slots (1–100) |
Response
{
"category": "all",
"window": "all",
"entries": [
{
"isPrivate": true,
"rank": 1
},
{
"userId": "e5f6a7b8-c9d0-1234-ef01-345678901234",
"handle": "alice",
"displayName": "Alice",
"avatarUrl": null,
"rank": 2,
"plCents": 482300,
"volCents": 1900000,
"winRate": 0.62,
"roi": 0.25,
"settledCount": 84
}
]
}| Field | Type | Description |
|---|---|---|
category | string | Echo of the requested category |
window | string | Echo of the requested window |
entries | array | Ranked traders and private rank placeholders, best first |
entries[].isPrivate | boolean | Present and true only for a private-profile or opted-out trader; that object contains no fields other than rank |
entries[].rank | integer | Position in this leaderboard (1 = top) |
entries[].userId | UUID string | Public entries only: trader user id |
entries[].handle | string | Public entries only: trader handle |
entries[].displayName | string | Public entries only: display name |
entries[].avatarUrl | string | null | Public entries only: avatar URL; null if none set |
entries[].plCents | integer | Public entries only: settled profit/loss in EUR-cents |
entries[].volCents | integer | Public entries only: settled trading volume in EUR-cents |
entries[].winRate | number | Public entries only: wins / settled trades, 0–1; 0 when settledCount is 0 |
entries[].roi | number | Public entries only: plCents / volCents; 0 when volCents is 0 |
entries[].settledCount | integer | Public entries only: number of settled trades in the window |
A trader whose profile or leaderboard appearance is private still occupies an existing snapshot rank, but the public response exposes only { "isPrivate": true, "rank": N }. User id, handle, display name, avatar, and every performance field are omitted.
Errors
| Status | Body | When |
|---|---|---|
400 | { "error": "Unknown category: ..." } | Category slug does not exist |
GET /api/v1/leaderboard/summary
Returns aggregate stats for a category and window: totals plus the top trader, biggest single win, and the median trader.
Auth: None
Rate limit: v1: 60 req / 60 s per IP
Cache: no-store (the response can contain trader identity)
Query parameters
| Param | Type | Default | Description |
|---|---|---|---|
category | string | "all" | "all" or a category slug from /categories |
window | string | "all" | "day" | "week" | "month" | "ytd" | "all" |
Response
{
"category": "all",
"window": "all",
"traderCount": 312,
"totalVolumeCents": 48200000,
"openInterestCents": 9100000,
"topTrader": {
"userId": "e5f6a7b8-c9d0-1234-ef01-345678901234",
"handle": "alice",
"displayName": "Alice",
"avatarUrl": null,
"plCents": 482300
},
"biggestSingleWin": {
"userId": "f6a7b8c9-d0e1-2345-f012-456789012345",
"handle": "bob",
"displayName": "Bob",
"avatarUrl": null,
"marketId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"marketTitle": "Reform Party",
"eventId": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"eventTitle": "Which party will win the most seats?",
"plCents": 91000
},
"medianTrader": {
"plCents": 1200,
"winRate": 0.5,
"settledCount": 12
},
"snapshotAt": "2026-01-15T14:30:00.000Z"
}| Field | Type | Description |
|---|---|---|
category | string | Echo of the requested category |
window | string | Echo of the requested window |
traderCount | integer | Identity-public traders with settled trades in the window; anonymous rank slots are not counted |
totalVolumeCents | integer | Settled trading volume in EUR-cents |
openInterestCents | integer | Open interest in EUR-cents |
topTrader | object | null | Highest-P/L trader; null when no data |
topTrader.plCents | integer | Settled P/L in EUR-cents |
biggestSingleWin | object | null | Largest single-market win; null when no data |
biggestSingleWin.marketId | UUID string | Market the win was on |
biggestSingleWin.marketTitle | string | Market title |
biggestSingleWin.eventId | UUID string | null | Parent event id; null for a standalone market |
biggestSingleWin.eventTitle | string | null | Parent event title; null for a standalone market |
biggestSingleWin.plCents | integer | P/L of that win in EUR-cents |
medianTrader | object | null | Median-trader stats; null when no data |
medianTrader.plCents | integer | Median settled P/L in EUR-cents |
medianTrader.winRate | number | Median win rate, 0–1 |
medianTrader.settledCount | integer | Median settled-trade count |
snapshotAt | ISO 8601 | null | When the snapshot was taken; null before the first refresh |
topTrader, biggestSingleWin, and medianTrader carry the same userId / handle / displayName / avatarUrl fields as a leaderboard entry. Before the first snapshot is written, every aggregate is 0 or null and snapshotAt is null.
GET /api/v1/leaderboard/me
Returns your own leaderboard entry for a category and window.
Auth: Required
Rate limit: v1-user: 120 req / 60 s per user (keyed on user id)
Cache: no-store
Query parameters
| Param | Type | Default | Description |
|---|---|---|---|
category | string | "all" | "all" or a category slug from /categories |
window | string | "all" | "day" | "week" | "month" | "ytd" | "all" |
Response
self is null when you have no settled trades in the window. Otherwise it is a complete public-entry shape, even when you have opted out of the public leaderboard; the authenticated response is only available to you.
{
"self": {
"userId": "e5f6a7b8-c9d0-1234-ef01-345678901234",
"handle": "alice",
"displayName": "Alice",
"avatarUrl": null,
"rank": 14,
"plCents": 23100,
"volCents": 140000,
"winRate": 0.55,
"roi": 0.16,
"settledCount": 22
}
}No settled trades:
{ "self": null }Errors
| Status | Body | When |
|---|---|---|
400 | { "error": "Unknown category: ..." } | Category slug does not exist |
401 | { "error": "Unauthorized" } | Missing or invalid token |