Sports Betting API in Python
A complete tutorial, from installation to fetching live NBA odds to streaming line changes as they happen. All examples use the SharpAPI Python SDK and work on the free tier (12 req/min, no card required).
To get real-time sports betting odds in Python, install the SharpAPI SDK with pip install sharpapi, authenticate with an API key, and call client.odds.list(sport="basketball_nba"). You get structured JSON with moneylines, spreads, totals, and player props from 45+ sportsbooks. For real-time updates, use SSE streaming via client.stream.odds(): delta updates arrive the instant lines move.
1. Installation
Install the SDK and python-dotenv for env var management:
pip install sharpapi python-dotenvCreate a .env file with your API key:
SHARPAPI_KEY=your_api_key_hereGet your free API key at sharpapi.io/sign-up, no credit card required.
2. Your First API Call - Fetch NBA Odds
import os
from dotenv import load_dotenv
from sharpapi import SharpAPI
load_dotenv()
client = SharpAPI(api_key=os.environ["SHARPAPI_KEY"])
# Fetch current NBA moneylines from all sportsbooks
odds = client.odds.list(sport="basketball_nba", markets=["h2h"])
for event in odds.data:
print(f"\n{event.home_team} vs {event.away_team}")
print(f"Start: {event.commence_time}")
for book in event.bookmakers:
market = book.markets[0]
for outcome in market.outcomes:
print(f" {book.title}: {outcome.name} {outcome.price}")Example output:
Boston Celtics vs Los Angeles Lakers
Start: 2026-04-17T00:30:00Z
DraftKings: Boston Celtics -160
FanDuel: Boston Celtics -155
Pinnacle: Boston Celtics -158
BetMGM: Boston Celtics -1623. Fetch Multiple Sports
SPORTS = ["basketball_nba", "americanfootball_nfl", "icehockey_nhl", "baseball_mlb"]
for sport in SPORTS:
odds = client.odds.list(sport=sport, markets=["h2h", "spreads"])
print(f"{sport}: {len(odds.data)} events, "
f"{sum(len(e.bookmakers) for e in odds.data)} book entries")For parallel fetching (much faster), use the async client:
import asyncio
from sharpapi import AsyncSharpAPI
async def fetch_all_sports():
async with AsyncSharpAPI(api_key=os.environ["SHARPAPI_KEY"]) as client:
tasks = [client.odds.list(sport=s, markets=["h2h"]) for s in SPORTS]
results = await asyncio.gather(*tasks)
return results4. Filter for Best Lines
def best_moneyline(event, team_name: str) -> tuple[str, float]:
"""Return (sportsbook, best price) for a given team."""
best_book, best_price = None, float("-inf")
for book in event.bookmakers:
for market in book.markets:
if market.key == "h2h":
for outcome in market.outcomes:
if outcome.name == team_name and outcome.price > best_price:
best_price = outcome.price
best_book = book.title
return best_book, best_price
odds = client.odds.list(sport="basketball_nba", markets=["h2h"])
for event in odds.data:
home_book, home_price = best_moneyline(event, event.home_team)
away_book, away_price = best_moneyline(event, event.away_team)
print(f"{event.home_team} best: {home_price} @ {home_book}")
print(f"{event.away_team} best: {away_price} @ {away_book}")5. Detect Arbitrage Opportunities
SharpAPI's built-in arbitrage endpoint (Hobby+ plans) detects cross-book arbs for you:
# Built-in arb detection, Hobby plan and above
arb_opps = client.opportunities.arbitrage(sport="basketball_nba")
for opp in arb_opps.data:
print(f"Arb: {opp.event}")
print(f" Profit: {opp.profit_percent:.2f}%")
for leg in opp.legs:
print(f" {leg.outcome} @ {leg.price} ({leg.sportsbook}) "
f" - stake {leg.stake_percent:.1f}%")For a deeper dive on the math and manual detection, see Sports Betting Arbitrage Explained.
6. Stream Live Odds via SSE (Hobby+)
Stop polling. Use SSE streaming for real-time line changes with sub-100ms latency:
def handle_odds_update(data: dict):
"""Called for each live odds change."""
event_id = data.get("event_id")
book = data.get("sportsbook")
for change in data.get("changes", []):
print(f"[{book}] {event_id}: {change['market']} {change['outcome']} "
f"{change['old_price']} -> {change['new_price']}")
# Streams indefinitely, reconnects automatically
for update in client.stream.odds(sports=["basketball_nba", "americanfootball_nfl"]):
handle_odds_update(update)7. Store Odds in SQLite for Line Movement
import sqlite3
from datetime import datetime
conn = sqlite3.connect("odds_history.db")
conn.execute("""
CREATE TABLE IF NOT EXISTS odds_log (
id INTEGER PRIMARY KEY,
event_id TEXT,
sportsbook TEXT,
market TEXT,
outcome TEXT,
price REAL,
fetched_at TEXT
)
""")
def log_odds(event):
now = datetime.utcnow().isoformat()
for book in event.bookmakers:
for market in book.markets:
for outcome in market.outcomes:
conn.execute(
"INSERT INTO odds_log VALUES (NULL,?,?,?,?,?,?)",
(event.id, book.title, market.key,
outcome.name, outcome.price, now),
)
conn.commit()
odds = client.odds.list(sport="basketball_nba", markets=["h2h", "spreads"])
for event in odds.data:
log_odds(event)
print(f"Logged {len(odds.data)} events")For a full walkthrough on building a line movement tracker, see How to Track Line Movement.
8. Error Handling and Rate Limits
from sharpapi.exceptions import RateLimitError, AuthenticationError
try:
odds = client.odds.list(sport="basketball_nba")
except RateLimitError:
# Free tier: 12 req/min. Retry after 1 second.
import time
time.sleep(1)
odds = client.odds.list(sport="basketball_nba")
except AuthenticationError:
print("Check your SHARPAPI_KEY. Get one at sharpapi.io/dashboard/api-keys")9. Summary - Methods and Plans
| Task | Method | Plan |
|---|---|---|
| Fetch odds (REST) | client.odds.list() | Free |
| Best odds across books | client.odds.best() | Free |
| Arbitrage detection | client.opportunities.arbitrage() | Hobby |
| +EV opportunities | client.opportunities.ev() | Pro |
| Live SSE streaming | client.stream.odds() | Hobby |