In-Game Predictions#
This feature is BETA
Quarter4's In-game predictions use the power of our AI to give predicted outcomes on a play-by-play level, for each available game. Be among the first to experience our In-game next generation prediction model and help us make it better. Check back here for changes as new features are added to the In-Game APIs.
Realtime APIs are available at the following endpoint:
https://api.quarter4.io/{sport}/v2/realtime/socket.io
where {sport}
is one of hockey
, basketball
, or american-football
.
The API uses the socket.io client libraries which use a WebSocket connection or, in the case a WebSocket connection is not possible, falls back to HTTP long-polling. It will also automatically try to reconnect if the connection is lost.
Message delay
Results coming into the realtime API are typically delayed about 8-15 seconds from live game play.
The Realtime APIs allow you to follow live prediction updates during broadcast games. Not all games are available live. Most professional level games are available and some NCAA level games. You can check using use the sport's Events
Data API to retrieve the information about an event and look for the coverage
attribute with a value of full
.
Supported Events
Only events with full
coverage will have live predictions available.
Expected Data Flow#
Your API connection is secured by an API key. This key is secret and must not be shared with external parties or users. Always check to be sure the code you write does not inadvertently expose your API key.
Danger
The API key is a secret key associated with your account. Do not share it or expose it in public web pages. Doing so would allow anyone to make queries as if they were you. If you think your API key may have been compromised, contact your Quarter4 Account representative immediately to disable your current key and receive a new key.
To load in-game data and distribute it to your users, you must:
- Connect the Quarter4 Push API on a server-to-server layer of your application,
- Listen for the incoming messages and process them as appropriate for your business logic,
- Send messages to your users using your own socket based push apis.
Here is a sequence diagram that outlines the general process:
This can also be illustrated by the following data flowchart where the message starts in the Quarter4 In-Game Push api but is then processed by your custom business logic and distributed to your users though yor own messaging service:
Quickstart#
To connect to the Quarter4 In-Game Push feed using socket.io, you'll need:
- Your API key.
- An Event UUID you'd like to connect to.
- An appropriate socket.io client library such as:
- JavaScript (which can be run either in Node.js or in React Native)
- Installation steps: https://socket.io/docs/v4/client-installation
- API: https://socket.io/docs/v4/client-api
- Source code: https://github.com/socketio/socket.io-client
- NOTE: Do not call the API directly from a front end website using JavaScript as it will expose your API key to the public.
- Java: https://github.com/socketio/socket.io-client-java
- C++: https://github.com/socketio/socket.io-client-cpp
- Swift: https://github.com/socketio/socket.io-client-swift
- Dart: https://github.com/rikulo/socket.io-client-dart
- Python: https://github.com/miguelgrinberg/python-socketio
- .Net: https://github.com/doghappy/socket.io-client-csharp
- Rust: https://github.com/1c3t3a/rust-socketio
- Kotlin: https://github.com/icerockdev/moko-socket-io
- JavaScript (which can be run either in Node.js or in React Native)
For example, using Node.js (server-to-server) you would do the following to make a new connection and join the feed for an event:
io = require("socket.io-client"),
const sport = 'basketball'
const apiKey = 'YOUR_API_KEY'
const eventUuid = 'EVENT_UUID'
const socket = io(server, {
path: "/" + sport + "/v2/realtime/socket.io",
auth: {
api_key: apiKey,
},
});
socket.on("connect", () => {
socket.emit(
"join",
eventUuid
(history) => {
console.log('history for ' + eventUuid, history);
}
);
});
socket.on("play", (item) => {
console.log("play", item, item.message);
});
socket.on("clock", (item) => {
console.log("clock", item, item.message);
});
socket.on("prediction", (item) => {
console.log("prediction", item, item.message);
});
// etc...
socket.connect();
Danger
You should never include your private API key in public source code. In a production environment you would connect the socket stream server side and then feed the results to your users though your own messaging layers. This includes web applications, native mobile applications or any situation where the API key could be intercepted by a third party.
In the above example, you connect to the socket at the endpoint and provide your API Key:
const socket = io(server, {
path: "/" + sport + "/v2/realtime/socket.io",
auth: {
api_key: apiKey,
},
});
Then, when the connection succeeds the connect
event will occur on the socket and you can then join
the event you'd like messages for:
socket.on("connect", () => {
socket.emit(
"join",
eventUuid
(history) => {
console.log('history for ' + eventUuid, history);
}
);
});
When emitting the join
message you can include a callback that will receive an object that contains the current message history for the event.
You then listen for the incoming play
messages and process the message accordingly:
socket.on("play", (item) => {
console.log("play", item, item.message);
});
Message Types#
There are several types of messages in the push feeds. These can be received though the socket by listening for the specific type, for example:
socket.on("play", (item) => {
console.log("play", item, item.message);
});
or by checking the messageType
attribute of the message:
socket.onAny((item) => {
console.log(item.messageType);
});
Warning
Additional message types may be added in the future. If you are using any sort fo catchall for socket messages, your code should check the message type and ignore those that it doesn't understand so that error do not occur when new message types are added.
The play
Message Type#
Supported Leagues: NBA NFL NHL NCAAM
Note
This message type was formerly message
. Both message
and play
will appear in the feed but are identical.
This message type describes the last play and includes a prediction of what to expect for the next play.
Example play
message
{
"uuid": "4dc3347c-9568-458c-a9a4-cd09f0bb12b7",
"sequence": 1675210264293,
"message": "Heat lineup change (Jimmy Butler, Tyler Herro, Caleb Martin, Kyle Lowry, Bam Adebayo). Predicting the Heat to score next (69.85% chance) for 2 points (63.01% chance).",
"prediction": {
"is_home_next_score": "Heat",
"home_next_score": 0.3015226721763611,
"away_next_score": 0.6984773278236389,
"free_throw_make": "No free throws.",
"free_throw_made": 0.3643263578414917,
"free_throw_miss": 0.6356736421585083,
"next_basket_3pt": 0.3699267506599426,
"next_basket_2pt": 0.6300732493400574,
"is_home_win": 0.5882874727249146,
"is_away_win": 0.41171252727508545,
"description": "Predicting the Heat to score next (69.85% chance) for 2 points (63.01% chance)."
},
"play": {
"description": "Heat lineup change (Jimmy Butler, Tyler Herro, Caleb Martin, Kyle Lowry, Bam Adebayo)"
},
"game": {
"id": "585ad812-108f-49ba-a3f8-b632f6f435f3",
"period": 1,
"period_type": "quarter",
"period_sequence": 1,
"clock": "12:00",
"home": {
"id": "bb980b2c-88df-442b-b2d2-97e73c301c1f",
"image": "https://avatar.api.quarter4.io/basketball/avatar/bb980b2c-88df-442b-b2d2-97e73c301c1f/256/logo.png",
"used_timeouts": null,
"remaining_timeouts": null,
"points": 0
},
"away": {
"id": "bb425691-8689-4adc-a9ef-323dc6d8c6c1",
"image": "https://avatar.api.quarter4.io/basketball/avatar/bb425691-8689-4adc-a9ef-323dc6d8c6c1/256/logo.png",
"used_timeouts": null,
"remaining_timeouts": null,
"points": 0
}
},
"messageType": "play"
}
The content of the prediction
attribute will vary by sport.
The clock
Message Type#
Supported Leagues: NBA NFL NHL NCAAM
This message type contains information about the current game clock.
Example clock
message
{
"game": {
"id": "585ad812-108f-49ba-a3f8-b632f6f435f3"
},
"event": {
"id": "585ad812-108f-49ba-a3f8-b632f6f435f3"
},
"clocks": {
"game": "1157",
"running": true,
"shot": "21.00"
},
"period": {
"sequence": 1,
"number": 1,
"type": "REG"
},
"messageType": "clock"
}
The prediction
Message Type#
Supported Leagues: NBA, NFL, NHL
This message type contains end-of-game team and player predictions, along with the current in-game stats. The messages are sent periodically throughout the game at natural breaks (commercial, timeouts, fouls, etc.). The predicted values will update throughout the game based on the players performance in the game.
Example pediction
message for NBA
{
"uuid": "292c8823-7f65-4821-bf40-e9bd70227412",
"players": [
{
"fullName": "Caleb Martin",
"uniform": "16",
"uuid": "1a4e4949-ec7a-4495-8e4b-b03c04dcd34d",
"avatarUrl": "https://avatar.api.quarter4.io/basketball/avatar/bb425691-8689-4adc-a9ef-323dc6d8c6c1/256/uniform/16.png",
"teamName": "Miami Heat",
"position": "SF",
"props": {
"period": 1,
"position": 0,
"points": 0,
"rebounds": 0,
"assists": 0,
"blocks": 0,
"steals": 0,
"turnovers": 0,
"freeThrowsMade": 0,
"freeThrowsAtt": 0,
"fieldGoalsMade": 0,
"fieldGoalsAtt": 0,
"threePointsMade": 0,
"threePointsAtt": 0,
"offensiveRebounds": 0,
"defensiveRebounds": 0,
"personalFouls": 0
},
"predictions": {
"fieldGoalsMadeTotal": 2.729,
"fieldGoalsAttTotal": 6.299,
"freeThrowsMadeTotal": 0.982,
"freeThrowsAttTotal": 0.863,
"threePointsMadeTotal": 1.06,
"threePointsAttTotal": 3.175,
"offensiveReboundsTotal": 0.895,
"defensiveReboundsTotal": 2.61,
"stealsTotal": 0.99,
"blocksTotal": 0.223,
"turnoversTotal": 1.106,
"personalFoulsTotal": 1.883,
"assistsTotal": 0.651,
"reboundsTotal": 3.505,
"pointsTotal": 7.501
}
},
// etc. for each active player in the depth chart...
],
"event": {
"uuid": "585ad812-108f-49ba-a3f8-b632f6f435f3",
"period": 1,
"period_type": "quarter",
"period_sequence": 1,
"clock": "12:00",
"props": {
"overUnder": 223.514,
"homeWinLoss": 0.521314,
"awayWinLoss": 0.478686,
"homeSpread": -1,
"awaySpread": 1,
"winLossSpreadConsistency": 1,
"updatedAt": "2023-01-31T23:43:54",
"probFirstHalfWinHome": 0.483333,
"probFirstHalfWinAway": 0.516667,
"probFirstTo10PointsHome": 0.340476,
"probFirstTo10PointsAway": 0.659524,
"probFirstTo20PointsHome": 0.514286,
"probFirstTo20PointsAway": 0.485714,
"probFirstTo30PointsHome": 0.511905,
"probFirstTo30PointsAway": 0.488095,
"probFirstToEarn3ptHome": 0.545238,
"probFirstToEarn3ptAway": 0.454762,
"probFirstToScoreHome": 0.547619,
"probFirstToScoreAway": 0.452381,
"overtimeProbability": 0.0357143,
"predictAsOvertime": 0,
"homePoints": 0,
"awayPoints": 0,
"period": 1,
"totalScore": 223.514
}
},
"teams": {
"home": {
"location": "Cleveland",
"nickName": "Cavaliers",
"abbreviation": "cle",
"uuid": "bb980b2c-88df-442b-b2d2-97e73c301c1f"
},
"away": {
"location": "Miami",
"nickName": "Heat",
"abbreviation": "mia",
"uuid": "bb425691-8689-4adc-a9ef-323dc6d8c6c1"
}
},
"messageType": "prediction"
}
The status
Message Type#
This message type contains the current game status (when it changes). For example when a game switches from inprogress
to halftime
.
Example status
message
{
"uuid": "585ad812-108f-49ba-a3f8-b632f6f435f3",
"status": "inprogress",
"messageType": "status"
}
Message Type: injury
#
This message type indicates an in-game injury has ocurred and the player wil not be returning to the game. New predictions after this will take the injury into account.
Example injury
message
{
"game": "5b21507c-3297-4a55-998b-3532807078f0",
"player": {
"fullName": "Mo Bamba",
"uniform": "11",
"uuid": "a0db8dd2-afdd-4f29-8e19-ebeccb670071",
"primaryPosition": "C",
"avatarUrl": "...url..."
},
"team": {
"location": "Orlando",
"nickName": "Magic",
"abbreviation": "orl",
"uuid": "d4c5d92a-43dd-40f9-96cc-c23f68733736",
"avatarUrl": "...url..."
},
"messageType": "injury"
}
Message Type: swing
#
This message type indicates an in-game prediction swing from one team to another. For example if Team A was expected to win but the homeWinLoss
and awayWinLoss
predictions now indicates Team B will win.
This message is provided for convenience will be preceded by a prediction
message with the same event
and team
information.
Example swing
message
{
"event": {
"uuid": "585ad812-108f-49ba-a3f8-b632f6f435f3",
"period": 1,
"period_type": "quarter",
"period_sequence": 1,
"clock": "12:00",
"props": {
"overUnder": 223.514,
"homeWinLoss": 0.521314,
"awayWinLoss": 0.478686,
"homeSpread": -1,
"awaySpread": 1,
"winLossSpreadConsistency": 1,
"updatedAt": "2023-01-31T23:43:54",
"probFirstHalfWinHome": 0.483333,
"probFirstHalfWinAway": 0.516667,
"probFirstTo10PointsHome": 0.340476,
"probFirstTo10PointsAway": 0.659524,
"probFirstTo20PointsHome": 0.514286,
"probFirstTo20PointsAway": 0.485714,
"probFirstTo30PointsHome": 0.511905,
"probFirstTo30PointsAway": 0.488095,
"probFirstToEarn3ptHome": 0.545238,
"probFirstToEarn3ptAway": 0.454762,
"probFirstToScoreHome": 0.547619,
"probFirstToScoreAway": 0.452381,
"overtimeProbability": 0.0357143,
"predictAsOvertime": 0,
"homePoints": 0,
"awayPoints": 0,
"period": 1,
"totalScore": 223.514
}
},
"teams": {
"home": {
"location": "Cleveland",
"nickName": "Cavaliers",
"abbreviation": "cle",
"uuid": "bb980b2c-88df-442b-b2d2-97e73c301c1f"
},
"away": {
"location": "Miami",
"nickName": "Heat",
"abbreviation": "mia",
"uuid": "bb425691-8689-4adc-a9ef-323dc6d8c6c1"
}
},
"messageType": "swing"
}