Skip to Content
SDKsPython

SDK Python

O pacote oficial sharpapi para Python fornece acesso tipado a todos os endpoints da SharpAPI com modelos Pydantic, streaming SSE e autocompletar completo na IDE.

InstalaçãoPermalink for this section

pip install sharpapi

Com suporte opcional a pandas:

pip install sharpapi[pandas]

Requer Python 3.9+.

Início RápidoPermalink for this section

from sharpapi import SharpAPI client = SharpAPI("sk_live_xxx") # Oportunidades de arbitragem arbs = client.arbitrage.get(min_profit=1.0, league="nba") for arb in arbs.data: print(f"{arb.profit_percent:.2f}% profit — {arb.event_name}") for leg in arb.legs: print(f" {leg.sportsbook}: {leg.selection} @ {leg.odds_american} ({leg.stake_percent:.1f}%)") # Oportunidades +EV evs = client.ev.get(min_ev=3.0, sport="basketball") for opp in evs.data: print(f"+{opp.ev_percentage:.1f}% EV on {opp.selection} @ {opp.sportsbook}") if opp.kelly_percent: print(f" Kelly: {opp.kelly_percent:.2f}%, Confidence: {opp.confidence_score}") # Melhores odds entre casas de apostas odds = client.odds.best(league="nba", market="moneyline") for line in odds.data: print(f"{line.home_team} vs {line.away_team}: {line.selection} {line.odds_american}")

RecursosPermalink for this section

OddsPermalink for this section

# Snapshot completo com filtros client.odds.get(sport="basketball", league="nba", limit=100) # Melhores odds por seleção entre casas client.odds.best(league="nfl", market="moneyline") # Comparação lado a lado para um evento client.odds.comparison(event_id="evt_abc123") # Consulta em lote client.odds.batch(event_ids=["evt_abc123", "evt_def456"])

Oportunidades +EV (Pro+)Permalink for this section

