Prediction markets are emerging as a legitimate alternative to traditional sportsbooks for betting on NBA games. Platforms like Kalshi and Polymarket allow you to buy and sell contracts based on game outcomes, often with different pricing than what you'd find at DraftKings or FanDuel.

This tutorial explains how prediction markets work, shows you how to convert prediction market prices to American odds, and walks through building a Python tool that compares odds across both market types using the BALLDONTLIE API.

If you haven't set up your API key yet, see our Getting Started guide.

What Are Prediction Markets?

Prediction markets are exchanges where participants buy and sell contracts based on the outcome of future events. Unlike traditional sportsbooks where you bet against the house, prediction markets are peer-to-peer: you're trading contracts with other market participants.

How Prediction Market Contracts Work

On platforms like Kalshi and Polymarket, contracts are priced between $0.00 and $1.00. The price directly represents the market's implied probability of that outcome occurring.

For example, if you see a contract for "Lakers to beat Celtics" priced at $0.65:

  • The market implies a 65% probability the Lakers win
  • You pay $0.65 to buy one contract
  • If the Lakers win, your contract pays out $1.00 (profit of $0.35)
  • If the Lakers lose, your contract is worth $0.00 (loss of $0.65)

This is fundamentally different from traditional sportsbooks where odds are presented in American format (-150, +200, etc.) and include the house's built-in margin (vig or juice).

Key Prediction Market Platforms

Kalshi is a CFTC-regulated prediction market based in the United States. It offers contracts on sports, politics, economics, and other events. Because it's federally regulated, Kalshi is legal in most US states where sports betting might otherwise be restricted.

Polymarket is a crypto-based prediction market that operates globally. Users trade with USDC (a stablecoin pegged to the US dollar) and can access markets on a wide range of events. Polymarket gained significant attention during recent election cycles for its accurate polling predictions.

Prediction Markets vs Traditional Sportsbooks

AspectPrediction MarketsTraditional Sportsbooks
Pricing$0.00 - $1.00 (probability)American odds (-150, +200)
CounterpartyOther traders (peer-to-peer)The sportsbook (house)
Vig/JuiceSpread between buy/sell pricesBuilt into the odds
RegulationCFTC (Kalshi), Crypto (Polymarket)State gaming commissions
AvailabilityBroader (Kalshi legal in 40+ states)Limited to legal betting states

Converting Prediction Market Prices to American Odds

To compare prediction market contracts with sportsbook lines, you need to convert between probability and American odds formats.

The Conversion Formulas

For favorites (probability >= 50%):

American odds = -(100 × probability) / (1 - probability)

For underdogs (probability < 50%):

American odds = 100 × (1 - probability) / probability

Conversion Examples

Contract PriceProbabilityAmerican OddsInterpretation
$0.6060%-150Risk $150 to win $100
$0.4040%+150Risk $100 to win $150
$0.7575%-300Risk $300 to win $100
$0.2525%+300Risk $100 to win $300
$0.5252%-108Slight favorite
$0.4848%+108Slight underdog

Let's verify the math for a 60% probability:

American odds = -(100 × 0.60) / (1 - 0.60)
             = -60 / 0.40
             = -150

And for 40% probability:

American odds = 100 × (1 - 0.40) / 0.40
             = 100 × 0.60 / 0.40
             = 60 / 0.40
             = +150

The Reverse: American Odds to Probability

To convert American odds back to implied probability:

For negative odds (favorites):

Probability = |odds| / (|odds| + 100)

For positive odds (underdogs):

Probability = 100 / (odds + 100)

Building a Prediction Market Comparison Tool

Now let's build a Python script that fetches NBA odds from both prediction markets and traditional sportsbooks, then compares them side by side.

Prerequisites

Step 1: Project Setup

Create a new directory and install dependencies:

mkdir nba-prediction-markets
cd nba-prediction-markets
pip install requests tabulate

Step 2: Core Conversion Functions

