?Publish

Docs

LEADERBOARDS

Every high score deserves a place

Rank scores, times, counts or completion rates. The platform manages player identity, personal bests and standings; your game reports round starts and final results.

Three steps

  1. Create a board

    Game page → Leaderboard settings: higher or lower is better, the unit, the score range and the minimum play time. Up to 10 boards per game.

  2. Connect start and finish

    No SDK needed. Get a runId when a round starts and submit the real integer score at the end.

  3. Test on the site

    Check personal bests, ties, retries and a new round. If the leaderboard is down, the game still plays.

APIs and examples

Common leaderboard endpoints
WhenEndpointKey detail
Read settingsGET /api/v1/games/{gameId}/leaderboardsEnabled boards
Round startPOST /api/v1/leaderboards/{boardId}/runsPlayer token + new request key
Final resultPOST /api/v1/runs/{runId}/score{"score":12800}
StandingsGET /api/v1/leaderboards/{boardId}/entrieslimit and cursor pagination
My rankGET /api/v1/leaderboards/{boardId}/me?around=2Personal result and neighbors
Native JavaScript / REST example

Replace the platform origin and YOUR_BOARD_ID with the generated configuration, then connect the calls to actual round-start and game-over events.

const platform = 'https://www.ai-made.games';
const boardId = 'YOUR_BOARD_ID';

function getPlayerSession() {
  return new Promise((resolve, reject) => {
    const requestId = crypto.randomUUID();
    const finish = (error, session) => {
      clearTimeout(timer);
      window.removeEventListener('message', receive);
      error ? reject(error) : resolve(session);
    };
    const receive = (event) => {
      const data = event.data;
      if (event.source !== window.parent || event.origin !== platform ||
          !data || data.source !== 'aimadegames-platform' ||
          data.type !== 'leaderboard-session' || data.requestId !== requestId) return;
      if (data.error) return finish(new Error(data.error));
      if (data.apiOrigin !== platform || !data.session?.token) return;
      finish(null, data.session);
    };
    const timer = setTimeout(() => finish(new Error('Session timeout')), 15000);
    window.addEventListener('message', receive);
    window.parent.postMessage({source:'aimadegames', type:'leaderboard-session', requestId}, platform);
  });
}

async function request(path, token, body, key) {
  const response = await fetch(platform + '/api/v1/' + path, {
    method: key ? 'POST' : 'GET', credentials: 'omit',
    headers: {Authorization: 'Bearer ' + token,
      ...(key ? {'Idempotency-Key': key} : {}),
      ...(body ? {'Content-Type': 'application/json'} : {})},
    body: body ? JSON.stringify(body) : undefined,
    signal: AbortSignal.timeout(15000)
  });
  const data = await response.json();
  if (!response.ok) throw Object.assign(new Error(data.error?.message), {
    code: data.error?.code, retryAfter: response.headers.get('Retry-After')
  });
  return data;
}

// Save these per round. Reuse the same keys when retrying that round.
const startKey = crypto.randomUUID();
const scoreKey = crypto.randomUUID();
const session = await getPlayerSession();
const run = await request('leaderboards/' + boardId + '/runs', session.token, null, startKey);

// At final game-over, use the actual integer score from your game:
async function submitFinalScore(score) {
  // Refreshing the token preserves this player's run.
  const current = Date.parse(session.expiresAt) <= Date.now() + 60000
    ? await getPlayerSession() : session;
  return request('runs/' + run.runId + '/score', current.token, {score}, scoreKey);
}
// Catch failures in your game's event handlers so gameplay can continue.
// Retry settlement with the SAME run, scoreKey and final score.
// For a new round create new keys and call the start endpoint again.
Tokens, scoring and retries
  • Get a one-hour player token through the parent’s leaderboard-session handshake. Validate the parent window, exact origin, request ID and returned apiOrigin. Keep tokens in memory; use credentials: omit.
  • Use separate UUID Idempotency-Key values for start and settlement. Retries keep the original key, runId and score; new rounds use new keys. A run can be settled within two hours.
  • Time boards use integer milliseconds; submit 9820 for 98.2%. Scoring rules are immutable; create a new board when gameplay requires incompatible rules.
  • Guest and account scores are separate. Client-reported scores are for casual play and are not anti-cheat verified.

Before launch

Try each one from Start game on the game page. Ticks are notes for you and stay in this browser.

Leaderboards

0 of 6

Stuck? Send us the error and your prompt here.