KBN partner API v1
Read-only JSON access to the official world ranking, fighter profiles, events, verified results and document verification, plus signed webhooks. Identity and contact data are never exposed, and nothing sent to the API can change points.
Authentication
Every request carries an API key as a Bearer token (X-API-Key is also accepted). Keys are issued by KBN World HQ to named partners, stored only as SHA-256 hashes, may have an expiry date, and carry scopes:
rankings.readfighters.readevents.readresults.readcurl -H "Authorization: Bearer kbn_xxxxxxxx_…" \ "https://kbn.hbzgroup.co.uk/api/v1/rankings?disc=kickboxing&cat=pro&g=m&wc=welterweight&scope=world"
Rate limits
Each key has its own per-minute limit (default 60). Responses include X-RateLimit-Limit and X-RateLimit-Remaining; exceeding the limit returns 429 with Retry-After. Every response carries as_of, the date the ranking was computed for. GET /api/v1/me shows your key's scopes, limit, expiry and request count.
Send a request
Runs in your browser against this server with your key (never stored). Demo key: kbn_demo1234_KbnDemoApiKeyForTesting0000000000
// response appears here
Endpoints
Base URL https://kbn.hbzgroup.co.uk/api/v1 · full schema in openapi.json.
| Endpoint | Scope | What it returns |
|---|---|---|
GET /api/v1/rankings | rankings.read | One division ranking table |
GET /api/v1/divisions | rankings.read | Divisions with ranked fighters and their world leader |
GET /api/v1/fighters | fighters.read | Каталог бойцов |
GET /api/v1/fighters/{slug} | fighters.read | Fighter profile with verified bouts and ranks Minors return name, country and division only. |
GET /api/v1/events | events.read | Санкционированные события |
GET /api/v1/events/{code} | events.read | One sanctioned event |
GET /api/v1/events/{code}/results | results.read | Verified results of one event |
GET /api/v1/results | results.read | Verified results across all events Newest events first. Results of minors are never included. |
GET /api/v1/verify/{number} | any key | Verify a licence no., certificate no. or KBN Fighter ID |
GET /api/v1/me | any key | The calling API key: partner, scopes, limits, expiry and usage |
{
"as_of": "2026-09-28",
"division": {"discipline": "Kickboxing", "category": "Professional", "gender": "M", "weight_class": "Welterweight"},
"scope": "world", "count": 6,
"data": [{"rank": 1, "movement": 0, "points": 635.0, "record": {"W": 4, "L": 0, "D": 0, "NC": 0, "KO": 1},
"fighter": {"slug": "karimov", "name": "Aleksei Karimov", "country": {"slug": "uzbekistan", …}, "fighter_code": "KBN-UZ-1040"}}]
}Вебхуки
KBN World HQ registers your HTTPS endpoint and the events you want:
ranking.published— A fighter's ranking is publishedresult.verified— A bout result is verifiedlicence.suspended— A licence is suspendedlicence.issued— A Fighter ID and licence are issuedevent.sanctioned— An event is sanctioned by KBN World HQ
Each delivery is a JSON POST with headers:
X-KBN-Event | event name |
X-KBN-Delivery | delivery id (unique per attempt series) |
X-KBN-Timestamp | Unix time the request was signed |
X-KBN-Attempt | 1 for the first try, then 2…5 |
X-KBN-Signature | sha256=HMAC-SHA256 of the raw body with your secret |
X-KBN-Signature-Timestamped | sha256=HMAC-SHA256 of "{timestamp}.{body}" — recommended: also reject timestamps older than 5 minutes |
X-KBN-Replay-Of | present when HQ replays an earlier delivery (its id); the payload id is unchanged, so de-duplicate on it |
Retries: any non-2xx response or timeout is retried automatically after 1 minute, 5 minutes, 30 minutes and 2 hours (5 attempts in total). Respond with 2xx quickly and process asynchronously. If a partner account is suspended, its webhooks pause.
{
"id": "5e0c…", "event": "licence.issued", "created_at": "2026-09-28T00:00:00+01:00",
"data": {"fighter": {"slug": "reid", "name": "Callum Reid", "country": "uk", "fighter_code": "KBN-GB-1041", "licence_no": "KBN-L-26-40101"},
"division": {…}, "status": "licensed", "licence_status": "ACTIVE", "issued_at": "…", "valid_until": "…", "url": "https://kbn.hbzgroup.co.uk/fighters/reid"}
}Verifying signatures
Always compute the HMAC over the raw request body and compare in constant time.
$body = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_KBN_TIMESTAMP'] ?? '';
$sig = $_SERVER['HTTP_X_KBN_SIGNATURE_TIMESTAMPED'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $ts . '.' . $body, getenv('KBN_WEBHOOK_SECRET'));
if (! hash_equals($expected, $sig) || abs(time() - (int) $ts) > 300) {
http_response_code(401); exit;
}
$event = json_decode($body, true); // de-duplicate on $event['id']
const crypto = require('crypto');
app.post('/kbn-webhook', express.raw({ type: 'application/json' }), (req, res) => {
const ts = req.get('X-KBN-Timestamp');
const expected = 'sha256=' + crypto.createHmac('sha256', process.env.KBN_WEBHOOK_SECRET)
.update(ts + '.' + req.body).digest('hex');
const got = req.get('X-KBN-Signature-Timestamped') || '';
if (got.length !== expected.length || !crypto.timingSafeEqual(Buffer.from(got), Buffer.from(expected))
|| Math.abs(Date.now() / 1000 - Number(ts)) > 300) return res.status(401).end();
res.status(204).end(); // then process JSON.parse(req.body) asynchronously
});
import hmac, hashlib, os, time
from flask import request, abort
@app.post('/kbn-webhook')
def kbn_webhook():
body = request.get_data()
ts = request.headers.get('X-KBN-Timestamp', '')
expected = 'sha256=' + hmac.new(os.environ['KBN_WEBHOOK_SECRET'].encode(), ts.encode() + b'.' + body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, request.headers.get('X-KBN-Signature-Timestamped', '')) or abs(time.time() - int(ts or 0)) > 300:
abort(401)
return '', 204Errors
| Статус | error | Meaning |
|---|---|---|
| 401 | unauthenticated / invalid_key | Missing, unknown, revoked, expired or disabled (partner suspended) key |
| 403 | insufficient_scope | Key lacks the scope for this endpoint |
| 404 | — | Unknown fighter, event or document number |
| 422 | invalid_* | Bad query parameter (message explains) |
| 429 | rate_limited | Per-key limit exceeded; see Retry-After |
The API is read-only. Nothing you send can create, edit or adjust points or verification statuses; those change only through KBN's admin workflow.
KBN TV API
JSON for KBN TV schedules and channels. Public endpoints need no key and are limited to 60 requests per minute per IP. Channel endpoints need a channel API key created by the channel's owner or a manager in Studio → API & webhooks; a channel key only ever sees its own channel (another channel's broadcast answers 404).
| Способ | Path | Auth / scope | Что |
|---|---|---|---|
| GET | /api/tv/live | public | Broadcasts live now |
| GET | /api/tv/upcoming | public | Scheduled broadcasts |
| GET | /api/tv/channels | public | Одобренные каналы |
| GET | /api/tv/channels/{slug} | public | One channel with live, upcoming and replays |
| GET | /api/tv/broadcasts/{slug} | public | One broadcast (followers-only and pay-per-view show locked: true) |
| GET | /api/tv/me | read | Your channel, billing state and broadcasts |
| POST | /api/tv/broadcasts | broadcasts:write | Create / schedule (title, source youtube|vimeo|hls|mp4|ingest, source_url, scheduled_at, visibility, discipline, language, tags). For ingest the response contains the server URL and stream key once. |
| PATCH | /api/tv/broadcasts/{id} | broadcasts:write | Edit any of the fields above |
| POST | /api/tv/broadcasts/{id}/go-live, /end | broadcasts:write | Go live (plan, billing and live slots permitting) / end (becomes a replay) |
| GET | /api/tv/broadcasts/{id}/health | read | Signal state, bitrate, resolution, fps, viewers and warnings |
curl -X POST "https://kbn.hbzgroup.co.uk/api/tv/broadcasts" -H "Authorization: Bearer kbn_…" -H "Content-Type: application/json" \
-d '{"title":"Fight Night 12","source":"ingest","scheduled_at":"2026-10-20T19:00:00Z","visibility":"public"}'
Channel webhooks
Events broadcast.scheduled, broadcast.live, broadcast.ended, replay.ready (and ping from the Test button) are POSTed to the channel's own https:// endpoints with the same headers and HMAC-SHA256 signatures as partner webhooks above (X-KBN-Signature, X-KBN-Signature-Timestamped). Failed deliveries are retried automatically; the studio shows the delivery log.
Stream keys, SRT passphrases and provider credentials are never returned by the API, except a new ingest broadcast's own key in its creation response. KBN TV never touches ranking points.