First, let's implement the conversion functions:

def probability_to_american(probability: float) -> int:
    """
    Convert a probability (0-1) to American odds.

    Examples:
        0.60 -> -150
        0.40 -> +150
        0.75 -> -300
    """
    if probability >= 0.5:
        # Favorite: negative odds
        return round((-100 * probability) / (1 - probability))
    else:
        # Underdog: positive odds
        return round((100 * (1 - probability)) / probability)


def american_to_probability(odds: int) -> float:
    """
    Convert American odds to implied probability.

    Examples:
        -150 -> 0.60
        +150 -> 0.40
        -300 -> 0.75
    """
    if odds < 0:
        return abs(odds) / (abs(odds) + 100)
    else:
        return 100 / (odds + 100)

Step 3: Fetch NBA Games and Odds

The BALLDONTLIE API returns odds from both prediction markets and sportsbooks in the same endpoint:

import requests
from datetime import date

API_KEY = "your-api-key"
BASE_URL = "https://api.balldontlie.io"

def get_headers():
    return {"Authorization": API_KEY}

def fetch_games(target_date: str) -> list:
    """Fetch NBA games for a specific date."""
    response = requests.get(
        f"{BASE_URL}/nba/v1/games",
        headers=get_headers(),
        params={"dates[]": target_date}
    )
    response.raise_for_status()
    return response.json()["data"]

def fetch_odds(target_date: str) -> list:
    """Fetch betting odds for a specific date."""
    response = requests.get(
        f"{BASE_URL}/nba/v2/odds",
        headers=get_headers(),
        params={"dates[]": target_date}
    )
    response.raise_for_status()
    return response.json()["data"]

Step 4: Separate Prediction Markets from Sportsbooks

The API returns odds with a vendor field. We can use this to categorize the data:

PREDICTION_MARKETS = ["kalshi", "polymarket"]
SPORTSBOOKS = ["draftkings", "fanduel", "caesars", "betmgm", "betrivers"]

# Fetch odds
odds = fetch_odds(date.today().isoformat())

# Separate by type
pm_odds = [o for o in odds if o["vendor"] in PREDICTION_MARKETS]
sb_odds = [o for o in odds if o["vendor"] in SPORTSBOOKS]

Step 5: Compare and Display Results

Here's the complete comparison logic:

from tabulate import tabulate
from typing import Optional

def format_odds(odds: Optional[int]) -> str:
    """Format American odds with +/- prefix."""
    if odds is None:
        return "-"
    return f"+{odds}" if odds >= 0 else str(odds)

def compare_markets_for_game(game: dict, odds_list: list):
    """Compare prediction markets vs sportsbooks for a game."""
    home = game["home_team"]["abbreviation"]
    away = game["visitor_team"]["abbreviation"]

    # Separate by market type
    pm_odds = [o for o in odds_list if o["vendor"] in PREDICTION_MARKETS]
    sb_odds = [o for o in odds_list if o["vendor"] in SPORTSBOOKS]

    print(f"\n{game['visitor_team']['full_name']} @ {game['home_team']['full_name']}")
    print("=" * 60)

    # Display prediction markets
    print("\nPREDICTION MARKETS")
    print("-" * 40)

    headers = ["Platform", f"{home} ML", "Implied %", f"{away} ML", "Implied %"]
    rows = []

    for odds in pm_odds:
        home_odds = odds.get("moneyline_home_odds")
        away_odds = odds.get("moneyline_away_odds")
        rows.append([
            odds["vendor"].upper(),
            format_odds(home_odds),
            f"{american_to_probability(home_odds) * 100:.1f}%" if home_odds else "-",
            format_odds(away_odds),
            f"{american_to_probability(away_odds) * 100:.1f}%" if away_odds else "-"
        ])

    print(tabulate(rows, headers=headers, tablefmt="simple"))

    # Display sportsbooks
    print("\nTRADITIONAL SPORTSBOOKS")
    print("-" * 40)

    rows = []
    for odds in sb_odds:
        home_odds = odds.get("moneyline_home_odds")
        away_odds = odds.get("moneyline_away_odds")
        rows.append([
            odds["vendor"].upper(),
            format_odds(home_odds),
            f"{american_to_probability(home_odds) * 100:.1f}%" if home_odds else "-",
            format_odds(away_odds),
            f"{american_to_probability(away_odds) * 100:.1f}%" if away_odds else "-"
        ])

    print(tabulate(rows, headers=headers, tablefmt="simple"))

