UFC Live Data API
The UFC Live Data API is a real-time fight-night data feed from Cito API, documented on ufcapi.dev. It pushes round, clock, status, and liveStats (sigStrikes, takedowns) over websocket so apps update as the fight does—without multi-second REST polling.
const key = process.env.CITO_API_KEY;
const ws = new WebSocket(
`wss://api.citoapi.com/api/v1/ufc/live/ws?api_key=${encodeURIComponent(key)}`
);
ws.onopen = () => console.log("connected");
ws.onmessage = (ev) => {
const msg = JSON.parse(ev.data);
// Server hello — then subscribe to bout and/or event rooms
if (msg.type === "ready") {
ws.send(JSON.stringify({
action: "subscribe",
rooms: [
"bout:ufc-12938",
"event:ufc-fight-night-example"
]
}));
return;
}
if (msg.type === "subscribed") {
console.log("rooms", msg.rooms);
return;
}
// Live fight frame
if (msg.type === "ufc.live.update") {
const { currentRound, currentTime, liveStats, status } = msg.data;
console.log(msg.room, status, "R" + currentRound, currentTime, liveStats);
}
};
ws.onerror = (err) => console.error("ws error", err);
ws.onclose = () => console.log("closed — reconnect with backoff");{
"type": "ufc.live.update",
"room": "bout:ufc-12938",
"data": {
"boutId": "ufc-12938",
"eventSlug": "ufc-fight-night-example",
"status": "live",
"currentRound": 2,
"currentTime": "3:12",
"red": { "fighterName": "Islam Makhachev", "corner": "red" },
"blue": { "fighterName": "Arman Tsarukyan", "corner": "blue" },
"liveStats": {
"red": { "sigStrikes": 42, "takedowns": 1 },
"blue": { "sigStrikes": 31, "takedowns": 0 }
},
"lagSeconds": 2,
"source": "ufc_fightmetric_cdn"
}
}Websocket vs polling
REST polling
You only see new state when you request it. A 2-5s poll adds lag. Fine for archives, weak for fight night UIs.
Websocket push
Server pushes frames when they land. Your frontend updates as soon as Cito has the frame.
What data updates in real time?
- currentRound — active round number as the bout progresses
- currentTime — clock remaining in the round (null when empty)
- status — live / watching / final and related bout state
- liveStats.sigStrikes — significant strike totals per corner when present
- liveStats.takedowns — takedown counts per corner when present
- red / blue.fighterName — corner identity on each ufc.live.update frame
- boutId + room — bout:{id} and event:{slug} subscribe targets
- lagSeconds + source — lag metadata and fight-night source tag
What data is returned
typeFrame type: ready, subscribed, ufc.live.update, ping, or error
roomSubscribed room id — bout:{boutId} or event:{eventSlug}
boutIdBout identity for subscribe filters and REST catch-up
eventSlugEvent slug when the frame is scoped to a card
statusBout state: live, watching, final, and related values
currentRoundActive round number during the bout
currentTimeClock remaining in the round (null when empty)
red / blueCorner objects with fighterName (and corner when present)
liveStatsPer-corner totals when present (sigStrikes, takedowns, …)
liveStats.*.sigStrikesSignificant strike count for that corner
liveStats.*.takedownsTakedown count for that corner
lagSecondsObserved lag from source clock to Cito frame
sourceFight-night source tag (e.g. ufc_fightmetric_cdn)
When to use this
- Build a UFC live data API consumer for fight-night scoreboards and broadcast overlays
- Push currentRound, currentTime, and strike/takedown liveStats the moment a frame lands
- Power fantasy, engagement, or second-screen apps that react to momentum mid-bout
- Replace multi-second REST polling when lag would show behind the broadcast
- Subscribe to event:{slug} for multi-bout boards or bout:{id} for a single fight UI
Full reference docs citoapi.com/docs/api/ufc/live/websocket