v1

Rankacy Sportsbook B2B API

Read-only HTTP feed of CS2 bot-league matches, markets, live odds and stream metadata for sportsbook and bookmaker partners.

OpenAPI Schema A machine-readable OpenAPI spec is served at /openapi.json on every running instance.

Core Concepts

ConceptDescription
MatchOne bot-vs-bot CS2 game on a specific map. States: PREGAME, WARMUP, LIVE, FINISHED.
RoundOne in-match round, tracked via current_round_number.
MarketA betting market type: MATCH (full match winner), ROUND (current round), NEXT_ROUND.
SidesCT / T refer to the server role. DRAW is only available on the MATCH market. Team IDs map sides to actual teams.
Decimal oddsAlways returned as strings (e.g. "1.87"). No fractional or American formats.
TimestampsISO-8601 in UTC throughout.

Authentication

Every request must include an API key via the X-API-Key HTTP header:

X-API-Key: rksb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Keys are issued by a Rankacy administrator via the admin panel. The raw key is shown once at creation — if lost, request a key rotation.

Quick Check
curl -H "X-API-Key: $RANKACY_API_KEY" \
  https://YOUR-HOST/api/b2b/ping
{
  "status": "ok",
  "partner_id": 3,
  "partner_name": "Fortuna CZ",
  "server_time": "2026-04-15T10:22:03Z"
}

Base URL

All endpoints are served under:

