Feature Recipes
Learn how to implement common game features with the LightLeaderboard API.
Seasons & Periodic Resets
Run monthly or weekly ranked seasons while keeping historical data.
Instead of deleting scores at the end of the month, pass a seasonId when submitting scores. Your game client decides what the "current" season is.
// 1. Submit a score tagged with the current season
curl -X POST "http://localhost:5173/api/v1/games/my-game/scores" \
-H "Authorization: Bearer <GAME_API_KEY>" \
-H "Content-Type: application/json" \
-d '{"score": 500, "playerRefId": "player_1", "seasonId": "season_2024_05"}'
// 2. Fetch the leaderboard for ONLY that season
curl "http://localhost:5173/api/v1/games/my-game/leaderboard?season=season_2024_05" \
-H "Authorization: Bearer <GAME_API_KEY>"| Rank | Player Ref | Score | Season ID |
|---|---|---|---|
| 1 | player_3 | 800 | season_2024_05 |
| 2 | player_1 | 500 | season_2024_05 |
Team / Guild Leaderboards
Show players how they rank amongst their friends or clan members.
Pass a teamId when submitting scores. This allows you to filter the leaderboard to only include players in that specific group.
// 1. Submit a score tagged with a team
curl -X POST "http://localhost:5173/api/v1/games/my-game/scores" \
-H "Authorization: Bearer <GAME_API_KEY>" \
-H "Content-Type: application/json" \
-d '{"score": 900, "playerRefId": "player_2", "teamId": "guild_warriors"}'
// 2. Fetch the leaderboard restricted to that team
curl "http://localhost:5173/api/v1/games/my-game/leaderboard?team=guild_warriors" \
-H "Authorization: Bearer <GAME_API_KEY>"| Rank | Player Ref | Score | Team ID |
|---|---|---|---|
| 1 | player_2 | 900 | guild_warriors |
| 2 | player_4 | 850 | guild_warriors |
Custom Metadata & Replays
Attach extra context to a score submission.
You can send an arbitrary JSON object in the metadata field. This is perfect for storing the character used, level data, or a reference ID to a replay file hosted elsewhere.
// 1. Submit a score with custom JSON metadata
curl -X POST "http://localhost:5173/api/v1/games/my-game/scores" \
-H "Authorization: Bearer <GAME_API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"score": 1200,
"playerRefId": "player_1",
"metadata": {
"character": "mage",
"time_taken_sec": 45,
"replay_id": "file_881923"
}
}'| Rank | Player Ref | Score | Metadata |
|---|---|---|---|
| 1 | player_1 | 1200 | { "character": "mage", "time_taken_sec": 45, "replay_id": "file_881923" } |
Relative / Centric Leaderboards
Show a player exactly where they stand, alongside the players immediately above and below them.
Instead of fetching the top 100 and trying to find the player, use the /centric endpoint. If you set limit=5, you will get the player, the 2 players ranked above them, and the 2 players ranked below them.
curl "http://localhost:5173/api/v1/games/my-game/players/player_1/centric?limit=5" \
-H "Authorization: Bearer <GAME_API_KEY>"| Rank | Player Ref | Score |
|---|---|---|
| 43 | player_x | 1500 |
| 44 | player_y | 1490 |
| 45 | player_1 | 1450 |
| 46 | player_z | 1400 |
| 47 | player_w | 1390 |
Rich Player Profiles
Store player metadata globally to enrich your leaderboards.
Use the Player endpoints to store avatar URLs, countries, levels, and associated devices. This data is updated independently from score submissions.
// Update player profile
curl -X PUT "http://localhost:5173/api/v1/games/my-game/players/player_1" \
-H "Authorization: Bearer <GAME_API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"playerName": "Alice",
"avatarUrl": "https://example.com/avatar.png",
"country": "CA",
"level": 42
}'| Name | Avatar URL | Country | Level |
|---|---|---|---|
| Alice | https://example.com/avatar.png | CA | 42 |
Anti-Cheat & Signature Validation
Block automated spam and strictly verify score submissions.
Configure Sanity Checks (max score, min score, rate limits) in your dashboard. For games that submit scores via a trusted backend, enforce Strict Server Signature Validation to cryptographically prove that the score submission wasn't tampered with.
// Strict Server-to-Server Validation (Node.js example)
import crypto from 'crypto';
const payload = JSON.stringify({ score: 1000, playerRefId: "player_1" });
const signature = crypto.createHmac('sha256', 'YOUR_SIGNATURE_SECRET')
.update(payload)
.digest('hex');
const response = await fetch("http://localhost:5173/api/v1/games/my-game/scores", {
method: 'POST',
headers: {
'Authorization': 'Bearer <GAME_API_KEY>',
'Content-Type': 'application/json',
'x-score-signature': `sha256=${signature}`
},
body: payload
});Handle Webhooks
Receive realtime events when high scores are achieved or players are overtaken.
Configure a webhook URL and secret in your game settings. Use an Express backend (or any HTTP server) to parse the JSON and verify the signature. You can use these events to send Discord messages or grant achievements.
import crypto from 'crypto';
import express from 'express';
const app = express();
const WEBHOOK_SECRET = 'your_webhook_secret';
app.post('/webhooks/lightleaderboard', express.text({ type: 'application/json' }), (req, res) => {
const sigHeader = req.headers['x-llb-signature'];
const eventType = req.headers['x-llb-event'];
// Verify Signature
const expectedSig = 'sha256=' + crypto.createHmac('sha256', WEBHOOK_SECRET).update(req.body).digest('hex');
if (sigHeader !== expectedSig) return res.status(401).send('Invalid Signature');
const payload = JSON.parse(req.body);
if (eventType === 'entered_top_10') {
console.log(`${payload.player.playerName} entered Top 10 at rank ${payload.rank}!`);
} else if (eventType === 'personal_best') {
console.log(`${payload.player.playerName} got a PB! Old: ${payload.previousBest}, New: ${payload.player.score}`);
}
res.status(200).send('OK');
});