Read the docs. Then ship.
Every endpoint has a worked example. Every example runs. Pick a guide below or jump straight to the API reference.
Guides
Make your first call
Sign up, mint a key, curl /v1/matches?status=live. 30 seconds end-to-end.
Interactive API reference
Every endpoint. Every response. Every error envelope. ReDoc-rendered, always in sync with the live spec.
League of Legends
LoL endpoints, schema, Riot partnership notes, live state cadence.
Counter-Strike 2
CS2 fixtures, tournaments and brackets. Demo parsing is not included today.
Tiers & quotas
How daily quotas + RPS bursts work, what the headers expose, and how to upgrade.
Sandbox
esp_test_ keys, fixture data, zero quota burn. Evaluate before paying.
Webhooks
Pro-tier outbound events. HMAC signature verification recipe for Python + TypeScript.
Authentication
Mint a key at /dashboard. Pass it as a bearer token on every request:
Authorization: Bearer $STADAR_API_KEY Two key shapes:
esp_live_<22>— production keys. Hit real data; count against daily quota.esp_test_<22>— sandbox keys. Hit the fixture schema; never burn quota.
Both shapes use the same header. Server-side prefix detection routes the request. See /docs/sandbox for the full sandbox model.
GitHub OAuth signups get a session cookie automatically; the first call to POST /v1/account/keys mints your first live key.
Pagination
Cursor-based. Always. We never expose offset or page numbers because they break silently when entities mutate underneath you.
GET /v1/matches?limit=100&cursor=...
# Response
{
"data": [...],
"meta": {
"next_cursor": "eyJpZCI6MTIz...", // null when exhausted
"limit": 100,
"sources": [...]
}
} Walk by passing meta.next_cursor back as ?cursor= on the next call. Treat the
cursor as opaque — we may change its encoding.
Errors
Every error response uses one envelope:
{
"error": {
"code": "rate_limit_exceeded",
"message": "Daily quota exhausted. Upgrade at https://stadar.net/pricing.",
"request_id": "req_01HMXY7N...",
"details": { "quota_reset_at": "2026-05-17T00:00:00Z" }
}
} Common codes:
unauthorized— missing or invalidAuthorization: Bearerforbidden— key revoked, account suspendednot_found— entity doesn’t exist (or never existed)validation_error— bad request (details enumerate failing fields)rate_limit_exceeded— quota or RPS exhaustedtier_required— your tier doesn’t include this capability (see Capabilities below)internal— our problem; therequest_idis what we need to debug
Always log request_id if you’re going to ask support about a 5xx.
Rate limits
Two budgets per key: a daily quota and a per-second burst (RPS). Both enforced in Redis with sliding windows. Headers on every response expose the live state:
X-RateLimit-Daily-Limit/X-RateLimit-Daily-Remaining/X-RateLimit-Daily-ResetX-RateLimit-RPS-Limit/X-RateLimit-RPS-Remaining
When you hit a limit you get 429 Too Many Requests plus the standard Retry-After header in seconds. The SDKs do not auto-retry on 429 — that’s intentional. You
see the limit; you decide what to do.
Capabilities
Some endpoints (match stats, brackets, bulk export) gate per tier. The middleware
checks your tier on every request and returns 402 Payment Required if your tier
doesn’t permit it, with a Polar checkout link in the body:
{
"error": {
"code": "tier_required",
"message": "match_stats requires Hobbyist+",
"details": {
"required_capability": "match_stats",
"your_tier": "free",
"upgrade_url": "https://stadar.net/checkout?capability=match_stats"
}
}
} Introspect your effective capabilities at GET /v1/account/me/capabilities. The
SDKs expose this as client.account.capabilities(). Helpful when “why am I getting
a 402?” is unclear.
Capabilities defined in v1:
match_stats— Hobbyist+ — per-match participant statsbrackets— Hobbyist+ — tournament bracket DAG in responsebulk_export— Pro+ —?bulk=truereturns ≤10k matches per pagewebhooks— Pro+ — subscribe to outbound events (see below)
Webhooks (Pro+)
Subscribe to outbound events at /v1/account/webhooks. We POST to your URL with an
HMAC-SHA256 signature in X-Stadar-Signature. Verify before trusting the body.
Event types in v1:
match.scheduled— new match addedmatch.started— status transition: scheduled → livematch.completed— status transition: live → completed (with final score)tournament.bracket_updated— bracket DAG changed
See /docs/webhooks for the full signature-verification recipe in
both Python and TypeScript. Failures retry on an exponential schedule
(1s · 5s · 30s · 5m · 30m · 6h) then archive to a dead-letter table visible in the
admin panel.
Sandbox
Mint a sandbox key at POST /v1/account/keys?mode=sandbox. Same shape as a live key
but prefixed esp_test_. Hits the fixture schema (~50 matches across all 6 games,
mix of scheduled/live/completed). Doesn’t burn production quota; gets meta.sandbox: true on every response. Lets you wire your integration end-to-end without paying.
See /docs/sandbox for the full model — what’s covered, what’s
not, how often we refresh.
Attribution (CC-BY-SA)
Every successful response includes a meta.sources array listing the upstream data
sources whose terms require attribution. Liquipedia is licensed CC-BY-SA 3.0; we
satisfy attribution for you on the wire. Do not strip meta.sources from your
own responses if you re-expose Stadar data. See our Attribution Policy for the full details.