▲ Build on 24 Hour Gaming

The 24HG Developer API

A public, read-only REST API over the whole 24HG platform — cross-game killboard, competitive ELO ladders, XP leaderboards, live servers, tournaments, forum and achievements. One API key, every game.

Quick start

Every public endpoint lives under the /api-platform/v1 base and is authenticated with an API key sent in the x-api-key header. One call gets you live cross-game data:

# Recent kills across CS2, CS1.6 and Rust
curl https://api.24hgaming.com/api-platform/v1/killboard/feed?limit=5 \
  -H "x-api-key: 24hg_your_key_here"

Responses are JSON. All v1 endpoints are read-only — the developer API never mutates platform data. Get a key in under a minute below.

Get an API key

API keys are tied to your 24HG account. Sign in at accounts.24hgaming.com to get a session token, then create a key:

# Create a named key (returns the secret ONCE — save it)
curl -X POST https://api.24hgaming.com/api-platform/keys \
  -H "Authorization: Bearer <your_session_jwt>" \
  -H "Content-Type: application/json" \
  -d '{ "name": "my-app", "scopes": ["read"] }'
{
  "key": {
    "id": 12, "name": "my-app",
    "key_prefix": "24hg_a1b2c3",
    "tier": "free", "rate_limit": 100,
    "api_key": "24hg_a1b2c3..."   // shown once — store it now
  }
}
Heads up: the full api_key is returned only on creation and never again. You can hold up to 10 active keys, list them with GET /api-platform/developer/my-keys, and revoke with DELETE /api-platform/keys/:id. Keys expire one year after creation.

Authentication

Send your key in the x-api-key request header on every v1 call. Keys are hashed at rest and scoped to read access.

x-api-key: 24hg_a1b2c3d4e5f6...

Error responses

StatusMeaning
401Missing, invalid, revoked or expired API key
429Rate limit exceeded — see the limit, window and retry_after fields, plus the Retry-After response header
400Bad request — an unsupported query value (e.g. an unknown game)

Rate limits

Each key has an hourly request budget set by your subscription tier. Limits are counted per key over a rolling one-hour window; exceeding one returns 429.

Free
100/hour
Default for every API key.
Authenticated
1,000/hour
Active subscribers. Gold tier raises this to 5,000/hour.
Premium
10,000/hour
Platinum subscribers.
Partner
50,000/hour
Approved partner applications.

Need more? Apply for partner access on Discord.

Response headers

Every authenticated v1 response carries your live budget so you can throttle client-side without guessing:

HeaderMeaning
X-RateLimit-LimitYour tier's request budget per rolling hour.
X-RateLimit-RemainingRequests left in the current window (after this call).
X-RateLimit-ResetUnix epoch (seconds) when the window frees up a slot.
Retry-AfterOn a 429 only: seconds to wait before retrying.

Endpoints

All paths below are relative to https://api.24hgaming.com/api-platform/v1.

GET/killboard/feed

Recent kills across every game (CS2, CS1.6, Rust) from the unified cross-game killboard.

ParamInDescription
gamequery · optionalcs2, cs16 or rust
playerquery · optionalFilter to a player — Steam ID or u<userId> (e.g. u42)
limitquery · optionalMax results, 1–100 (default 50)
{
  "kills": [{
    "id": 12, "game": "cs2", "server_id": 3,
    "killer": { "steam_id": "7656...", "user_id": 4, "name": "gamer42" },
    "victim": { "steam_id": "7656...", "user_id": 6, "name": "rival" },
    "weapon": "ak47", "headshot": true, "distance": 18,
    "round_number": 5, "occurred_at": "2026-06-15T12:00:00Z"
  }]
}
GET/killboard/top-frag

Top fraggers across every game over a time window, with headshot percentage.

ParamInDescription
periodquery · optionalweek, month or all (default all)
gamequery · optionalcs2, cs16 or rust
limitquery · optionalMax results, 1–100 (default 25)
{
  "period": "all",
  "leaders": [{
    "rank": 1, "user_id": 4, "steam_id": "7656...",
    "name": "gamer42", "game": "cs2",
    "kills": 120, "headshots": 48, "hs_pct": 40
  }]
}
GET/stats/elo

