SKIP TO MAIN CONTENT
// MENU
HOME SERVERS MAPS PLAYERS OPT-IN API CONTACT LOGIN REGISTER
// API

API Reference

A public, read-only, versioned API over the same data this site shows — no account, no API key.

NO AUTH REQUIRED 60 REQ/MIN PER IP JSON /API/V1
// BASE URL
https://hrl.effakt.info/api/v1
// ENDPOINTS
GET /servers

Every active (non-archived) server, with real derived stats.

QUERY PARAMETERS
page        int   optional, default 1
per_page    int   optional, default 50, capped at 100
EXAMPLE REQUEST
GET /api/v1/servers
EXAMPLE RESPONSE
{
  "data": [
    {
      "id": 7,
      "name": "EffakT's Server",
      "total_laps": 342,
      "total_players": 58,
      "maps_played": 6,
      "last_active_at": "2026-07-14T09:12:03+00:00"
    }
  ],
  "links": { "first": "…?page=1", "last": "…?page=1", "prev": null, "next": null },
  "meta": { "current_page": 1, "last_page": 1, "per_page": 50, "total": 1 }
}
GET /maps

Every map, paginated. Useful for discovering a map's id/name before calling the leaderboard endpoint below.

QUERY PARAMETERS
page        int   optional, default 1
per_page    int   optional, default 50, capped at 100
EXAMPLE REQUEST
GET /api/v1/maps?per_page=2
EXAMPLE RESPONSE
{
  "data": [
    { "id": 1, "name": "bloodgulch", "label": "Blood Gulch", "checkpoint_count": 5, "total_laps": 342 },
    { "id": 2, "name": "dangercanyon", "label": "Danger Canyon", "checkpoint_count": 6, "total_laps": 118 }
  ],
  "links": { "first": "…?page=1", "last": "…?page=7", "prev": null, "next": "…?page=2" },
  "meta": { "current_page": 1, "last_page": 7, "per_page": 2, "total": 14 }
}
GET /maps/{map}/leaderboard

The global leaderboard for one map — every player's single best lap across all active servers, ranked (earliest lap wins a tie). {map} accepts either the numeric id or the map's real name (e.g. bloodgulch).

QUERY PARAMETERS
server      int   optional — scope to one server's nested leaderboard instead of the global one
port        int   optional — identify yourself as a game server by ip:port (your request's own
                  IP, plus this port); resolves to your own nested leaderboard and takes
                  precedence over `server`. 404s if no registered server matches
page        int   optional, default 1
per_page    int   optional, default 50, capped at 100
EXAMPLE REQUEST
GET /api/v1/maps/bloodgulch/leaderboard
GET /api/v1/maps/bloodgulch/leaderboard?server=7
GET /api/v1/maps/bloodgulch/leaderboard?port=2302
EXAMPLE RESPONSE
{
  "data": [
    {
      "rank": 1,
      "lap_id": 4821,
      "player": { "id": 12, "name": "EffakT" },
      "server": { "id": 7, "name": "EffakT's Server" },
      "time": 68.066666666667,
      "time_formatted": "1:08.07",
      "gap": 0,
      "set_at": "2026-07-09T20:14:05+00:00",
      "splits": []
    }
  ],
  "links": { "first": "…?page=1", "last": "…?page=1", "prev": null, "next": null },
  "meta": { "current_page": 1, "last_page": 1, "per_page": 50, "total": 1 }
}
GET /players

The Global Leaderboard — every player with at least one real lap, ranked by Global Score. Same data as the Players List page.

QUERY PARAMETERS
page        int   optional, default 1
per_page    int   optional, default 50, capped at 100
EXAMPLE REQUEST
GET /api/v1/players?per_page=2
EXAMPLE RESPONSE
{
  "data": [
    {
      "id": 12,
      "rank": 1,
      "name": "EffakT",
      "score": 1845,
      "records": 6,
      "maps_played": 11,
      "total_laps": 342,
      "last_active_at": "2026-07-14T09:12:03+00:00"
    },
    {
      "id": 31,
      "rank": 2,
      "name": "RunnerUp",
      "score": 1720,
      "records": 2,
      "maps_played": 10,
      "total_laps": 210,
      "last_active_at": "2026-07-13T22:40:11+00:00"
    }
  ],
  "links": { "first": "…?page=1", "last": "…?page=44", "prev": null, "next": "…?page=2" },
  "meta": { "current_page": 1, "last_page": 44, "per_page": 2, "total": 87 }
}
GET /laps/{lapTime}

One specific submitted lap's full detail, by its id. Not scoped to active servers — a lap's historical existence doesn't depend on whether its server was later archived.

EXAMPLE REQUEST
GET /api/v1/laps/4821
EXAMPLE RESPONSE
{
  "data": {
    "id": 4821,
    "time": 68.066666666667,
    "time_formatted": "1:08.07",
    "player": { "id": 12, "name": "EffakT" },
    "map": { "id": 1, "label": "Blood Gulch" },
    "server": { "id": 7, "name": "EffakT's Server" },
    "set_at": "2026-07-09T20:14:05+00:00",
    "splits": [
      { "checkpoint_id": 1, "duration": 9.3 },
      { "checkpoint_id": 2, "duration": 12.4 }
    ]
  }
}
POST /laps

The lap-submission webhook — how a Halo game server reports a completed lap. Not intended for general use; if you're setting up a server to report to HRL, see the Opt-In guide instead — this endpoint's request/response shape is tied to the HRL Lua script, not documented here.

// AUTH

None. HRL is already a fully public leaderboard with no login system, so the read endpoints above expose nothing the site itself doesn't already show.

// RATE LIMITING

60 requests/minute per IP on the read endpoints above.

// ERRORS

The requested map, lap, or server (via ?port=) doesn't exist:

HTTP/1.1 404 Not Found

{
  "message": "No query results for map bloodgulch2"
}

Rate limit exceeded:

HTTP/1.1 429 Too Many Requests

{
  "message": "Too Many Attempts."
}