PrismSMP API Docs

Code Examples

Runnable snippets for the common ways people integrate with this API. Every snippet uses the same two placeholders — fill these in before running anything:

bash
API_BASE="https://api.prismsmp.net"
API_KEY="your-api-key-here"     # from /api key, in-game

cURL

Health check

bash
curl -s "$API_BASE/api/health" \
  -H "X-API-Key: $API_KEY"

Player lookup

bash
curl -s "$API_BASE/api/player/SomePlayer" \
  -H "X-API-Key: $API_KEY"

Auction search

bash
curl -s "$API_BASE/api/auction/search?item=netherite&sort=cheap&limit=10" \
  -H "X-API-Key: $API_KEY"

Leaderboard

bash
curl -s "$API_BASE/api/leaderboard?type=balance&limit=5" \
  -H "X-API-Key: $API_KEY"

Player count

bash
curl -s "$API_BASE/api/players/count" \
  -H "X-API-Key: $API_KEY"

JavaScript (Node.js, plain fetch)

No dependencies beyond a Node version with global fetch (Node 18+). This helper centralizes error handling so every call site gets consistent 404/429 behavior.

prismsmp-client.jsjavascript
const API_BASE = "https://api.prismsmp.net";
const API_KEY = process.env.PRISMSMP_API_KEY;

class PrismSMPApiError extends Error {
  constructor(status, message) {
    super(message);
    this.status = status;
  }
}

async function prismsmpGet(path) {
  const res = await fetch(`${API_BASE}${path}`, {
    headers: { "X-API-Key": API_KEY }
  });

  if (res.status === 429) {
    const retryAfter = Number(res.headers.get("Retry-After")) || 60;
    throw new PrismSMPApiError(429, `Rate limited, retry after ${retryAfter}s`);
  }

  if (!res.ok) {
    const body = await res.json().catch(() => ({}));
    throw new PrismSMPApiError(res.status, body.error || `HTTP ${res.status}`);
  }

  return res.json();
}

async function main() {
  const health = await prismsmpGet("/api/health");
  console.log("Status:", health.status);

  const player = await prismsmpGet("/api/player/SomePlayer");
  console.log(`${player.name}: $${player.balance} balance, ${player.stats.kills} kills`);

  const auctions = await prismsmpGet("/api/auction/search?item=netherite&sort=cheap&limit=5");
  console.log(`${auctions.total_matching} listings match, showing ${auctions.count}`);

  const leaderboard = await prismsmpGet("/api/leaderboard?type=balance&limit=5");
  leaderboard.entries.forEach((e) => console.log(`#${e.rank} ${e.name}: ${e.value}`));

  const playerCount = await prismsmpGet("/api/players/count");
  console.log(`${playerCount.online} players online`);
}

main().catch((err) => {
  console.error(err.message);
  process.exit(1);
});

Python (requests)

prismsmp_client.pypython
import os
import time
import requests

API_BASE = "https://api.prismsmp.net"
API_KEY = os.environ["PRISMSMP_API_KEY"]


class PrismSMPApiError(Exception):
    def __init__(self, status, message):
        super().__init__(message)
        self.status = status


def prismsmp_get(path, params=None):
    res = requests.get(
        f"{API_BASE}{path}",
        headers={"X-API-Key": API_KEY},
        params=params,
    )

    if res.status_code == 429:
        retry_after = int(res.headers.get("Retry-After", "60"))
        raise PrismSMPApiError(429, f"Rate limited, retry after {retry_after}s")

    if not res.ok:
        try:
            message = res.json().get("error", f"HTTP {res.status_code}")
        except ValueError:
            message = f"HTTP {res.status_code}"
        raise PrismSMPApiError(res.status_code, message)

    return res.json()


def main():
    health = prismsmp_get("/api/health")
    print("Status:", health["status"])

    try:
        player = prismsmp_get("/api/player/SomePlayer")
        print(f"{player['name']}: ${player['balance']} balance, {player['stats']['kills']} kills")
    except PrismSMPApiError as err:
        if err.status == 404:
            print("No player with that name/UUID has been seen before.")
        else:
            raise

    auctions = prismsmp_get("/api/auction/search", params={"item": "netherite", "sort": "cheap", "limit": 5})
    print(f"{auctions['total_matching']} listings match, showing {auctions['count']}")

    leaderboard = prismsmp_get("/api/leaderboard", params={"type": "balance", "limit": 5})
    for entry in leaderboard["entries"]:
        print(f"#{entry['rank']} {entry['name']}: {entry['value']}")

    player_count = prismsmp_get("/api/players/count")
    print(f"{player_count['online']} players online")


