This API provides programmatic access to leaderboard rankings, player statistics, and match history for the SlugBot PvP Leaderboard system.

Base URL

https://api.slugbot.xyz/pvplb/{server_id}/{leaderboard_id}

  • server_id: ID of the Discord server the leaderboard is in.
    • The user tied to the API key must be a member of this server to have access to the leaderboard data.
  • leaderboard_id: The name of the leaderboard. Special characters must be URL encoded.

Authorisation

A valid API key must be provided in the Authorization header:

Authorization: Bearer {API_KEY}

Note: An API key can be generated using the command api get_new_key in any Discord server with SlugBot.

Error Codes

All error responses return a JSON body in the form:

{
  "error": "Not Found",
  "message": "Could not find match with ID: 6961335332de57bd419e3b16"
}
  • error: The standard HTTP reason phrase for the status code.
  • message: A specific description of what went wrong.
StatusMeaning
400The match ID is not in a valid format (must be a MongoDB ObjectId).
401The Authorization header is missing or malformed.
403The API key is invalid, the key’s user is not a member of the target server, or the endpoint requires higher permissions (e.g. ladder admin for Declare Match Result).
404The match, player, or path was not found.
409The match is already completed and cannot be updated (Declare Match Result).
422The declaration body is malformed (Declare Match Result).
500An internal server error occurred.

Player Endpoints

Get Player Stats

Retrieves leaderboard statistics for a single player.

  • URL: /player/{player_id}
  • Method: GET
  • Path Parameters:
    • player_id (string): The Discord User ID of the player. For teams, use the team’s leaderboard ID (the userId returned in batch responses), not a member’s Discord ID.
  • Returns 404 if the player has no data on this leaderboard.

Example

https://api.slugbot.xyz/pvplb/123123123123123123/test%20lb/player/2290978510728123123 Example response:

{
  "userId": "2290978510728123123",
  "guildId": "123123123123123123",
  "leaderBoardId": "test lb",
  "displayName": "Narcolept",
  "members": ["2290978510728123123"],
  "rank": 1,
  "matchesPlayed": 8,
  "matchesWon": 7,
  "points": 30.10524021259957,
  "decayedPoints": 0,
  "elo": 1029.0332436228407,
  "lastMatchPlayedAt": 1767985291309
}

Response fields:

  • teamLeaderId: Discord ID of the team leader. Present on team entries only.
  • members: Discord User IDs of all members of the entry (a single ID for solo players).
  • rank: The player’s stored rank. Omitted if no rank has been recorded yet.
  • lastMatchPlayedAt: Unix timestamp in milliseconds.
  • All other fields are self-explanatory.

Get All Players (Batch)

Retrieves a paginated list of all ranked participants on the specified leaderboard.

  • URL: /player/batch
  • Method: GET
  • Query Parameters:
    • limit (number): Maximum number of records to return. Defaults to 10, capped at 500.
    • nextCursor (number): The rank of the last player on the previous page.
  • Results are sorted by rank, starting from 1.
  • Only players with a recorded rank appear in this list. New players without a rank are excluded.
  • Each item in data is the same player object returned by Get Player Stats.

Example

https://api.slugbot.xyz/pvplb/123123123123123123/test%20lb/player/batch?limit=3 Example Response:

{
  "success": true,
  "data": [{...}, {...}, {...}],
  "paging": {
    "nextCursor": 3,
    "hasNextPage": true
  }
}

Match Endpoints

Get Match Info

Retrieves match info on a specific match.

  • URL: /match/{match_id}
  • Method: GET
  • Path Parameters:
    • match_id (string): The unique identifier for the match (MongoDB ObjectId).
  • Returns 400 if the ID is not in a valid format, and 404 if no match with that ID exists on the leaderboard.

Example

https://api.slugbot.xyz/pvplb/123123123123123123/test%20lb/match/6961335332de57bd419e3b16 Example response:

{
  "matchId": "6961335332de57bd419e3b16",
  "guildId": "123123123123123123",
  "leaderBoardId": "test lb",
  "timestamp": 1767985291309,
  "rankRangeLimit": -1,
  "platformLimit": [],
  "regionLimit": [],
  "isResolvePending": false,
  "declaredResults": {
    "team0": [1, 0],
    "team1": [0, 1]
  },
  "winTeam": 0,
  "participants": [
    {
      "teamId": 0,
      "playerId": "2290978510728123123",
      "initialRank": 1,
      "initialPoints": 29.61793872377991,
      "initialElo": 1022.40341488487301,
      "pointsReceived": 0.4873014888196579,
      "eloReceived": 4.623613044190915,
      "pointDecayRegened": 0
    },
    {
      "teamId": 1,
      "playerId": "460530474897567567",
      "initialRank": 2,
      "initialPoints": 15.58222222222222,
      "initialElo": 994.40341488487301,
      "pointsReceived": 0.2222222222222221,
      "eloReceived": -4.623613044190915,
      "pointDecayRegened": 0
    }
  ]
}

Response fields:

  • timestamp: Unix timestamp in milliseconds.
  • rankRangeLimit, platformLimit, regionLimit: Restrictions the match was created with. -1 / empty arrays mean no restriction.
  • isResolvePending: true once both teams have declared matching results and the match is waiting to be resolved.
  • declaredResults: The score each team declared, in [own score, opponent score] form. Values are null until that team declares.
  • winTeam: The outcome of the match (see table below).
  • participants: Per-player stats for the match.
    • initialRank, initialPoints, initialElo: The player’s stats at the time the match started.
    • pointsReceived, eloReceived, pointDecayRegened: The rewards applied when the match completed.
    • While a match is incomplete (winTeam is -1), all of these stat fields are null.

winTeam values:

ValueMeaning
-1Incomplete, no winner yet
0Team 0 won
1Team 1 won
2Draw

Get Match History (Batch)

Retrieves a paginated list of matches for the leaderboard. Includes filters for specific users or match status.

  • URL: /match/batch
  • Method: GET
  • Query Parameters:
    • userId (string): Filter history to matches the player accepted.
    • complete (boolean): Include finished matches.
    • incomplete (boolean): Include ongoing/pending matches.
    • limit (number): Maximum number of records to return. Defaults to 10, capped at 50.
    • nextCursor (number): The timestamp of the last match on the previous page.
  • Results are sorted by timestamp, newest first.
  • Leaving both complete and incomplete unset (or setting both to true) applies no status filter and returns all matches.
  • Each item in data is the same match object returned by Get Match Info.

Example

https://api.slugbot.xyz/pvplb/123123123123123123/test%20lb/match/batch?limit=3&userId=2290978510728123123&complete=true&incomplete=false Example Response:

{
  "success": true,
  "data": [{...}, {...}, {...}],
  "paging": {
    "nextCursor": 1748195916815,
    "hasNextPage": true
  }
}

Note: This query will return completed matches played by user 2290978510728123123

Declare Match Result

Submits a score declaration for a team in an ongoing match. Both teams must declare matching results before the match completes.

  • URL: /match/{match_id}/declare
  • Method: POST
  • Permissions: The API key’s user must be a ladder admin (whitelisted for the ladder command in the target server).
  • Body (JSON):
{
  "teamIndex": 0,
  "result": [1, 0]
}
  • teamIndex (number): The index of the team making the declaration (0 or 1).
  • result (number[]): The declared score in [own score, opponent score] form. Both values must be non-negative numbers.
  • Returns 409 if the match is already completed, and 422 if the declaration is malformed.
  • Returns the updated match object on success.

Pagination Logic

Both /batch endpoints use cursor-based pagination.

  1. Initial Request: Leave the nextCursor parameter empty.
  2. Handling Response: The API will return a list of items and a paging object containing a nextCursor number and a hasNextPage boolean.
  3. Subsequent Request: Pass that number into the nextCursor query parameter to fetch the next page of results. If hasNextPage is false, you have reached the end of the data. Note: hasNextPage is true whenever the page came back full, even if no more data exists. A final request may return an empty data array.

Rate Limiting

All endpoints are rate limited per IP address:

  • Rate: 10 requests per second.
  • Exceeding the limit returns a 429 Too Many Requests response.