Example Output

Running this script produces output like:

Philadelphia 76ers @ Memphis Grizzlies
============================================================

PREDICTION MARKETS
----------------------------------------
Platform      MEM ML  Implied %      PHI ML  Implied %
----------  --------  -----------  --------  -----------
POLYMARKET      +102  49.5%            -102  50.5%
KALSHI          +102  49.5%            -102  50.5%

TRADITIONAL SPORTSBOOKS
----------------------------------------
Sportsbook      MEM ML  Implied %      PHI ML  Implied %
------------  --------  -----------  --------  -----------
FANDUEL           -104  51.0%            -112  52.8%
DRAFTKINGS        -105  51.2%            -115  53.5%
CAESARS           -105  51.2%            -115  53.5%
BETRIVERS         -103  50.7%            -120  54.5%

COMPARISON: BEST AVAILABLE ODDS
----------------------------------------
Team    Best Sportsbook      Kalshi    Polymarket  Prob. Diff
------  -----------------  --------  ------------  ------------
MEM     -103 (betrivers)       +102          +102  +1.2%
PHI     -112 (fanduel)         -102          -102  +2.3%

Finding Value: Sportsbooks vs Prediction Markets

The comparison reveals interesting discrepancies. In the example above:

  • Sportsbooks have Memphis as a slight favorite (-103 to -105)
  • Prediction markets have it as a coin flip (+102 for Memphis)

This 1-2% probability difference represents potential value. Sportsbooks include vig in their lines, while prediction markets reflect pure market sentiment.

Why Prediction Markets Sometimes Differ

  1. Different liquidity pools: Prediction markets may have less volume, leading to less efficient pricing
  2. Different participant bases: Sportsbooks attract professional bettors; prediction markets attract a broader audience
  3. No vig structure: Prediction markets don't need to guarantee a profit margin on every market
  4. Regulatory arbitrage: Prediction markets may be legal where sportsbooks aren't, attracting different bettors

Extending This Further

Here are ideas to build on this foundation:

  • Arbitrage alerts: Notify when prediction market prices diverge significantly from sportsbook lines
  • Historical tracking: Store snapshots to analyze how markets converge before game time
  • Multi-sport comparison: The API includes NFL, NHL, and EPL prediction market odds as well
  • Automated trading signals: Build a system that identifies when prediction markets lag sportsbook moves

Check out our AI-assisted development guide to quickly generate these extensions using ChatGPT or Claude.

API Endpoints Used

This tutorial uses the BALLDONTLIE betting odds endpoint:

  • GET /nba/v2/odds: Returns odds from both prediction markets and sportsbooks
    • Vendors include: kalshi, polymarket, draftkings, fanduel, caesars, betmgm, betrivers, and more
    • Filter by date with dates[] parameter
    • Returns moneylines, spreads, and totals

The same endpoint structure is available for NFL, NHL, EPL, MLB, WNBA, NCAAF, and NCAAB.

Important Notes

The BALLDONTLIE API provides current odds only. We do not store historical betting data. This means the data is ideal for:

  • Live comparison between prediction markets and sportsbooks
  • Real-time monitoring of price discrepancies
  • Building alerts when markets diverge

It is not suitable for historical backtesting of trading strategies.

Next Steps

Need help? Join our Discord community or browse the full documentation.

Ready to compare prediction markets with sportsbooks? Sign up for a free API key and start building.