Skip to content

Timed leaderboards ​

Run weekly competitions, daily challenges, and monthly tournaments — boards that reset on a schedule and archive their final standings so you can show past winners.

The board types ​

TypeResetsArchives keptUse case
All-timeNever—Career high scores
WeeklyMonday 00:00 UTC52Weekly competitions
DailyMidnight UTC90Daily challenges
Monthly1st of month, 00:00 UTC12Monthly tournaments
Custom intervalEvery N days52Sprints, fortnightly events

Resets happen on calendar boundaries in UTC, not rolling windows — a weekly board always turns over at Monday 00:00 UTC, so every player's week starts and ends at the same moment.

Timed vs targeted

A board's reset cadence is independent of its write mode. By default a board is fan-out (receives every submit); you can instead make it targeted so it only receives scores sent to it by ID. Any cadence here works with either mode. See Category boards.

Creating one ​

In the Developer Console → your game → Scoreboards → Add Scoreboard:

FieldExampleDescription
IDweekly-scoreboardUnique identifier
NameWeekly ChallengeDisplay name
Reset PeriodWeeklyWhen to archive & reset
Sort ByScore (high to low)Ranking method

For a custom cadence, choose Custom interval (every N days) and set the day count — e.g. 3 for a short sprint, 14 for a fortnightly event. Leave it blank and the board never auto-resets (same as All-Time).

Submitting and reading ​

You don't submit to timed boards individually. Submit once, and the score fans out to every standard board on the game — all-time, weekly, daily — automatically:

gdscript
CheddaBoards.submit_score(score, streak)

Read a specific board by ID:

gdscript
CheddaBoards.get_scoreboard("weekly-scoreboard", 100)

func _on_scoreboard_loaded(scoreboard_id, config, entries):
    for entry in entries:
        print("#%d %s: %d pts" % [entry.rank, entry.nickname, entry.score])
bash
curl "https://api.cheddaboards.com/games/my-game/scoreboards/weekly-scoreboard?limit=100" \
  -H "X-API-Key: cb_my-game_xxxxxxxxx" \
  -H "X-Game-ID: my-game"

Archives ​

When a timed board resets, its final standings are snapshotted into an archive you can read back later — "last week's winners", a hall of fame, and so on. Resetting never throws results away.

REST endpoints ​

EndpointReturns
GET /games/{gameId}/scoreboards/{id}/archivesList of archived periods for a board (add ?after=&before= nanosecond timestamps to filter a date range)
GET /games/{gameId}/scoreboards/{id}/archives/latestThe most recent archive (last week / yesterday / last month)
GET /archives/{archiveId}One specific archive
GET /games/{gameId}/archives/statsArchive statistics for the game
bash
curl "https://api.cheddaboards.com/games/my-game/scoreboards/weekly-scoreboard/archives/latest?limit=10" \
  -H "X-API-Key: cb_my-game_xxxxxxxxx" \
  -H "X-Game-ID: my-game"

An archive ID has the form gameId:scoreboardId:timestamp (the timestamp is nanoseconds, ICP-standard).

Godot ​

The SDK wraps these with signals and a few convenience calls:

gdscript
# Most recent archived period
CheddaBoards.get_last_archived_scoreboard("weekly-scoreboard", 100)
CheddaBoards.archived_scoreboard_loaded.connect(_on_archive)

func _on_archive(archive_id, config, entries):
    if entries.is_empty():
        return   # a brand-new board has no archives yet
    var winner = entries[0]
    print("Last week's champion: %s (%d pts)" % [winner.nickname, winner.score])

# Shorthands for the common cases
CheddaBoards.get_last_week_scoreboard()
CheddaBoards.get_yesterday_scoreboard()
CheddaBoards.get_last_month_scoreboard()

# List every archived period, e.g. for a hall of fame
CheddaBoards.get_scoreboard_archives("weekly-scoreboard")
CheddaBoards.archives_list_loaded.connect(_on_list)

The archive signals are in the signals reference. The included Leaderboard.tscn already wires an All-Time / Weekly toggle and a Current / Last-Period toggle, so on the template you get archive browsing without writing this yourself.

The config dictionary ​

Scoreboard and archive reads both carry a config, but with slightly different fields — a live board knows when it last reset, an archive knows the exact period it covers.

Live board (GET /games/{gameId}/scoreboards/{id}, and the SDK's scoreboard_loaded):

name          display name
description   board description ("" if none)
period        the reset cadence: allTime, weekly, …
sortBy        score | streak
lastReset     nanosecond timestamp of the current period's start

Archive (…/archives/latest, /archives/{archiveId}, and the SDK's archived_scoreboard_loaded):

name          display name
period        the cadence of the board it came from
sortBy        score | streak
periodStart   nanosecond timestamp — when the archived period began
periodEnd     nanosecond timestamp — when it ended (the reset moment)

An archive response also carries its archiveId alongside config, and both kinds return entries plus a totalEntries count. Real example, trimmed:

json
{
  "ok": true,
  "data": {
    "archiveId": "my-game:weekly:1789344976813703772",
    "config": { "name": "weekly", "period": "weekly", "sortBy": "score",
                "periodStart": 1788739200000000000, "periodEnd": 1789344976813703772 },
    "entries": [ { "rank": 1, "nickname": "Chedz", "score": 5148, "streak": 4,
                   "authType": "external", "submittedAt": 1788964834738168040 } ],
    "totalEntries": 1
  }
}

Timestamps are nanoseconds since epoch — divide by 1,000,000,000 for seconds before handing to Time.get_datetime_dict_from_unix_time().

Retention ​

CadenceArchives kept
Weekly52 (a year)
Daily90
Monthly12 (a year)
Custom interval52

Archives don't change once written, so they're safe to cache client-side.

Good practice ​

  • Always keep an all-time board alongside timed ones — players want their career best, not just this week's.
  • Show current and last period together — "This Week" / "Last Week" is the pairing players expect.
  • Celebrate winners — a crown and a highlight on the #1 archive entry does a lot.

See also: Category boards · What's stored · REST quick start · Godot signals

Can't be taken away. Open source backend and SDKs.