Custom Leaderboards
Create multiple independent leaderboards per app — each with its own score name and sort direction. A soccer game can have "Goals" and "Assists" as separate boards; a racing game can rank by fastest lap time.
Concepts
| Field | Type | Description |
|---|---|---|
| slug | string | Unique identifier within your app. Only [a-z0-9_-]. E.g. goals, fastest-lap |
| name | string | Display name shown to users. E.g. "Top Scorers" |
| points_name | string | Label for the score unit. E.g. "Goals", "Kills", "ms" |
| sort_order | string | desc — higher is better (default). asc — lower is better (e.g. lap times) |
Authentication
Management calls (create, update, delete, score) require Authorization: AppID:AppKey. Read-only calls (list, ranking, user) also accept a user session token in the Authorization header — the response will include my_position automatically.
Endpoints overview
| Endpoint | Action |
|---|---|
POST /api/leaderboards/create.php | Create a leaderboard |
POST /api/leaderboards/update.php | Update name, points_name or sort_order |
POST /api/leaderboards/delete.php | Delete a leaderboard and all its scores |
POST /api/leaderboards/score.php | Set, add, reset or remove a user's score |
POST /api/leaderboards/list.php | List all leaderboards for an app |
POST /api/leaderboards/ranking.php | Ranked entries for a leaderboard (global or friends) |
POST /api/leaderboards/user.php | All scores for a user across an app's leaderboards |
1. Create a leaderboard
Do this once — from your backend or the Developer Console — before calling score.php from the game.
// Create leaderboard (one-time setup from server)
await fetch('https://vaneltonmedia.com/api/leaderboards/create.php', {
method: 'POST',
headers: { 'Authorization': `${APP_ID}:${APP_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ slug: 'goals', name: 'Top Scorers', points_name: 'Goals' })
});
// Ascending sort: lower score = better rank (e.g. lap time in ms)
await fetch('https://vaneltonmedia.com/api/leaderboards/create.php', {
method: 'POST',
headers: { 'Authorization': `${APP_ID}:${APP_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ slug: 'fastest_lap', name: 'Fastest Lap', points_name: 'ms', sort_order: 'asc' })
});
function createLeaderboard(string $slug, string $name, string $pointsName = 'Points', string $order = 'desc'): array {
$ch = curl_init('https://vaneltonmedia.com/api/leaderboards/create.php');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Authorization: ' . APP_ID . ':' . APP_KEY, 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode(['slug' => $slug, 'name' => $name, 'points_name' => $pointsName, 'sort_order' => $order]),
]);
$r = json_decode(curl_exec($ch), true);
curl_close($ch);
return $r;
}
curl -s -X POST "https://vaneltonmedia.com/api/leaderboards/create.php" \
-H "Authorization: $APP_ID:$APP_KEY" \
-H "Content-Type: application/json" \
-d '{"slug":"goals","name":"Top Scorers","points_name":"Goals"}'
2. Record a score
Call score.php from your game server. You can set an absolute value, add to the current score (use negative values to subtract), reset to zero, or remove the user entirely.
// Add 2 goals after a match
await fetch('https://vaneltonmedia.com/api/leaderboards/score.php', {
method: 'POST',
headers: { 'Authorization': `${APP_ID}:${APP_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'add', slug: 'goals', token: userToken, value: 2 })
});
// Set an absolute best lap time (player beat their record)
await fetch('https://vaneltonmedia.com/api/leaderboards/score.php', {
method: 'POST',
headers: { 'Authorization': `${APP_ID}:${APP_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'set', slug: 'fastest_lap', token: userToken, value: 87432 })
});
type ScoreAction = 'set' | 'add' | 'reset' | 'remove';
async function recordScore(slug: string, action: ScoreAction, userToken: string, value?: number) {
const res = await fetch('https://vaneltonmedia.com/api/leaderboards/score.php', {
method: 'POST',
headers: { 'Authorization': `${APP_ID}:${APP_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ action, slug, token: userToken, ...(value !== undefined && { value }) })
});
return res.json() as Promise<{ success: boolean; new_score: number }>;
}
function recordScore(string $slug, string $action, string $userToken, ?int $value = null): array {
$payload = ['action' => $action, 'slug' => $slug, 'token' => $userToken];
if ($value !== null) $payload['value'] = $value;
$ch = curl_init('https://vaneltonmedia.com/api/leaderboards/score.php');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Authorization: ' . APP_ID . ':' . APP_KEY, 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode($payload),
]);
$r = json_decode(curl_exec($ch), true);
curl_close($ch);
return $r; // $r['new_score'] has the updated value
}
import requests
HEADERS = {'Authorization': f'{APP_ID}:{APP_KEY}', 'Content-Type': 'application/json'}
def record_score(slug: str, action: str, user_token: str, value: int | None = None) -> dict:
body = {'action': action, 'slug': slug, 'token': user_token}
if value is not None:
body['value'] = value
return requests.post('https://vaneltonmedia.com/api/leaderboards/score.php', headers=HEADERS, json=body).json()
record_score('goals', 'add', user_token, value=2) # add 2 goals
record_score('goals', 'add', user_token, value=-1) # subtract 1 (own goal penalty)
using System.Net.Http;
using System.Text;
using System.Text.Json;
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", $"{APP_ID}:{APP_KEY}");
async Task RecordScore(string slug, string action, string userToken, int? value = null)
{
var body = new System.Collections.Generic.Dictionary
{
{ "action", action }, { "slug", slug }, { "token", userToken }
};
if (value.HasValue) body["value"] = value.Value;
var payload = JsonSerializer.Serialize(body);
await client.PostAsync(
"https://vaneltonmedia.com/api/leaderboards/score.php",
new StringContent(payload, Encoding.UTF8, "application/json"));
}
await RecordScore("goals", "add", userToken, value: 2);
var _headers = ds_map_create();
ds_map_add(_headers, "Authorization", global.app_id + ":" + global.app_key);
ds_map_add(_headers, "Content-Type", "application/json");
// Add 2 goals after a match
global.score_req = http_request(
"https://vaneltonmedia.com/api/leaderboards/score.php",
"POST", _headers,
json_stringify({ action: "add", slug: "goals", token: global.user_token, value: 2 })
);
ds_map_destroy(_headers);
// --- In Async HTTP Event ---
if (async_load[? "id"] == global.score_req) {
var _d = json_parse(async_load[? "result"]);
show_debug_message("New score: " + string(_d.new_score));
}
3. Display the ranking
Call ranking.php with a user token to get my_position even if the user is outside the top N.
// Global top 10 + my position
const res = await fetch('https://vaneltonmedia.com/api/leaderboards/ranking.php', {
method: 'POST',
headers: { 'Authorization': userToken, 'Content-Type': 'application/json' },
body: JSON.stringify({ slug: 'goals', app_id: 'com.myapp.game', limit: 10 })
});
const { entries, my_position } = await res.json();
// entries[].rank, entries[].username, entries[].score, entries[].updated_at
// my_position.rank — always the global rank, even if using scope: 'friends'
// Friends-only leaderboard
const res2 = await fetch('https://vaneltonmedia.com/api/leaderboards/ranking.php', {
method: 'POST',
headers: { 'Authorization': userToken, 'Content-Type': 'application/json' },
body: JSON.stringify({ slug: 'goals', app_id: 'com.myapp.game', scope: 'friends' })
});
user_headers = {'Authorization': user_token, 'Content-Type': 'application/json'}
r = requests.post('https://vaneltonmedia.com/api/leaderboards/ranking.php',
headers=user_headers,
json={'slug': 'goals', 'app_id': 'com.myapp.game', 'limit': 10}
).json()
for e in r['entries']:
print(f"#{e['rank']} {e['username']} — {e['score']} {r['leaderboard']['points_name']}")
if r['my_position']:
print(f"Your rank: #{r['my_position']['rank']}")
// Authenticate as user for my_position
using var userClient = new HttpClient();
userClient.DefaultRequestHeaders.Add("Authorization", userToken);
var payload = JsonSerializer.Serialize(new { slug = "goals", app_id = "com.myapp.game", limit = 10 });
var res = await userClient.PostAsync(
"https://vaneltonmedia.com/api/leaderboards/ranking.php",
new StringContent(payload, Encoding.UTF8, "application/json"));
using var doc = JsonDocument.Parse(await res.Content.ReadAsStringAsync());
var entries = doc.RootElement.GetProperty("entries");