The Counter-Strike competitive ELO ladder — ranked rating, games, wins and win %. Distinct from the XP-based /leaderboards.

ParamInDescription
gamequery · optionalcs2 or cs16 (default cs2)
limitquery · optionalMax results, 1–200 (default 100)
{
  "game": "cs2",
  "leaderboard": [{
    "rank": 1, "user_id": 4, "steam_id": "7656...",
    "name": "gamer42", "rating": 1240,
    "games": 30, "wins": 18, "win_pct": 60
  }]
}
GET/leaderboards

The platform-wide XP leaderboard — users ranked by total cross-game XP.

ParamInDescription
limitquery · optionalMax results, 1–100 (default 100)
{
  "leaderboard": [{
    "rank": 1, "user_id": 1, "username": "gamer42",
    "level": 50, "total_xp": 99000
  }]
}
GET/servers

All active game servers with live player counts and current map.

ParamInDescription
gamequery · optionalFilter by game, e.g. cs2, rust, minecraft
{
  "servers": [{
    "id": 1, "name": "24HG CS2 Main", "game": "cs2",
    "ip": "49.12.36.159", "port": 27015, "status": "online",
    "players": 12, "max_players": 20, "map": "de_dust2"
  }]
}
GET/tournaments

Upcoming, active and completed tournaments.

ParamInDescription
statusquery · optionalupcoming, active or completed
{
  "tournaments": [{
    "id": 1, "name": "CS2 Weekly Cup", "game": "cs2",
    "status": "upcoming", "max_teams": 16,
    "starts_at": "2026-04-01T18:00:00Z"
  }]
}
GET/users/{id}

Public profile for a user by ID.

ParamInDescription
idpath · requiredUser ID (integer)
{
  "user": {
    "id": 1, "username": "gamer42", "avatar": "/uploads/avatar.png",
    "level": 15, "achievements_count": 8, "joined": "2025-01-01T00:00:00Z"
  }
}
GET/users/{id}/stats

XP, level, achievement count, forum post count and tournament participation for a user.

ParamInDescription
idpath · requiredUser ID (integer)
{
  "stats": {
    "xp": 4500, "level": 15, "total_xp": 12000,
    "achievements": 8, "forum_posts": 42, "tournaments_played": 3
  }
}
GET/forum/threads

Recent forum threads with author info. Fetch a thread's posts with GET /forum/threads/{id}/posts.

ParamInDescription
category_idquery · optionalFilter by category ID
limitquery · optionalMax results, 1–50 (default 25)
offsetquery · optionalPagination offset (default 0)
{
  "threads": [{
    "id": 1, "title": "Welcome!", "author": "admin",
    "views": 150, "created_at": "2025-06-01T00:00:00Z"
  }]
}
GET/achievements

Every available achievement with descriptions, categories and XP rewards.

ParamInDescription
categoryquery · optionalFilter by category
{
  "achievements": [{
    "id": 1, "name": "First Blood", "description": "Win your first match",
    "icon": "🏆", "category": "general", "xp_reward": 100
  }]
}

Webhooks

The v1 endpoints above are pull — you ask, we answer. Webhooks are the push side: register an HTTPS endpoint and 24HG will POST a signed JSON event to it the moment something happens across any game — a kill on the cross-game killboard, a match or tournament result, a server going up or down, or a ban. No polling, every game, one subscription.

Different base & auth. Webhook management lives at https://api.24hgaming.com/webhooks and is authenticated with your account session token (Authorization: Bearer <jwt>), not the x-api-key header. Registering is gated by the API-key tiers — you must hold at least one active key, and your tier sets how many subscriptions you can create.

Event types

Subscribe to any subset of these, or to ["*"] for all of them. Each delivery's event field and the X-24HG-Event header tell you which fired.

EventFires when
killboard.killA kill is recorded on the unified cross-game killboard.
match.resultA match (hub / quickmatch / tournament) finishes with a result.
tournament.resultA tournament concludes with final standings.
server.upA game server comes online.
server.downA game server goes offline.
ban.createdA player ban is issued.

Register a subscription

