Leaderboard API
The leaderboard API for consumer apps
Add recurring, all-time, and segmented leaderboards to your app in under an hour. Trophy handles scaling high-frequency leaderboard infrastructure so you stay focused on user experience. Start for free, scale to millions of users.
Quickstart
Quickstart: rank users from metric events
Send metric events to update rankings automatically, then fetch the board or a user’s position. Segment rankings with user attributes when you need cohorts.
import { TrophyApiClient } from "@trophyso/node";
const trophy = new TrophyApiClient({ apiKey: process.env.TROPHY_API_KEY });
// 1. Track activity that powers the leaderboard
const eventResponse = await trophy.metrics.event("lessons", {
user: {
id: "user-123",
attributes: { city: "London" },
},
value: 1,
});
console.log("Leaderboard updates:", eventResponse.leaderboards);
// 2. Fetch the full board rankings
const board = await trophy.leaderboards.get("weekly-words");
for (const entry of board.rankings) {
console.log(entry.rank, entry.userName, entry.value);
}
// 3. Fetch one user's rank and history
const ranking = await trophy.users.leaderboards("user-123", "weekly-words");
console.log("Rank:", ranking.rank, "Value:", ranking.value);Buy vs. build
Why use Trophy's leaderboard API?
Building a real-time leaderboard API from scratch is a classic distributed systems challenge. Ranking at scale requires more than a sorted query. Trophy's API handles the infrastructure so you can keep focused on the user experience.
Integration Time
Reliability
Scalability
Ongoing Maintenance
Feature Development
Endpoints
Leaderboard API endpoints
Use Trophy's leaderboard API to fetch real-time rankings and user positions. Leaderboards populate automatically from the metric events you track — no additional ranking code needed.
Send a metric event to track user activity and power leaderboards.
Path Parameters
keystringrequiredUnique reference of the metric as set when created.
Request Body
userUserrequiredThe user that triggered the event (must include id).
valuenumberrequiredThe value to add to the user's current total for the given metric.
Response
eventIdstringUnique identifier for the created event.
totalnumberThe user's new total for this metric.
leaderboardsLeaderboard[]Changes to leaderboard rankings.
import { TrophyApiClient } from '@trophyso/node';
const trophy = new TrophyApiClient({
apiKey: 'YOUR_API_KEY'
});
// Track a user event
await trophy.metrics.event("lessons", {
user: {
id: "user-123",
"attributes": {
"city": "London" // Optional, segment rankings by user attributes
}
},
value: 1
}
);
// This single event:
// - Records user activity
// - Computes leaderboard rankings
// - Returns new leaderboard rankings
Get a specific leaderboard and rankings. Supports pagination and filtering.
Path Parameters
keystringrequiredUnique key of the leaderboard as set when created.
Query Parameters
limitintegerMaximum number of rankings to return (default: 10).
offsetintegerNumber of rankings to skip for pagination.
userIdstringThe ID of the user to center the rankings around.
rundateSpecific run date (YYYY-MM-DD) to fetch rankings for.
Response
rankingsRanking[]Ranked list of users with scores.
runUnitstringTime unit for recurring leaderboards (e.g., "day").
runIntervalnumberNumber of runUnits per leaderboard period.
import { TrophyApiClient } from '@trophyso/node';
const trophy = new TrophyApiClient({
apiKey: 'YOUR_API_KEY'
});
// Fetch leaderboard rankings
const board = await trophy.leaderboards.get("weekly-words");
// Response:
// {
// rankings: [
// { rank: 1, userId: "...", value: 5000, userName: "Sarah" },
// { rank: 2, userId: "...", value: 4500, userName: "Alex" },
// ...
// ],
// runUnit: "day",
// runInterval: 7
// }Get a user's rank, value, and history for a specific leaderboard.
Path Parameters
idstringrequiredThe user's ID in your database.
keystringrequiredUnique key of the leaderboard as set when created.
Query Parameters
rundateSpecific run date (YYYY-MM-DD) to fetch rankings for.
Response
ranknumberUser's current position on the leaderboard.
valuenumberUser's current score/value.
historyHistoryEntry[]Rank and value changes over time.
import { TrophyApiClient } from '@trophyso/node';
const trophy = new TrophyApiClient({
apiKey: 'YOUR_API_KEY'
});
// Get a user's leaderboard data
const ranking = await trophy.users.leaderboards(
"user-123", "weekly-words"
);
// Response:
// { rank: 2, value: 4500, history: [...] }Submission
Score submission and anti-cheat: client- vs server-authoritative
How you submit scores decides how hard your leaderboard is to cheat. There are two models.
In a client-authoritative model, the app reports a final score directly ("this user scored 4,200"). It is fast to build, but anyone can open the network tab, replay the request, or modify the client and post an arbitrary number.
In a server-authoritative model, the client reports what happened (a lesson finished, a level cleared), and your backend validates the activity before it counts. This is the safer default for competitive boards.
Trophy leaderboards are server-authoritative. They populate from metric events sent through the server-side SDKs (Node, Go, Java, .NET, PHP, Python, Ruby), authenticated with a secret API key. The client triggers an activity event on your server; your server sends it:
trophy.metrics.event("lessons", {
user: { id: "user_123" },
value: 1,
});- Keep the secret API key on your server. A key shipped in a web or mobile client can be extracted and used to write arbitrary events.
- Send activity events (`value: 1` for one completed lesson), not a client-reported score total. Let the leaderboard accumulate from events your backend has already validated.
- Concurrent writes use atomic score updates, so two events landing at once cannot corrupt a rank.
Client-authoritative submission is fine for low-stakes boards. If the ranking carries real stakes (prizes or paid tiers), keep submission on your server.
Resets
Automatic resets: daily, weekly, monthly
A leaderboard can run forever (all-time) or reset on a schedule — daily, weekly, monthly, or a custom interval. Each cycle starts everyone at zero.
Reset schedule is dashboard configuration, not application code. Your app keeps sending the same metric events, and Trophy assigns each event to the current period. Switching an all-time board to weekly does not require code changes.
GET /leaderboards/{key} returns runUnit (for example, "day") and runInterval (the number of runUnits in one period), alongside the current rankings. Pass a run query parameter (YYYY-MM-DD) to fetch a past run instead of the current one:
GET /leaderboards/{key}?run=2024-05-06
GET /users/{id}/leaderboards/{key}?run=2024-05-06Past runs are archived, not overwritten. The user endpoint returns that user's rank, value, and history for the run you request, so finished cycles stay queryable for recaps or audits.
Built for production workloads
Reliable infrastructure designed for performance at scale.
- Uptime SLA
- 99.99%
- p95 API Latency
- 200ms
- Monthly API Requests
- 150M
- Users Tracked
- 1M+
FAQ
Frequently asked questions
Have a different question and can’t find the answer you’re looking for? Send us an email.
Get started