if __name__ == "__main__":
    main()

Discord.js slash command

The main use case this API is built for: a /stats command that looks up a player (by the Discord-provided option value, e.g. their Minecraft username) and replies with an embed showing rank, balance, and combat stats. This example handles the two failure modes you'll actually hit in production — 404 player not found and 429 rate limited — instead of only covering the happy path.

Command registration

deploy-commands.jsjavascript
const { SlashCommandBuilder } = require("discord.js");

module.exports = new SlashCommandBuilder()
  .setName("stats")
  .setDescription("Look up a PrismSMP player's rank, balance, and stats")
  .addStringOption((option) =>
    option
      .setName("player")
      .setDescription("Minecraft username or UUID")
      .setRequired(true)
  );

Command handler

commands/stats.jsjavascript
const { EmbedBuilder } = require("discord.js");

const API_BASE = "https://api.prismsmp.net";
const API_KEY = process.env.PRISMSMP_API_KEY;

const RANK_COLOR = {
  "0": 0x000000, "1": 0x0000AA, "2": 0x00AA00, "3": 0x00AAAA,
  "4": 0xAA0000, "5": 0xAA00AA, "6": 0xFFAA00, "7": 0xAAAAAA,
  "8": 0x555555, "9": 0x5555FF, "a": 0x55FF55, "b": 0x55FFFF,
  "c": 0xFF5555, "d": 0xFF55FF, "e": 0xFFFF55, "f": 0xFFFFFF
};

function stripColorCodes(s) {
  return s.replace(/&[0-9a-fk-or]/g, "");
}

function embedColorFor(rankDisplay) {
  const match = rankDisplay.match(/&([0-9a-f])/);
  return match ? RANK_COLOR[match[1]] : 0x5ec8d8;
}

async function fetchPlayer(name) {
  const res = await fetch(`${API_BASE}/api/player/${encodeURIComponent(name)}`, {
    headers: { "X-API-Key": API_KEY }
  });

  if (res.status === 404) return { notFound: true };

  if (res.status === 429) {
    const retryAfter = Number(res.headers.get("Retry-After")) || 60;
    return { rateLimited: true, retryAfter };
  }

  if (!res.ok) {
    const body = await res.json().catch(() => ({}));
    throw new Error(body.error || `PrismSMP API returned ${res.status}`);
  }

  return { player: await res.json() };
}

module.exports = {
  async execute(interaction) {
    const query = interaction.options.getString("player", true);
    await interaction.deferReply();

    let result;
    try {
      result = await fetchPlayer(query);
    } catch (err) {
      await interaction.editReply(
        `Something went wrong talking to the PrismSMP API: ${err.message}`
      );
      return;
    }

    if (result.notFound) {
      await interaction.editReply(`No known player matches **${query}**.`);
      return;
    }

    if (result.rateLimited) {
      await interaction.editReply(
        `This bot is being rate limited right now. Try again in about ${result.retryAfter}s.`
      );
      return;
    }

    const player = result.player;
    const rankLabel = stripColorCodes(player.rank.display);

    const embed = new EmbedBuilder()
      .setTitle(player.name)
      .setColor(embedColorFor(player.rank.display))
      .addFields(
        { name: "Rank", value: rankLabel, inline: true },
        { name: "Balance", value: `$${player.balance.toLocaleString()}`, inline: true },
        { name: "Shards", value: player.shards.toLocaleString(), inline: true },
        { name: "Kills / Deaths", value: `${player.stats.kills} / ${player.stats.deaths}`, inline: true },
        { name: "Playtime", value: `${player.stats.playtime_hours.toFixed(1)}h`, inline: true },
        { name: "Online", value: player.online ? "Yes" : "No", inline: true }
      );

    if (player.stats_may_lag) {
      embed.setFooter({ text: "Combat/playtime stats may be slightly out of date while online." });
    }

    await interaction.editReply({ embeds: [embed] });
  }
};
Why defer the reply

Discord interactions expire after 3 seconds; deferReply() buys up to 15 minutes by showing a "thinking..." state while the API call completes, then editReply() fills in the real result — including the error and rate-limit paths above.