Skip to main content

Overview

A leaderboard is the cheapest way to make a prediction market product feel alive. It gives a new user something to look at before they have traded, it turns wallets into pages worth visiting, and it feeds directly into copy trading. It is also the easiest thing in this API to build, because the Data API reads any public wallet with no funded account. You can ship a fully populated leaderboard before a single user signs up. The parts worth getting right are subtler: which ranking you show, which fields the window actually applies to, and how to cache it so a popular page does not consume your entire rate limit.
Read time: about 12 minutes. Front-end or full-stack context assumed.

TL;DR

  • GET /leaderboard ranks by realized PnL. /leaderboard/volume ranks by USDC traded. They surface different people.
  • The window parameter does not apply to every field. Fee totals, streaks, and drawdown are all-time regardless.
  • Cost basis always comes from the wallet’s first ever trade, not the window start. That is what makes a 7-day figure meaningful.
  • Numbers are JSON strings. Parse with a decimal type.
  • Cache aggressively. Rankings move slower than users refresh.
  • 503 with available: false means computing, not failed.

What you will do

  • Fetch both rankings and understand what each is telling you
  • Render a leaderboard without float precision bugs
  • Build a trader profile page from four endpoints
  • Paginate a full trade history correctly
  • Cache within your rate limit
  • Handle the processing state for wallets nobody has queried before

What you will need

Knowledge
  • A backend and a front-end framework of your choice
Tools and access
  • A Bravado API key. No collateral needed; every endpoint here is read-only.

Two rankings

Showing only PnL is the common choice and the misleading one. It puts a wallet that made one correct call above a trader who has been consistently profitable for a year, because the leaderboard sorts on total rather than repeatability. Offering both as a toggle costs almost nothing and gives users a much more honest picture. If you show only one, say which.

The window parameter is partial

window does not apply to every field in the response. Fee totals, streak metadata, and drawdown statistics are computed all-time regardless of what you pass. Labelling them as belonging to the selected period is wrong, and users will notice when a “7-day” drawdown does not change as they switch windows.
What the window does control is which trades are included in the ranking metric. What it never changes is cost basis. That is always reconstructed from the wallet’s first ever trade. A 7-day PnL figure still uses the true cost of a position opened two years ago, which is exactly what makes the number meaningful rather than an artefact of where you cut the period.

Fetch and render

Expected output:
Parsing these as floats produces totals that disagree with the API. Values are strings specifically to prevent that. Use Decimal, BigDecimal, or your language’s arbitrary-precision equivalent. See Numeric conventions.

Trader profiles

A leaderboard row should open something worth reading. Four calls cover a good profile: The category breakdown is the most interesting of these in product terms and the most often omitted. It shows whether someone is broadly capable or good at exactly one thing, which is the difference between a trader worth following and a trader worth following in politics only.
That profile is also the input to copy trading. See Track a whale wallet.

Paginate history

Trade logs and position lists are paginated. Raising limit is not a substitute for following the cursor:
For a heavy wallet this can be a lot of rows, which is a good reason to do it in a background job rather than in a request handler.

Cache within your budget

A leaderboard is read far more often than it changes, and every request counts against rate_limit_per_min.
Sensible defaults: Without caching, a leaderboard page that renders 50 rows and fetches a profile per row will exhaust a 120-per-minute budget on a single page load. Cache the board, and fetch profiles only when a row is opened. On 429, honour Retry-After rather than retrying immediately, which extends the limit rather than clearing it.

Handle the processing state

PMWAS and statement endpoints compute a wallet’s full history on first request. For an address nobody has queried before, available: false is expected. An error toast here is wrong; nothing failed.

Wrapping up

The leaderboard itself is two endpoints and some formatting. The judgement is in what you show and how you label it. Show both rankings if you can, because PnL alone rewards luck over repeatability. Do not label all-time fields as belonging to the selected window. Cache, because the data changes far more slowly than users refresh. And parse everything as decimals, because a leaderboard that disagrees with the venue by a cent is the kind of bug users screenshot.

Frequently asked questions

PnL is the more intuitive default, but pair it with a volume toggle. On its own it puts one lucky bet above a year of consistency, and users will draw the wrong conclusion from that.
They are computed all-time regardless of the window. So are streak metadata and drawdown statistics. Label them accordingly rather than implying they belong to the selected period.
No. Every Data API read works on any public address with no collateral and no permission from the wallet owner.
Almost certainly float parsing. Values arrive as JSON strings to preserve precision, and converting them to floats introduces exactly the drift you are seeing.
Every 60 to 120 seconds is ample. Rankings do not move meaningfully faster, and refreshing harder mostly consumes rate limit you will want for profile loads.
Its history is being computed. Poll on a reasonable interval and show a processing state rather than an error.

Resources