← Back to Home

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.

GET https://spro.agency/api/get_info?key=YOUR_TOKEN

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.

GET https://spro.agency/api/get_games?key=YOUR_TOKEN

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.

GET https://spro.agency/api/get_markets?key=YOUR_TOKEN&sports=YOUR_SPORTS&sportsbooks=YOUR_BOOKS

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:


GET https://spro.agency/api/get_markets?key=YOUR_TOKEN&sports=MLB,NHL,US Open (M)&sportsbooks=draftkings,betmgm

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

  1. Establish WebSocket connection using a valid API key.
  2. 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
  • 
    {'action': 'socket_connected'}
    
  • initial_state - After subscribing, you will be sent the state of all odds at that very moment in accordance to your subscription filters
  • 
    {
        "timestamp": "2025-07-23T19:45:03.048958+00:00",
        "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
            }
        }
    }
    
  • game_update - All odds for a specific game have been updated
  • 
    {
        "timestamp": "2025-07-23T19:46:10.023175+00:00",
        "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
            }
        }
    }
    
  • game_removed - Entire game removed (finished, suspended, etc)
  • 
    {
        "timestamp": "2025-07-23T19:55:09.062375+00:00",
        "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": {}
        }
    }
    
  • 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
  • 
    {
        "timestamp": "2025-07-23T19:46:10.023175+00:00",
        "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"
                }
            }
        }
    }
    
  • book_clear - All games for specified book have been cleared. This may happen in the case of connection drops on our backend
  • 
    {
        "timestamp": "2025-07-23T19:58:43.086382+00:00",
        "action": "book_clear",
        "data": {
            "sport": {},
            "sportsbook": "ballybet",
            "game": {},
            "home_team": {},
            "away_team": {},
            "info": {},
            "outcomes": {}
        }
    }
    
  • ping - Keep-alive message
  • 
    {"action": "ping"}
    

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