Documentation menu

Data API (reference)

A read-only, customer-scoped HTTP API for your own tooling — killfeed bots, leaderboard sites, Discord integrations — to read the servers you own. A key can only ever see the servers belonging to the account that minted it.

Base URL: https://api.citadel-hub.com/api/v1.

Everything below lives under /data. The interactive reference (try-it-out) is at /developers, and the machine-readable OpenAPI 3.1 spec is served unauthenticated at https://api.citadel-hub.com/api/v1/openapi.json.

Getting a key

Mint a key in your Citadel dashboard under Account → API Keys. The raw value (ctk_…) is shown once at creation and never again — copy it immediately. If you lose it, revoke it and mint a new one. Keys are scoped to your account and cannot be transferred.

Authentication

Send the key on every /data request, either as a bearer token or a custom header:

Authorization: Bearer ctk_…

or

X-Citadel-Api-Key: ctk_…

A missing, unknown, or revoked key returns 401. Requesting a server you don't own returns 404 (not 403) — so a key can't probe the id space of other accounts.

Rate limits

Each key carries a tier that sets two ceilings: a per-minute request rate and a hard cap on any single page's limit. Exceeding the rate returns 429 with a retryAfter of 60 seconds; a limit above your tier's page cap is silently clamped down. The default standard tier is sufficient for a typical killfeed or leaderboard integration; contact support for a higher tier.

No coordinates, by design

This API deliberately exposes no live player coordinates (no x/y/z). Position streams are exactly what enable radar and other cheating, which cuts against the Trust Network's whole reason for existing. Online-player endpoints return identities and session info only.

Errors

Errors share one shape:

{
  "error": "NOT_FOUND",
  "message": "Server not found",
  "statusCode": 404,
  "requestId": "…"
}

Quote requestId to support when reporting a failure.


GET /data/servers

Every non-revoked server owned by the key's account, with live online status, newest activity first.

curl https://api.citadel-hub.com/api/v1/data/servers \
  -H "Authorization: Bearer ctk_…"
{
  "servers": [
    {
      "id": "0f9c2b6e-1a2b-4c3d-8e4f-5a6b7c8d9e0f",
      "name": "Chernarus Hardcore",
      "map": "chernarusplus",
      "gameVersion": "1.29",
      "online": true,
      "lastSeenAt": "2026-07-16T18:42:10.000Z"
    }
  ]
}

Use the id here as the {serverId} in every endpoint below.

GET /data/servers/

One server's status, including the latest metrics sample (player count, FPS, uptime).

curl https://api.citadel-hub.com/api/v1/data/servers/0f9c2b6e-1a2b-4c3d-8e4f-5a6b7c8d9e0f \
  -H "Authorization: Bearer ctk_…"
{
  "id": "0f9c2b6e-1a2b-4c3d-8e4f-5a6b7c8d9e0f",
  "name": "Chernarus Hardcore",
  "map": "chernarusplus",
  "gameVersion": "1.29",
  "online": true,
  "players": 38,
  "fps": 42.7,
  "uptimeSeconds": 51840,
  "lastSeenAt": "2026-07-16T18:42:10.000Z",
  "metricsAt": "2026-07-16T18:42:05.000Z"
}

players, fps, uptimeSeconds, and metricsAt are null when no metrics have been sampled yet.

GET /data/servers//players

Players active in the last 90 seconds, identities only. No coordinates.

curl https://api.citadel-hub.com/api/v1/data/servers/0f9c2b6e-…/players \
  -H "Authorization: Bearer ctk_…"
{
  "players": [
    { "steamId": "76561198000000001", "name": "Survivor_Ana" },
    { "steamId": "76561198000000002", "name": "Boris" }
  ],
  "count": 2
}

GET /data/servers//leaderboard

Ranked players for the server.

QueryDefaultNotes
sortpointsOne of points, kd, kills
limit251–1000, clamped to your tier's page cap
offset0For paging through the ranking
curl "https://api.citadel-hub.com/api/v1/data/servers/0f9c2b6e-…/leaderboard?sort=kills&limit=2" \
  -H "Authorization: Bearer ctk_…"
{
  "total": 1284,
  "sort": "kills",
  "players": [
    {
      "rank": 1,
      "steamId": "76561198000000010",
      "name": "Reaper",
      "kills": 412,
      "deaths": 88,
      "headshots": 190,
      "kd": 4.68,
      "points": 824,
      "meanKillDistance": 143.2,
      "playMinutes": 9820
    },
    {
      "rank": 2,
      "steamId": "76561198000000011",
      "name": "Ghost",
      "kills": 388,
      "deaths": 121,
      "headshots": 150,
      "kd": 3.21,
      "points": 776,
      "meanKillDistance": 96.5,
      "playMinutes": 11040
    }
  ]
}

meanKillDistance (metres) is null for a player with no recorded kills. points is kills × 2. total is the full player count across the ranking (use it with offset to page).

GET /data/servers//kills

The killfeed, newest first. This is a cursor feed: pass the previous response's nextCursor back as before to page backwards in time. nextCursor is null when there are no older kills.

QueryDefaultNotes
beforenowISO-8601 timestamp; returns kills strictly before this instant
limit501–1000, clamped to your tier's page cap
curl "https://api.citadel-hub.com/api/v1/data/servers/0f9c2b6e-…/kills?limit=1" \
  -H "Authorization: Bearer ctk_…"
{
  "kills": [
    {
      "ts": "2026-07-16T18:41:52.000Z",
      "killerSteamId": "76561198000000010",
      "killerName": "Reaper",
      "victimSteamId": "76561198000000022",
      "victimName": "Fresh_Spawn",
      "weapon": "M4-A1",
      "distance": 118.4,
      "isHeadshot": true,
      "hitZone": "Head"
    }
  ],
  "nextCursor": "2026-07-16T18:41:52.000Z"
}

To pull the next page: ?before=2026-07-16T18:41:52.000Z. hitZone may be null.

GET /data/servers//bans

The server's ban list, newest sync first.

QueryDefaultNotes
active(all)true for currently-active bans, false for expired; omit for both
limit1001–1000, clamped to your tier's page cap
offset0For paging
curl "https://api.citadel-hub.com/api/v1/data/servers/0f9c2b6e-…/bans?active=true&limit=1" \
  -H "Authorization: Bearer ctk_…"
{
  "bans": [
    {
      "steamId": "76561198000000099",
      "playerName": "Cheater123",
      "reason": "Aimbot",
      "source": "manual",
      "permanent": false,
      "bannedAt": "2026-07-10T12:00:00.000Z",
      "expiresAt": "2026-08-10T12:00:00.000Z"
    }
  ]
}

permanent is true (and expiresAt null) for a ban with no expiry. source is one of manual, import, or cloud.


See also: the public Trust API for cross-community player trust lookups (no key required).