Owls Insight Python SDK
Official Python SDK for the Owls Insight real-time sports betting odds API. Sync and async clients, fully typed with Pydantic v2, plus a Socket.io WebSocket client for live streams.
pip install owls-insight
Requires Python 3.9+. Get an API key at owlsinsight.com.
Quick start (sync)
from owls_insight import OwlsInsight
client = OwlsInsight(api_key="owlsinsight_...")
# REST: current NBA odds. `data` is keyed by sportsbook -> list of events.
odds = client.rest.get_odds("nba", books=["pinnacle", "fanduel"])
for book, events in odds.data.items():
for event in events:
print(book, event.home_team, "vs", event.away_team)
# WebSocket: stream live updates
client.ws.connect()
client.ws.subscribe(sports=["nba"], books=["pinnacle"])
client.ws.on("odds-update", lambda data: print("live:", data))
# or block for the next event
update = client.ws.wait_for("odds-update", timeout=15)
client.destroy()
Quick start (async)
import asyncio
from owls_insight import AsyncOwlsInsight
async def main():
async with AsyncOwlsInsight(api_key="owlsinsight_...") as client:
odds = await client.rest.get_odds("nba", books=["pinnacle"])
print(sum(len(events) for events in odds.data.values()), "games")
await client.ws.connect()
await client.ws.subscribe(sports=["nba"], books=["pinnacle"])
update = await client.ws.wait_for("pinnacle-realtime", timeout=30)
print("realtime:", update)
asyncio.run(main())
Authentication
Pass your API key to the constructor. REST uses the Authorization: Bearer header; the WebSocket uses the ?apiKey= query string. Both are handled for you.
import os
client = OwlsInsight(api_key=os.environ["OWLS_INSIGHT_API_KEY"])
REST
Method names are snake_case (the JS SDK's getOdds is get_odds here). Every method returns a typed Pydantic model; unknown fields from the evolving API are preserved (model_extra), never dropped.
Full parity with the TypeScript SDK — every endpoint is available on both client.rest (sync) and the async client.
| Area | Methods |
|---|---|
| Odds | get_odds, get_moneyline, get_spreads, get_totals, get_realtime, get_ps3838_realtime, get_esports_realtime, get_ev, list_events, get_one_x_bet_soccer, get_prophetx_odds |
| Props | get_props, get_book_props, get_props_history, get_props_stats, get_book_props_stats |
| Prop results & trends | get_prop_results (one game, or date= to list a day's graded games), get_prop_trends (hit rate / average / streak vs a line, or line="closing" + book) |
| Same-Game Parlay | get_sgp_events, build_sgp (FanDuel bet-slip price; Rookie/MVP/HoF) |
| Scores / schedule | get_scores, get_schedule, get_results, get_splits, normalize, normalize_batch |
| Stats | get_stats, get_match_stats, get_h2h, get_player_averages |
| Line history | get_odds_history, get_moneyline_history, get_spread_history, get_totals_history |
| Historical | get_history_games, get_history_odds, get_history_props, iter_history_odds / iter_history_props (page a whole game, retries 429/503), get_history_stats, get_history_tennis_stats, get_game_stats_detail, get_closing_odds, get_historical_player_props, get_public_betting, get_cs2_matches, get_cs2_match, get_cs2_players |
| v2 Source API | get_hard_rock_events, get_hard_rock_leagues, get_bet365_v2/_leagues, get_betonline_v2/_leagues, get_betrivers_v2/_leagues, get_betus_v2/_leagues, get_bookmaker_v2/_leagues, get_bovada_v2/_leagues, get_draftkings_v2/_leagues, get_fanaticsmarkets_v2/_leagues, get_fanduel_v2/_leagues, get_kalshi_v2/_leagues, get_lowvig_v2/_leagues, get_mybookie_v2, get_pinnacle_v2/_leagues, get_polymarket_v2/_leagues, get_stake_v2, get_thescore_v2/_leagues, get_thunderpick_v2, get_underdog_v2, get_versus_v2/_leagues, plus get_hard_rock_ladder for Hard Rock's rootIdx decode |
Methods whose response shape isn't individually modelled yet return a permissive
ApiResponseenvelope (success/data/meta, all fields preserved). Deep per-endpoint Pydantic typing is the remaining follow-on; the method surface is complete.
ProphetX order book
Each ProphetX markets entry (keyed "{market_id}:{line}") parses as models.ProphetXMarket.
The book is a list of sides in outcomes order, each side its price levels best-first or
None when nothing is resting. On spread and total deltas the book sits under
marketLines[0] rather than the top level, so use the helpers:
from owls_insight import OwlsInsight, prophetx_book, prophetx_outcomes, prophetx_best
px = OwlsInsight(api_key="...").rest.get_prophetx_odds("baseball")
for event in px.data.sports["Baseball"]:
for key, market in event.markets.items():
outcomes = prophetx_outcomes(market) # labels, one per side
for i, side in enumerate(prophetx_book(market)):
best = prophetx_best(market, i)
if best is None: # None side = nothing resting
continue
# best.value = amount you can bet now; best.odds = American; payout from those two, not from stake
label = outcomes[i].name if i < len(outcomes) else None
print(key, label, best.odds, best.value)
Hard Rock odds decoding
Hard Rock v2 selections carry a rootIdx, not inline odds. Decode it client-side:
from owls_insight import root_idx_to_american_odds
root_idx_to_american_odds(42) # -325
root_idx_to_american_odds(72) # 100 (even)
WebSocket events
odds-update, props-update (and per-book {book}-props-update), pinnacle-realtime, ps3838-realtime, esports-update, prophetx-update, and the v2 {book}-v2-update deltas.
client.ws.connect()
client.ws.subscribe(sports=["nba", "nhl"], books=["pinnacle", "fanduel"])
client.ws.subscribe_props(book="fanduel") # props stream
client.ws.update_subscription(prophetx=True) # merge, don't replace
client.ws.on("props-update", handle_props)
Errors
from owls_insight import AuthenticationError, RateLimitError, ServiceBusyError, OwlsInsightError
try:
client.rest.get_odds("nba")
except AuthenticationError:
... # 401
except RateLimitError as e:
# e.code is "HISTORY_CONCURRENCY" when too many history requests were in flight
print("retry after", e.retry_after_ms, "ms", e.code)
except ServiceBusyError as e:
# 503: too expensive to serve in one request, or a transient outage
print("busy, retry after", e.retry_after_ms, "ms or narrow the request")
except OwlsInsightError as e:
print(e.status, e.message)
Retries are opt-in for direct calls: OwlsInsight(api_key, max_retries=3) retries 429 and 503 honouring Retry-After (else exponential backoff). iter_history_odds / iter_history_props retry by default. history_concurrency (default 3, the MVP plan's per-key history allowance; Hall of Fame allows 4) caps the history requests a client keeps in flight; requests beyond it wait client-side instead of being refused.
License
MIT
Release files for owls-insight 0.22.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| owls_insight-0.22.0.tar.gz | 98.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| owls_insight-0.22.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 185.4 kB
Release files / owls_insight-0.22.0.tar.gz
| Download URL | owls_insight-0.22.0.tar.gz |
|---|---|
| Size | 98.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9ca75f8c5b56c60222a80d80768856d97478ea72a01359da7e40146b6775772a
|
|
BLAKE2b-256 checksum How to use checksums |
c8d4bb481bc6d57139777398ca4080a9d1c993a1fd832536d710516f193a5ff3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.7
|
Release files / owls_insight-0.22.0-py3-none-any.whl
| Download URL | owls_insight-0.22.0-py3-none-any.whl |
|---|---|
| Size | 87.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f5e49a6675b3976507a673c6bc015620071ba0abd4629fec3fc7012ec4720a21
|
|
BLAKE2b-256 checksum How to use checksums |
a53e635e53ca2fc9aead996f6f51a10033d4564c353973cbc219e848a4bd1df5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.7
|