POST your endpoint URL and the events you care about. The response returns a signing secret exactly once — store it now; it is never shown again and is used to verify every delivery.

# Subscribe to cross-game kills + match results
curl -X POST https://api.24hgaming.com/webhooks \
  -H "Authorization: Bearer <your_session_jwt>" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.example.com/hooks/24hg",
    "events": ["killboard.kill", "match.result"],
    "description": "my killfeed bot"
  }'
{
  "subscription": {
    "id": 7, "url": "https://your-app.example.com/hooks/24hg",
    "events": ["killboard.kill", "match.result"],
    "tier": "free", "active": true, "secret_set": true
  },
  "secret": "whsec_3f9a...e21",   // shown once — store it now
  "message": "Store this signing secret now — it will not be shown again."
}

Manage subscriptions with GET /webhooks (list), PUT /webhooks/:id (change url / events / active / description), POST /webhooks/:id/rotate-secret (issue a fresh secret) and DELETE /webhooks/:id.

Payload & headers

Every delivery is an HTTP POST with a JSON envelope. data is the event-specific body; the rest is constant across event types.

{
  "id": "a1b2c3d4-...",        // unique delivery id (also X-24HG-Delivery)
  "event": "killboard.kill",
  "created_at": "2026-06-15T19:30:00Z",
  "data": {
    "id": 12, "game": "cs2", "server_id": 3, "weapon": "ak47",
    "headshot": true, "distance": 18, "round": 5,
    "killer": { "user_id": 4, "steam_id": "7656...", "name": "gamer42" },
    "victim": { "user_id": 6, "steam_id": "7656...", "name": "rival" }
  }
}

Each request carries these headers:

HeaderMeaning
X-24HG-EventThe event type, e.g. killboard.kill.
X-24HG-DeliveryUnique delivery id — use it to dedupe retries idempotently.
X-24HG-TimestampISO-8601 time the delivery was created.
X-24HG-Signaturesha256=<hex> HMAC of the raw body — see below.

Verify signatures

The X-24HG-Signature header is sha256= followed by the hex HMAC-SHA256 of the exact raw request body, keyed by your subscription's signing secret. Compute the same HMAC and compare in constant time before trusting a payload — reject anything that doesn't match.

// Node.js / Express — verify a 24HG webhook
const crypto = require('crypto');

app.post('/hooks/24hg', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.get('X-24HG-Signature') || '';
  const expected = 'sha256=' + crypto
    .createHmac('sha256', process.env.WH_SECRET)   // your whsec_… secret
    .update(req.body)                              // the raw Buffer, not parsed JSON
    .digest('hex');
  const ok = sig.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
  if (!ok) return res.sendStatus(401);

  const evt = JSON.parse(req.body);                // { id, event, created_at, data }
  // …handle evt.event… then ACK fast:
  res.sendStatus(200);
});
ACK quickly. Respond 2xx as soon as you've stored the event; do heavy work asynchronously. Any non-2xx (or a timeout past 10s) is treated as a failure and retried.

Retries & testing

A delivery is attempted up to 3 times with exponential backoff (≈1s, then ≈3s) until your endpoint returns a 2xx. Each attempt and its outcome is recorded in a per-subscription delivery log. Send yourself a signed sample event any time with the test endpoint:

# Fire a one-off signed test delivery to a subscription
curl -X POST https://api.24hgaming.com/webhooks/7/test \
  -H "Authorization: Bearer <your_session_jwt>"
{ "delivery_id": "…", "success": true, "attempts": 1,
  "status_code": 200, "duration_ms": 142 }

Inspect recent attempts (status code, retries, latency, errors) with GET /webhooks/:id/deliveries. Repeated failures increment the subscription's consecutive_failures counter, surfaced on GET /webhooks.

SDK & resources

The complete machine-readable spec is served live as OpenAPI 3.0 — point Swagger UI, Postman or a codegen tool straight at it:

GET https://api.24hgaming.com/api-platform/docs
OpenAPI specapi.24hgaming.com/api-platform/docs
Developer portal/api-platform/developer/portal — SDK, key management, rate limits
Community24HG Discord — support & partner access
Platform24HG Social · Stats