evs = client.ev.get( min_ev=2.0, sportsbook="draftkings", league="nba", sort="-ev", # Highest EV first max_odds_age=60, # Only fresh odds limit=50, ) for opp in evs.data: print(f"+{opp.ev_percentage:.1f}% on {opp.selection} @ {opp.sportsbook}") print(f" Fair probability: {opp.fair_probability}") print(f" Devig: {opp.devig_method} via {opp.sharp_book}") print(f" Kelly: {opp.kelly_percent:.2f}%") print(f" Confidence: {opp.confidence_score}/100")

Arbitragem (Hobby+)Permalink for this section

arbs = client.arbitrage.get( min_profit=0.5, sport="basketball", group="best", # One per event+market max_odds_age=30, # Only fresh odds ) for arb in arbs.data: if arb.possibly_stale: continue # Skip stale opportunities print(f"{arb.profit_percent:.2f}% — {arb.event_name}") if arb.game_state: gs = arb.game_state print(f" Live: {gs.period} {gs.clock} ({gs.score_home}-{gs.score_away})") for leg in arb.legs: print(f" {leg.sportsbook}: {leg.selection} @ {leg.odds_american} → stake {leg.stake_percent:.1f}%")

Middles (Pro+)Permalink for this section

middles = client.middles.get(sport="football", min_size=3.0, sort="quality") for mid in middles.data: print(f"{mid.event_name} — gap: {mid.middle_size} pts") if mid.side1 and mid.side2: print(f" {mid.side1.book}: {mid.side1.selection} {mid.side1.line} @ {mid.side1.odds.american}") print(f" {mid.side2.book}: {mid.side2.selection} +{mid.side2.line} @ {mid.side2.odds.american}") print(f" Hit probability: {mid.middle_probability:.1%}") print(f" Expected value: ${mid.expected_value:.2f}") print(f" Key numbers: {mid.key_numbers}")

Low HoldPermalink for this section

low_holds = client.low_hold.get(max_hold=2.0, sport="basketball") for lh in low_holds.data: print(f"{lh.event_name}: {lh.hold_percentage:.2f}% hold")

Dados de ReferênciaPermalink for this section

client.sports.list() client.leagues.list(sport="basketball") client.sportsbooks.list() client.events.list(league="nba", live=True) client.events.search("Lakers")

ContaPermalink for this section

info = client.account.me() print(f"Tier: {info.key['tier']}") print(f"Rate limit: {info.limits.requests_per_minute} req/min") print(f"Features: EV={info.features.ev}, Arb={info.features.arbitrage}")

Streaming SSEPermalink for this section

Streaming em tempo real com padrões baseados em handlers ou iteradores.

Crítico: Eventos odds:update são deltas — eles contêm apenas as odds que mudaram. Seu cliente deve manter o estado local e mesclar as atualizações nele. Tratar cada evento como um snapshot completo produzirá dados incorretos.

Baseado em Handlers (Padrão Decorator)Permalink for this section

stream = client.stream.opportunities(league="nba", min_ev=3.0) @stream.on("ev:detected") def on_ev(data): # data is {"opportunities": [...], "count": N, "type": "ev"}. An item is new, # or an updated version of one already sent (same id) for opp in data["opportunities"]: if not opp.get("possibly_stale"): print(f"+EV: {opp['selection']} {opp['ev_percentage']}% @ {opp['sportsbook']}") @stream.on("arb:detected") def on_arb(data): for arb in data["opportunities"]: print(f"Arb: {arb['profit_percent']}% — {arb['event_name']}") @stream.on("snapshot:complete") def on_ready(data): print(f"Ready: snapshot complete at {data['timestamp']}") stream.connect() # Blocks, processing events

Baseado em IteradorPermalink for this section

stream = client.stream.all(sport="basketball") for event_type, data in stream.iter_events(): if event_type == "ev:detected": for opp in data["opportunities"]: print(f"+EV: {opp['ev_percentage']}%") elif event_type == "arb:detected": for arb in data["opportunities"]: print(f"Arb: {arb['profit_percent']}%") elif event_type == "snapshot:complete": print("Stream ready")

Gerenciamento Completo de EstadoPermalink for this section

O canal all transporta os mesmos frames de odds que o canal odds: os chunks snapshot e os deltas odds:update guardam suas linhas em um array odds, sem chaveamento por sportsbook: cada linha de snapshot, e cada linha de odds:update enviada completa, traz seu sportsbook; uma linha compacta de odds:update não o traz, então leia o sportsbook do campo book do envelope. Em all você também recebe chunks snapshot de oportunidades — sob ev, arbitrage, middles, low_hold — e, se seu plano incluir game state, gamestate:snapshot, que este exemplo ignora. Todos os frames estão listados na referência do endpoint Stream.

odds_map = {} # Keyed by odds line ID resuming = False # True entre uma conexão retomada e o frame que a resolve stream = client.stream.all(league="nba") @stream.on("connected") def on_connected(data): # "resumed": True (só canal odds) significa que a reprodução COMEÇOU, não que # terminou — o servidor ainda pode cair para um snapshot completo, então não # limpe aqui. Em qualquer outro caso vem um snapshot completo: limpe agora. global resuming # religar um nome de módulo dentro de um handler exige `global` resuming = data.get("resumed") is True if not resuming: odds_map.clear() @stream.on("snapshot") def on_snapshot(data): global resuming if resuming: # O servidor abandonou a retomada depois dos frames reproduzidos: este # snapshot é a nova base (snapshot:complete informa "full_resync"). odds_map.clear() resuming = False # As linhas de odds chegam sob "odds", aqui e em odds:update; um chunk de # oportunidades traz "ev" / "arbitrage" / "middles" / "low_hold" e é ignorado. for odds in data.get("odds", []): odds_map[odds["id"]] = odds @stream.on("snapshot:complete") def on_snapshot_complete(data): # Um full_resync sem linhas correspondentes não envia chunk algum: limpe aqui também. global resuming if data.get("mode") == "full_resync" and resuming: odds_map.clear() resuming = False @stream.on("odds:update") def on_update(data): # DELTA — mescle cada linha na que você já tem. Um delta traz só os campos que # podem mudar; substituir a linha armazenada perderia o resto. for delta in data.get("odds", []): row = odds_map.get(delta["id"]) if row is not None: row.update(delta) elif "sportsbook" in delta: # Uma linha aberta depois do seu snapshot chega como linha completa odds_map[delta["id"]] = delta @stream.on("odds:removed") def on_removed(data): for odds_id in data.get("ids", []): odds_map.pop(odds_id, None) stream.connect()

Canais de StreamPermalink for this section

# Apenas odds client.stream.odds(league="nba") # Apenas oportunidades (EV + arb + middles) client.stream.opportunities(min_ev=3.0) # Tudo client.stream.all(sport="basketball") # Evento único client.stream.event("evt_abc123")

Qualidade dos DadosPermalink for this section

Toda oportunidade inclui metadados de obsolescência:

arbs = client.arbitrage.get() for arb in arbs.data: # Ignorar oportunidades obsoletas ou suspeitas if arb.possibly_stale: print(f"Stale ({arb.oldest_odds_age_seconds}s old)") continue if "LIVE_HIGH_PROFIT_SUSPICIOUS" in arb.warnings: print("Phantom arb — skipping") continue print(f"Actionable: {arb.profit_percent}%")

Rate LimitsPermalink for this section

As informações de rate limit ficam disponíveis após cada requisição:

response = client.odds.get() rl = client.rate_limit print(f"{rl.remaining}/{rl.limit} requests remaining (tier: {rl.tier})")

Tratamento de ErrosPermalink for this section

from sharpapi import ( SharpAPI, AuthenticationError, TierRestrictedError, RateLimitedError, ) client = SharpAPI("sk_live_xxx") try: evs = client.ev.get() except AuthenticationError: print("Invalid API key") except TierRestrictedError as e: print(f"Upgrade to {e.required_tier} for this feature") except RateLimitedError as e: print(f"Rate limited — retry after {e.retry_after}s")

Utilitários de Conversão de OddsPermalink for this section

from sharpapi import american_to_decimal, american_to_probability, decimal_to_american american_to_decimal(-110) # 1.909 american_to_decimal(150) # 2.5 american_to_probability(-110) # 0.524 decimal_to_american(2.5) # 150

Context ManagerPermalink for this section

with SharpAPI("sk_live_xxx") as client: arbs = client.arbitrage.get() # O cliente HTTP é fechado automaticamente ao sair

Segurança de TiposPermalink for this section

Todas as respostas usam modelos Pydantic com type hints completos:

from sharpapi import EVOpportunity, ArbitrageOpportunity # O autocomplete da IDE funciona em todos os campos arbs = client.arbitrage.get() arb: ArbitrageOpportunity = arbs.data[0] arb.profit_percent # float arb.legs[0].sportsbook # str arb.game_state.period # str | None
Last updated on