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 32+ 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 |
| Historical odds | client.odds.historical() | Pro |