Welcome to the complete guide for integrating and using BoltOdds. This documentation will help you get up and running quickly so you can access real-time sports betting odds.
Quick Start
Get connected in just a few steps:
Step 1: Obtain Your API Key
First, sign up for an account and retrieve your API key. This should be sent to the email you signed up with. You'll need this for authentication. If you dont see it in your inbox, please check your spam/junk folder.
Step 2: Establish Connection
async def run_client():
uri = "wss://spro.agency/api?key=YOUR_TOKEN"
while True:
try:
async with websockets.connect(uri) as websocket:
ack_message = await websocket.recv()
print(ack_message)
Step 3: Send Subscription Data
To see all the sports and sportsbooks you could subscribe to, see the GET request below. NOTE: sports and sportsbooks MUST be subscribed to in the exact format seen in the response from the GET request below. Any deviation will be ignored. If either of these subscription filters are left out, your subscription will default to all available options, in this case all sports and sportsbooks.
To see all the games you could subscribe to, see the GET request below. NOTE: games MUST be subscribed to in the exact format seen in the response from the GET request below. Any deviation will be ignored. If this subscription filter is left out, your subscription will default to all available options, in this case all games.
To see all the markets you could subscribe to, see the GET request below. NOTE: markets MUST be subscribed to in the exact format seen in the response from the GET request below. Any deviation will be ignored. If this subscription filter is left out, your subscription will default to all available options, in this case all markets.
You have the option to add query paramaters to this request, specifically "sportsbooks" and "sports". This allows you to specify a certain book, sport, or a combo of each where you want to see the markets available. If your request does not specify these parameters, it will return all markets for all sportsbooks and all sports. YOUR_SPORTS can be a comma-seperated string if you want to get the markets for multiple sports in one request. The same goes for YOUR_BOOKS. For example:
If you dont want to filter for a certain category, just dont include its key in your subscription filters. For example, if you dont want to specify a game(s) to sub to, dont include the 'games' key in the subscription filters.
subscribe_message = {
"action": "subscribe",
"filters": {
"sports": ["NBA", "MLB", "Wimbledon (M)"],
"sportsbooks": ["draftkings", "betmgm"],
"games":["San Francisco Giants vs Philadelphia Phillies, 2025-07-07, 09", "Corinthians vs Bragantino, 2025-07-13, 06"],
"markets":["Moneyline", "Spread"]
}
}
await websocket.send(json.dumps(subscribe_message))
# You are now connected and subscribed to your specified stream of data
while True:
message = await websocket.recv()
Authentication
All WebSocket connections must be authenticated using your API key.
Authentication Flow
- Establish WebSocket connection using a valid API key.
- Once ack_message is received, you have been validated
Connection Management
Automatic Reconnection
async def run_client():
uri = "wss://spro.agency/api?key=YOUR_TOKEN"
while True:
try:
async with websockets.connect(uri) as websocket:
ack_message = await websocket.recv()
print(ack_message)
subscribe_message = {
"action": "subscribe",
"filters": {
"sports": ["NBA", "MLB", "Wimbledon (M)"],
"sportsbooks": ["draftkings", "betmgm"],
"games":["San Francisco Giants vs Philadelphia Phillies, 2025-07-07, 09", "Corinthians vs Bragantino, 2025-07-13, 06"],
"markets":["Moneyline", "Spread"]
}
}
await websocket.send(json.dumps(subscribe_message))
# You are now connected and subscribed to your specified stream of data
while True:
message = await websocket.recv()
except websockets.ConnectionClosed as e:
print("Connection closed — reason:", e)
print('Reconnecting...')
await asyncio.sleep(5)
continue
#can add other exception handling as needed
Message Format
All messages sent through the WebSocket are JSON formatted and follow a consistent structure.
Message Types
The foundation of all messages is an action which will tell you what the message entails. These are the actions BoltOdds sends which you could expect to receive. Please note that message format can be changed or modified in the future.
- socket_connected - Initial authentication successfull
- initial_state - After subscribing, you will be sent the state of all odds at that very moment in accordance to your subscription filters
- game_update - All odds for a specific game have been updated
- game_removed - Entire game removed (finished, suspended, etc)
- line_update - A line update. If odds provided are None or '', it means there are no odds available for this line i.e its currently suspended, deleted etc
- book_clear - All games for specified book have been cleared. This may happen in the case of connection drops on our backend
- ping - Keep-alive message
- error - Something went wrong. Message will be accompanied with a description of the issue.
- subscription_updated - Ack message that your subscription filters were succesfully changed.
{'action': 'socket_connected'}
{
"timestamp": "2025-07-23T19:45:03.048958+00:00", #UTC
"action": "initial_state",
"data": {
"sport": "MLB",
"sportsbook": "ballybet",
"game": "New York Mets vs Los Angeles Angels, 2025-07-23, 01",
"home_team": "New York Mets",
"away_team": "Los Angeles Angels",
"info": {
"id": 1022036664,
"game": "New York Mets vs Los Angeles Angels, 2025-07-23, 01",
"when": "2025-07-23, 01:09 PM",
"link": "https://play.ballybet.ca/sports#event/1022036664"
},
"outcomes": {
"New York Mets Moneyline": {
"odds": "-10000",
"link": "https://play.ballybet.ca/sports#event/1022036664?coupon=pickType|3815533797|wagerAmounts|replace", #deep link
"outcome_name": "Moneyline",
"outcome_line": null,
"outcome_over_under": null,
"outcome_target": "New York Mets"
},
"Los Angeles Angels Moneyline": {
"odds": "+3300",
"link": "https://play.ballybet.ca/sports#event/1022036664?coupon=pickType|3815533801|wagerAmounts|replace", #deep link
"outcome_name": "Moneyline",
"outcome_line": null,
"outcome_over_under": null,
"outcome_target": "Los Angeles Angels"
},
... #all other lines offered
}
}
}
{
"timestamp": "2025-07-23T19:46:10.023175+00:00", #UTC
"action": "game_update",
"data": {
"sport": "MLB",
"sportsbook": "ballybet",
"game": "New York Mets vs Los Angeles Angels, 2025-07-23, 01",
"home_team": "New York Mets",
"away_team": "Los Angeles Angels",
"info": {
"id": 1022036664,
"game": "New York Mets vs Los Angeles Angels, 2025-07-23, 01",
"when": "2025-07-23, 01:09 PM",
"link": "https://play.ballybet.ca/sports#event/1022036664"
},
"outcomes": {
"New York Mets Moneyline": {
"odds": "-3333",
"link": "https://play.ballybet.ca/sports#event/1022036664?coupon=pickType|3815533797|wagerAmounts|replace", #deep link
"outcome_name": "Moneyline",
"outcome_line": null,
"outcome_over_under": null,
"outcome_target": "New York Mets"
},
"Los Angeles Angels Moneyline": {
"odds": "+900",
"link": "https://play.ballybet.ca/sports#event/1022036664?coupon=pickType|3815533801|wagerAmounts|replace", #deep link
"outcome_name": "Moneyline",
"outcome_line": null,
"outcome_over_under": null,
"outcome_target": "Los Angeles Angels"
},
... #all other lines offered
}
}
}
{
"timestamp": "2025-07-23T19:55:09.062375+00:00", #UTC
"action": "game_removed",
"data": {
"sport": "MLB",
"sportsbook": "ballybet",
"game": "New York Mets vs Los Angeles Angels, 2025-07-23, 01",
"home_team": "New York Mets",
"away_team": "Los Angeles Angels",
"info": {
"id": 1022036664,
"game": "New York Mets vs Los Angeles Angels, 2025-07-23, 01",
"when": "2025-07-23, 01:09 PM",
"link": "https://play.ballybet.ca/sports#event/1022036664"
},
"outcomes": {}
}
}
{
"timestamp": "2025-07-23T19:46:10.023175+00:00", #UTC
"action": "line_update",
"data": {
"sport": "MLB",
"sportsbook": "ballybet",
"game": "New York Mets vs Los Angeles Angels, 2025-07-23, 01",
"home_team": "New York Mets",
"away_team": "Los Angeles Angels",
"info": {
"id": 1022036664,
"game": "New York Mets vs Los Angeles Angels, 2025-07-23, 01",
"when": "2025-07-23, 01:09 PM",
"link": "https://play.ballybet.ca/sports#event/1022036664"
},
"outcomes": {
"New York Mets Moneyline": {
"odds": "-1250", #if line is suspended, closed, unavailable, this will be None or ''
"link": "https://play.ballybet.ca/sports#event/1022036664?coupon=pickType|3815533797|wagerAmounts|replace", #deep link
"outcome_name": "Moneyline",
"outcome_line": null,
"outcome_over_under": null,
"outcome_target": "New York Mets"
}
}
}
}
{
"timestamp": "2025-07-23T19:58:43.086382+00:00", #UTC
"action": "book_clear",
"data": {
"sport": {},
"sportsbook": "ballybet",
"game": {},
"home_team": {},
"away_team": {},
"info": {},
"outcomes": {}
}
}
{"action": "ping"}
{"action": "error", "message": "Re-subscription rate limit hit. Please wait 5 seconds before re-subscribing."}
{"action": "subscription_updated", "message": "You have succesfully updated your subscription filters."}
Resubscribing
If you want to update your subscription filters without terminating your connection, simply send a subscription message in the same format you used when you initially subscribed. Your subscription filters will be updated, and you will start receiving data according to your new filters.
Endpoints
These endpoints provide access to additional odds-related data beyond your subscription filters. Use them to retrieve specialized information such as parlays, boosts, or sportsbook-specific offerings. Note: Access to these endpoints may be limited based on your plan
GET /api/get_parlays
This endpoint returns parlay betting data for a specific sportsbook.
You can use the /get_info endpoint (described above) to retrieve a list of supported sportsbooks and view the correct formatting for the sportsbooks parameter.
This endpoint will return a json object with parlay data for the specified sportsbook if its a sportsbook we are collecting parlay data for.
[
{
"odds":"+1746",
"total_bets":3238,
"type":"SGP_PLUS",
"legs":{
"Auston Matthews Over 0.5 Goals":{
"game":"Toronto Maple Leafs vs New York Rangers, 2025-10-16, 07",
"sport":"NHL",
"universal_id":"ac792a4acb18",
"outcome_name":"Goals",
"outcome_line":"0.5",
"outcome_over_under":"Over",
"outcome_target":"Auston Matthews"
},
"Toronto Maple Leafs Moneyline":{
"game":"Toronto Maple Leafs vs New York Rangers, 2025-10-16, 07",
"sport":"NHL",
"universal_id":"ac792a4acb18",
"outcome_name":"Moneyline",
"outcome_line":null,
"outcome_over_under":null,
"outcome_target":"Toronto Maple Leafs"
},
"Toronto Blue Jays Moneyline":{
"game":"Seattle Mariners vs Toronto Blue Jays, 2025-10-16, 08",
"sport":"MLB",
"universal_id":"762ce180a3ef",
"outcome_name":"Moneyline",
"outcome_line":null,
"outcome_over_under":null,
"outcome_target":"Toronto Blue Jays"
},
"Vladimir Guerrero Jr. Over 0.5 Home Runs":{
"game":"Seattle Mariners vs Toronto Blue Jays, 2025-10-16, 08",
"sport":"MLB",
"universal_id":"762ce180a3ef",
"outcome_name":"Home Runs",
"outcome_line":"0.5",
"outcome_over_under":"Over",
"outcome_target":"Vladimir Guerrero Jr."
}
}
}
...
]
Rate Limits
Rate limits are subject to change. If rate limits are exceed, your request will be rejected.
WebSocket Connections
WebSocket connections are limited to 12 per minute per IP address.
GET Endpoints
Requests are limited to 12 per minute per IP address. You can make up to 3 requests in quick succession before the rate limit is enforced.
Play by Play
The Play by Play feed provides real-time updates for live games, including scoring events, turnovers, fouls, and other key moments. This allows you to track games as they happen and build live dashboards, alerts, or analytics tools.
How To Use
In order to connect and start receiving live in-game updates from BoltOdds Play by Play, you need to establish a connection to this endpoint:
This feature is available to Trial, Pro, and Custom users.
Once you receive a connection_ack message from BoltOdds, you can send a subscription message containing the games you want to receive updates for.
subscribe_message =
{
"action": "subscribe",
"filters":
{
"games":["Houston Texans vs Jacksonville Jaguars, 2025-11-09, 01",
"New York Jets vs Cleveland Browns, 2025-11-09, 01"
],
}
}
To view all available games and confirm the correct naming format, make a GET request to the following endpoint:
Only live games will return play by play updates.
Basic Example
Here's a complete basic example of connecting to and using BoltOdds:
import asyncio
import websockets
import json
async def run_client():
uri = "wss://spro.agency/api?key=YOUR_TOKEN"
while True:
try:
async with websockets.connect(uri) as websocket:
ack_message = await websocket.recv()
print(ack_message)
# Send the subscription message
subscribe_message = {
"action": "subscribe",
"filters": {
"sports": ["NBA", "MLB", "Wimbledon (M)"],
"sportsbooks": ["draftkings", "betmgm"],
"games":["San Francisco Giants vs Philadelphia Phillies, 2025-07-07, 09", "Corinthians vs Bragantino, 2025-07-13, 06"],
"markets":["Moneyline", "Spread"]
}
}
await websocket.send(json.dumps(subscribe_message))
# Listen for incoming messages
while True:
message = await websocket.recv()
data = json.loads(message)
if data['action'] == 'ping':
continue
#sent upon connection, initial state of odds subbed to
if data['action'] == 'initial_state':
...
#entire game odds update
elif data['action'] == 'game_update':
...
#games done
elif data['action'] == 'game_removed':
...
#game added
elif data['action'] == 'game_added':
...
#singular line odd update
#if odds r None or '', no odds available for line i.e its deleted, suspended etc
elif data['action'] == 'line_update':
...
#all games for this book have been cleared
elif data['action'] == 'book_clear':
...
except websockets.ConnectionClosed as e:
print("Connection closed — reason:", e)
print('Reconnecting...')
await asyncio.sleep(5)
continue
if __name__ == "__main__":
asyncio.run(run_client())
Troubleshooting
Common issues and their solutions:
Connection Fails
- Verify your API key is correct
- Check that you're using the correct WebSocket URL
- Ensure your firewall allows WebSocket connections
- Ensure you're sending subscription message soon after connecting
Authentication Errors
- Double-check your API key format
Support
Need help? We're here for you:
- Email: support@boltodds.com
- Documentation: Available 24/7 online