Leaderboard API
Player rankings and statistics leaderboards.
Get Leaderboard
Retrieve player rankings based on various metrics.
Endpoint: GET /api/leaderboard?metric=balance&limit=10
Authentication: Required (JWT)
Query Parameters:
metric- Ranking metric (default:balance)balance- Economy balancelevel- Player levelplaytime- Total playtimekills- Mob/player killsdeaths- Deathsjob_level- Specific job level
limit- Number of entries (default: 10, max: 100)jobName- Job name (required if metric isjob_level)
Success Response (200):
{
"success": true,
"metric": "balance",
"leaderboard": [
{
"rank": 1,
"username": "Player123",
"displayName": "Player123",
"value": 50000.0,
"formatted": "💰50,000.00"
},
{
"rank": 2,
"username": "Player456",
"displayName": "Player456",
"value": 45000.0,
"formatted": "💰45,000.00"
}
],
"updatedAt": "2024-01-20T15:30:00Z"
}Example - Balance Leaderboard:
curl "http://localhost:8080/api/leaderboard?metric=balance&limit=10" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"Example - Job Level Leaderboard:
curl "http://localhost:8080/api/leaderboard?metric=job_level&jobName=Miner&limit=10" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"Example - Playtime Leaderboard:
curl "http://localhost:8080/api/leaderboard?metric=playtime&limit=20" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"Available Metrics
Balance
Players ranked by economy balance.
Response Value: Currency amount (e.g., 50000.0)
Level
Players ranked by experience level.
Response Value: Player level (e.g., 35)
Playtime
Players ranked by total time played.
Response Value: Minutes played (e.g., 12000)
Formatted: "200 hours" or "50 days"
Kills
Players ranked by entity kills.
Response Value: Total kills (e.g., 1500)
Deaths
Players ranked by deaths (descending).
Response Value: Total deaths (e.g., 50)
Job Level
Players ranked by level in a specific job.
Response Value: Job level (e.g., 45)
Requires: jobName parameter
Code Examples
TypeScript
interface LeaderboardEntry {
rank: number;
username: string;
displayName: string;
value: number;
formatted: string;
}
type LeaderboardMetric = 'balance' | 'level' | 'playtime' | 'kills' | 'deaths' | 'job_level';
class LeaderboardAPI {
constructor(private baseUrl: string, private token: string) {}
async getLeaderboard(
metric: LeaderboardMetric,
limit = 10,
jobName?: string
): Promise<LeaderboardEntry[]> {
const params = new URLSearchParams({
metric,
limit: limit.toString()
});
if (jobName && metric === 'job_level') {
params.append('jobName', jobName);
}
const response = await fetch(
`${this.baseUrl}/api/leaderboard?${params}`,
{
headers: { 'Authorization': `Bearer ${this.token}` }
}
);
const data = await response.json();
return data.leaderboard;
}
async getTopPlayers(limit = 10) {
return this.getLeaderboard('balance', limit);
}
async getTopJobPlayers(jobName: string, limit = 10) {
return this.getLeaderboard('job_level', limit, jobName);
}
async getPlayerRank(username: string, metric: LeaderboardMetric): Promise<number | null> {
const leaderboard = await this.getLeaderboard(metric, 100);
const entry = leaderboard.find(e => e.username === username);
return entry?.rank ?? null;
}
}Python
class LeaderboardAPI:
def __init__(self, base_url, token):
self.base_url = base_url
self.headers = {'Authorization': f'Bearer {token}'}
def get_leaderboard(self, metric='balance', limit=10, job_name=None):
params = {'metric': metric, 'limit': limit}
if job_name and metric == 'job_level':
params['jobName'] = job_name
response = requests.get(
f'{self.base_url}/api/leaderboard',
headers=self.headers,
params=params
)
return response.json()
def get_top_players(self, limit=10):
"""Get top players by balance"""
return self.get_leaderboard('balance', limit)
def get_top_job_players(self, job_name, limit=10):
"""Get top players for a specific job"""
return self.get_leaderboard('job_level', limit, job_name)
def get_player_rank(self, username, metric='balance'):
"""Find a player's rank in the leaderboard"""
data = self.get_leaderboard(metric, limit=100)
for entry in data.get('leaderboard', []):
if entry['username'] == username:
return entry['rank']
return NoneReact Component
import { useState, useEffect } from 'react';
interface LeaderboardEntry {
rank: number;
username: string;
displayName: string;
value: number;
formatted: string;
}
export function Leaderboard({ metric = 'balance', limit = 10 }) {
const [entries, setEntries] = useState<LeaderboardEntry[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchLeaderboard() {
const token = localStorage.getItem('token');
const response = await fetch(
`http://localhost:8080/api/leaderboard?metric=${metric}&limit=${limit}`,
{
headers: { 'Authorization': `Bearer ${token}` }
}
);
const data = await response.json();
setEntries(data.leaderboard);
setLoading(false);
}
fetchLeaderboard();
}, [metric, limit]);
if (loading) return <div>Loading...</div>;
return (
<div className="leaderboard">
<h2>Top Players - {metric}</h2>
<table>
<thead>
<tr>
<th>Rank</th>
<th>Player</th>
<th>Value</th>
</tr>
</thead>
<tbody>
{entries.map(entry => (
<tr key={entry.rank}>
<td>{entry.rank}</td>
<td>{entry.displayName}</td>
<td>{entry.formatted}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}Display Examples
Top 10 Richest Players
┌──────┬──────────────┬──────────────┐
│ Rank │ Player │ Balance │
├──────┼──────────────┼──────────────┤
│ 1 │ Player123 │ 💰50,000.00 │
│ 2 │ Player456 │ 💰45,000.00 │
│ 3 │ Player789 │ 💰40,500.00 │
└──────┴──────────────┴──────────────┘Top Job Performers
┌──────┬──────────────┬────────┐
│ Rank │ Player │ Level │
├──────┼──────────────┼────────┤
│ 1 │ MinerPro │ 95 │
│ 2 │ DigDugger │ 87 │
│ 3 │ CaveExplorer │ 82 │
└──────┴──────────────┴────────┘Update Frequency
- Leaderboards cached and updated every 5 minutes
- Real-time updates for current player's rank
- Manual refresh available via cache invalidation (admin)
Customization
Custom Metrics
Server owners can add custom metrics by:
- Implementing metric calculation in plugin
- Registering metric with leaderboard service
- Metric becomes available via API
Display Options
Consider these UI enhancements:
- Medal icons for top 3 (🥇🥈🥉)
- Highlight current player's position
- Show percentile rank
- Compare to friends
- Historical rank changes
Pagination
For large leaderboards, use pagination:
async function getFullLeaderboard(api: LeaderboardAPI, metric: string) {
const allEntries = [];
let page = 1;
const pageSize = 100;
while (true) {
const entries = await api.getLeaderboard(metric, pageSize);
allEntries.push(...entries);
if (entries.length < pageSize) break;
page++;
}
return allEntries;
}Notes
- Leaderboards cached for performance
- Minimum rank shown is #1
- Tied values share the same rank
- Inactive players (90+ days) may be excluded
- Job leaderboards require Jobs plugin
- Balance leaderboard requires Vault economy
Related
- Economy API - Balance information
- Jobs API - Job statistics
- Players API - Player data
- Admin API - Cache management