/api/b2b/*

Replace YOUR-HOST in examples with the hostname provided during onboarding.

Error Handling

All errors use a standard JSON envelope:

{ "detail": "Match not found" }
StatusMeaning
401Missing / invalid API key, or partner deactivated
404Unknown match, season, or webhook ID
422Invalid query parameters (bad enum value, out-of-range, etc.)
5xxServer error — safe to retry after a short backoff

Endpoints — Core

GET /api/b2b/ping Auth probe & health check

Validates your API key and returns your partner identity with the current server time.

Response

statusstringAlways "ok"
partner_idintegerYour partner ID
partner_namestringYour partner display name
server_timedatetimeCurrent UTC server time
GET /api/b2b/markets Static market catalogue

Returns the three supported betting markets. This data is static — safe to cache indefinitely.

Response — Array of:

codeenumMATCH | ROUND | NEXT_ROUND
namestringHuman-readable market name
descriptionstringMarket description
sidesstring[]Available sides, e.g. ["CT","T","DRAW"]
GET /api/b2b/matches Paginated match list

Query Parameters

ParamTypeDefaultDescription
statusenumPREGAME, WARMUP, LIVE, FINISHED
season_idintegerFilter to one season/tournament
updated_sincedatetimeOnly matches updated after this instant
limitinteger201–100
offsetinteger0Pagination offset

Response

totalintegerTotal matching records
limitintegerRequested page size
offsetintegerCurrent offset
itemsMatchSummary[]Array of match summaries (see schema below)
webhook_eventsenum[]Webhook events that can invalidate this cached list

MatchSummary Schema

match_idintegerUnique match identifier
unique_match_idstringGlobally unique match ID
map_namestringCS2 map name (e.g. de_dust2)
statusenumPREGAME | WARMUP | LIVE | FINISHED
team_1TeamSummary{ id, name, logo_name }
team_2TeamSummary{ id, name, logo_name }
team_1_scoreintegerTeam 1 round wins
team_2_scoreintegerTeam 2 round wins
ct_team_idinteger?Which team is currently CT (live matches only)
t_team_idinteger?Which team is currently T (live matches only)
current_round_numberinteger?Active round number
season_idinteger?Season/tournament this match belongs to
starts_atdatetimeScheduled start time
updated_atdatetimeLast update timestamp
Example
curl -H "X-API-Key: $KEY" \
  "https://HOST/api/b2b/matches?status=LIVE&limit=5"
GET /api/b2b/matches/upcoming Queued upcoming matches

Returns queued season matches that haven't started yet.

Query Parameters

ParamTypeDefaultDescription
season_idintegerFilter to one season
limitinteger201–100
offsetinteger0Pagination offset

Response

totalintegerTotal matching records
limitintegerRequested page size
offsetintegerCurrent offset
itemsUpcomingMatchSummary[]Array of queued upcoming matches
webhook_eventsenum[]Webhook events that can invalidate this cached upcoming list

UpcomingMatchSummary Schema

queue_idintegerQueue entry ID
unique_match_idstringGlobally unique match ID
queued_atdatetimeWhen the match was queued
season_idintegerSeason ID
season_namestringSeason display name
team_1TeamSummary{ id, name, logo_name }
team_2TeamSummary{ id, name, logo_name }
statusenumQueue status
GET /api/b2b/matches/{match_id} Full match detail

Returns all MatchSummary fields plus match-page specific context. Returns 404 if the match does not exist.

Path Parameters

ParamTypeDescription
match_idrequired integerMatch ID

Treat this endpoint as the source of truth for score, status and side mapping. ct_team_id / t_team_id tell you which real team currently occupies the CT / T side, while current_round_number tells you which round the live markets refer to.

Additional Response Fields

season_namestring?Name of the season/tournament
roundsMatchRound[]Ordered round summaries with parsed round events
webhook_eventsenum[]Webhook events that should trigger a refetch for this match_id

MatchRound Schema

round_numberintegerRound number in match order
ct_team_idinteger?Team currently on CT for the round
t_team_idinteger?Team currently on T for the round
winner_team_idinteger?Winning team for the round, when known
win_reasonstring?Round-end reason such as elimination or bomb outcome
eventsMatchRoundEvent[]Newest-first parsed event feed for the round

MatchRoundEvent Schema

event_labelstringShort uppercase label such as KILL or PLANT_STARTED
event_summarystringHuman-readable one-line summary
event_categorystring?High-level group such as kill, plant, defuse, winner, utility
event_timestampstringIn-event timestamp text when available
is_importantbooleanWhether the event is important enough for compact feeds
Webhook usage Webhooks are invalidation signals, not full match payloads. When one of the listed webhook_events arrives for this match_id, refetch this endpoint.
GET /api/b2b/matches/{match_id}/odds Latest odds snapshot

Returns the latest odds for all three markets on a match. Each market reports is_available — a market may be unavailable between rounds when the model has no active prediction.

Path Parameters

ParamTypeDescription
match_idrequired integerMatch ID

Response

match_idintegerMatch ID
updated_atdatetime?Latest update across all markets
marketsMarketOdds[]Array of market odds (see below)
webhook_eventsenum[]Webhook events that can invalidate this cached odds snapshot

MarketOdds Schema

marketenumMATCH | ROUND | NEXT_ROUND
round_numberinteger?Applicable round (for ROUND/NEXT_ROUND markets)
is_availablebooleanWhether the market is currently active
margindecimal?Bookmaker margin
updated_atdatetime?When these odds were last computed
sidesMarketOddsSide[]Odds per side

MarketOddsSide Schema

sidestringCT, T, or DRAW
team_idinteger?Team on this side (null for DRAW)
decimal_odddecimalStandard decimal odd (e.g. "1.87")
Example Response
{
  "match_id": 42,
  "updated_at": "2026-04-15T10:22:03Z",
  "markets": [
    {
      "market": "MATCH",
      "round_number": null,
      "is_available": true,
      "margin": "0.05",
      "updated_at": "2026-04-15T10:22:03Z",
      "sides": [
        { "side": "CT", "team_id": 9,    "decimal_odd": "1.87" },
        { "side": "T",  "team_id": 10,   "decimal_odd": "1.92" },
        { "side": "DRAW", "team_id": null, "decimal_odd": "18.00" }
      ]
    },
    {
      "market": "ROUND",
      "round_number": 7,
      "is_available": true,
      "margin": "0.04",
      "updated_at": "2026-04-15T10:22:01Z",
      "sides": [
        { "side": "CT", "team_id": 9,  "decimal_odd": "2.10" },
        { "side": "T",  "team_id": 10, "decimal_odd": "1.75" }
      ]
    }
  ]
}
GET /api/b2b/matches/{match_id}/odds/history Historical odds for one market

Returns the time-series of odds snapshots for a specific market on a match.

Path Parameters

ParamTypeDescription
match_idrequired integerMatch ID

Query Parameters

ParamTypeDefaultDescription
marketrequired enumMATCH, ROUND, or NEXT_ROUND
round_numberintegerRequired for ROUND / NEXT_ROUND

Response

match_idintegerMatch ID
marketenumRequested market type
round_numberinteger?Requested round
pointsOddsHistoryPoint[]Ordered by updated_at
webhook_eventsenum[]Webhook events that can invalidate this cached history view

OddsHistoryPoint Schema

idintegerPoint ID
sidestringCT, T, or DRAW
decimal_odddecimalOdd at this point in time
margindecimalMargin at this point
updated_atdatetimeTimestamp of snapshot
GET /api/b2b/odds/changes Live odds delta feed
Primary Live Feed This is the main endpoint for real-time odds integration. Poll every 1–2 seconds.

Returns every market snapshot written after the supplied since timestamp.

Query Parameters

ParamTypeDefaultDescription
sincedatetimeOmit on first call. Use previous server_time for subsequent calls.
limitinteger2001–1000

Response

sincedatetime?The since value you sent
server_timedatetimeUse as since in your next request
changesOddsChange[]Changed market snapshots
webhook_eventsenum[]Webhook events that can replace polling for this feed

OddsChange Schema

match_idintegerMatch this change belongs to
match_statusenumCurrent match status
marketenumMATCH | ROUND | NEXT_ROUND
round_numberinteger?Applicable round
updated_atdatetimeWhen this snapshot was computed
is_availablebooleanWhether this market is active
sidesMarketOddsSide[]Odds per side
Example Response
{
  "since": "2026-04-15T10:22:00Z",
  "server_time": "2026-04-15T10:22:03.421Z",
  "webhook_events": ["ODDS_UPDATED"],
  "changes": [
    {
      "match_id": 42,
      "match_status": "LIVE",
      "market": "MATCH",
      "round_number": null,
      "updated_at": "2026-04-15T10:22:03Z",
      "is_available": true,
      "sides": [
        { "side": "CT",   "team_id": 9,    "decimal_odd": "1.87" },
        { "side": "T",    "team_id": 10,   "decimal_odd": "1.92" },
        { "side": "DRAW", "team_id": null,  "decimal_odd": "18.00" }
      ]
    }
  ]
}
GET /api/b2b/stream Twitch embed config

Returns the Twitch channel and embed URL for displaying the live stream on your site. The parent query parameter is derived from your request hostname.

Response

channelstringTwitch channel name
embed_urlstringReady-to-use iframe src URL

Seasons

GET /api/b2b/seasons List all seasons

Returns all seasons/tournaments.

Response — Array of:

idintegerSeason ID
namestringSeason display name
is_activebooleanWhether this season is currently running
created_atdatetimeWhen the season was created
GET /api/b2b/seasons/{season_id}/standings Season standings / leaderboard

Returns ranked standings for a season, ordered by points.

Path Parameters

ParamTypeDescription
season_idrequired integerSeason ID

Response

seasonSeasonSummarySeason metadata
rowsStandingRow[]Ranked team rows

StandingRow Schema

rankintegerPosition in standings
team_idintegerTeam ID
team_namestringTeam display name
winsintegerMatch wins
drawsintegerMatch draws
lossesintegerMatch losses
pointsintegerTotal points

Webhooks

Register HTTPS endpoints to receive real-time push notifications. Each delivery is signed with HMAC-SHA256 so you can verify authenticity server-side.

Webhooks are lightweight invalidation messages, not full REST payloads. Use the incoming match_id plus each endpoint's webhook_events field to decide which B2B endpoint to refetch.

Supported Event Types

EventDescription
MATCH_CREATEDA new match has been created
MATCH_STARTEDA match transitioned to LIVE
MATCH_FINISHEDA match reached final state
ODDS_UPDATEDOdds changed for any market on a match

Delivery Headers

HeaderValue
Content-Typeapplication/json
X-Rankacy-EventOne of the event types above
X-Rankacy-Signaturesha256=<hex> — HMAC-SHA256 of the request body
No automatic retries Failed deliveries are logged but not retried. Use the /odds/changes poll feed as your source of truth.
GET /api/b2b/webhooks List your webhook subscriptions

Response — Array of:

idintegerWebhook ID
event_typeenumSubscribed event
target_urlstringYour receiving endpoint
is_activebooleanWhether the subscription is active
created_atdatetimeWhen registered
POST /api/b2b/webhooks Register a webhook
Save the secret! The HMAC signing secret is returned only once in the creation response. Store it securely server-side.

Request Body

event_typeenumrequired One of the supported event types
target_urlstringrequired HTTPS URL (8–2048 chars)

Response (201 Created)

idintegerWebhook ID
event_typeenumSubscribed event
target_urlstringYour endpoint
is_activebooleanAlways true on creation
created_atdatetimeCreation time
secretstringHMAC-SHA256 signing secret (shown once!)
Example
curl -X POST -H "X-API-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d '{"event_type":"ODDS_UPDATED","target_url":"https://example.com/hooks/rankacy"}' \
  https://HOST/api/b2b/webhooks
DELETE /api/b2b/webhooks/{webhook_id} Remove a webhook subscription

Path Parameters

ParamTypeDescription
webhook_idrequired integerWebhook ID to remove

Response

{ "message": "Webhook removed" }

Returns 404 if the webhook doesn't exist or doesn't belong to you.

Verifying Webhook Signatures

Every webhook delivery is signed. Verify the X-Rankacy-Signature header before processing.

Python
import hmac, hashlib

def verify_signature(body: bytes, header: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(
        secret.encode(), body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, header)
Node.js
const crypto = require("crypto");

function verifySignature(body, header, secret) {
  const expected =
    "sha256=" +
    crypto.createHmac("sha256", secret).update(body).digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(header)
  );
}

Integration Flow

  1. Bootstrap — Call GET /markets and GET /seasons to populate your static UI data. Cache these.
  2. Render lobbyGET /matches?status=LIVE for active matches, plus GET /matches/{id} for detail.
  3. Push updates — If you register webhooks, use the endpoint-level webhook_events array as your invalidation map and refetch only the affected endpoint when an event arrives.
  4. Pull fallback — If you do not register webhooks, fetch once on page load and refresh only on user navigation. For live odds polling, use GET /odds/changes.
  5. Match page — Use GET /stream for the Twitch iframe. Show all three markets side-by-side. Treat GET /matches/{id} as the source of truth for score, status and settlement.
  6. Settlement — A match is settled when status == "FINISHED". Final scores are on the match detail response.

Polling Recipe

Recommended approach The delta feed is the most efficient way to keep odds in sync.
  1. First call: GET /api/b2b/odds/changes (no since param).
  2. Store server_time from the response.
  3. Every 1–2 seconds: GET /api/b2b/odds/changes?since=<server_time>
  4. Apply changes to your local cache, keyed by (match_id, market, round_number).
  5. Update server_time from each new response.
Python Example
import time, requests

API = "https://HOST/api/b2b"
HEADERS = {"X-API-Key": "rksb_live_..."}
since = None

while True:
    params = {"limit": 500}
    if since:
        params["since"] = since

    r = requests.get(f"{API}/odds/changes", headers=HEADERS, params=params)
    data = r.json()
    since = data["server_time"]

    for change in data["changes"]:
        key = (change["match_id"], change["market"], change["round_number"])
        # update your local cache with change["sides"], change["is_available"]

    time.sleep(1.5)

Versioning & Change Policy