Skip to Content
SDKsPython

Python SDK

Das offizielle sharpapi Python-Paket bietet typisierten Zugriff auf alle SharpAPI-Endpunkte mit Pydantic-Modellen, SSE-Streaming und vollständiger IDE-Autovervollständigung.

InstallationPermalink for this section

pip install sharpapi

Mit optionaler pandas-Unterstützung:

pip install sharpapi[pandas]

Erfordert Python 3.9+.

SchnellstartPermalink for this section

from sharpapi import SharpAPI client = SharpAPI("sk_live_xxx") # Arbitrage-Gelegenheiten 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}%)") # +EV-Gelegenheiten 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}") # Beste Quoten über alle Sportsbooks 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}")

RessourcenPermalink for this section

OddsPermalink for this section

# Vollständiger Snapshot mit Filtern client.odds.get(sport="basketball", league="nba", limit=100) # Beste Quoten pro Auswahl über alle Sportsbooks client.odds.best(league="nfl", market="moneyline") # Direkter Vergleich für ein Event client.odds.comparison(event_id="evt_abc123") # Batch-Abfrage client.odds.batch(event_ids=["evt_abc123", "evt_def456"])

+EV-Gelegenheiten (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")

Arbitrage (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")

ReferenzdatenPermalink 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")

KontoPermalink 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}")

SSE-StreamingPermalink for this section

Echtzeit-Streaming mit Handler-basierten oder Iterator-basierten Mustern.

Wichtig: odds:update-Ereignisse sind Deltas — sie enthalten nur Quoten, die sich geändert haben. Ihr Client muss den lokalen Zustand verwalten und Aktualisierungen in diesen einfügen. Wenn jedes Ereignis als vollständiger Snapshot behandelt wird, entstehen fehlerhafte Daten.

Handler-basiert (Decorator-Muster)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

Iterator-basiertPermalink 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")

Vollständige ZustandsverwaltungPermalink for this section

Der all-Kanal überträgt dieselben Odds-Frames wie der odds-Kanal: snapshot-Chunks und odds:update-Deltas halten ihre Zeilen jeweils in einem odds-Array, nie nach Sportsbook strukturiert: Jede snapshot-Zeile und jede vollständig gesendete odds:update-Zeile enthält ihr sportsbook; eine kompakte odds:update-Zeile enthält es nicht, daher lesen Sie das Sportsbook aus dem book-Feld des Envelopes. Auf all erhalten Sie zusätzlich snapshot-Chunks mit Gelegenheiten — unter ev, arbitrage, middles, low_hold — sowie, falls Ihr Plan Game-State einschließt, gamestate:snapshot, was dieses Beispiel ignoriert. Alle Frames sind in der Stream-Endpoint-Referenz aufgeführt.

odds_map = {} # Keyed by odds line ID resuming = False # True zwischen einem fortgesetzten Connect und dem Frame, der ihn klärt stream = client.stream.all(league="nba") @stream.on("connected") def on_connected(data): # "resumed": True (nur odds-Kanal) heißt, dass das Replay BEGONNEN hat, nicht # dass es beendet ist — der Server kann noch auf einen vollständigen Snapshot # zurückfallen, also hier nicht löschen. Sonst folgt ein vollständiger # Snapshot: jetzt löschen. global resuming # eine Modulvariable im Handler neu zu binden braucht `global` resuming = data.get("resumed") is True if not resuming: odds_map.clear() @stream.on("snapshot") def on_snapshot(data): global resuming if resuming: # Der Server hat die Fortsetzung nach den wiederholten Frames abgebrochen: # dieser Snapshot ist die neue Baseline (snapshot:complete meldet "full_resync"). odds_map.clear() resuming = False # Odds-Zeilen kommen unter "odds", hier und in odds:update; ein Chunk mit # Gelegenheiten enthält stattdessen "ev" / "arbitrage" / "middles" / "low_hold" # und wird übersprungen. for odds in data.get("odds", []): odds_map[odds["id"]] = odds @stream.on("snapshot:complete") def on_snapshot_complete(data): # Ein full_resync ohne passende Zeilen sendet gar keine Chunks — also auch hier löschen. global resuming if data.get("mode") == "full_resync" and resuming: odds_map.clear() resuming = False @stream.on("odds:update") def on_update(data): # DELTA — jede Zeile in die gehaltene einpflegen. Ein Delta enthält nur die # Felder, die sich ändern können; die gespeicherte Zeile zu ersetzen, verliert den Rest. for delta in data.get("odds", []): row = odds_map.get(delta["id"]) if row is not None: row.update(delta) elif "sportsbook" in delta: # Eine Linie, die nach Ihrem Snapshot eröffnet wurde, kommt als vollständige Zeile 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()

Stream-KanälePermalink for this section

# Nur Quoten client.stream.odds(league="nba") # Nur Gelegenheiten (EV + Arb + Middles) client.stream.opportunities(min_ev=3.0) # Alles client.stream.all(sport="basketball") # Einzelnes Event client.stream.event("evt_abc123")

DatenqualitätPermalink for this section

Jede Gelegenheit enthält Metadaten zur Veraltung:

arbs = client.arbitrage.get() for arb in arbs.data: # Veraltete oder verdächtige Gelegenheiten überspringen 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

Informationen zu Rate Limits sind nach jeder Anfrage verfügbar:

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

FehlerbehandlungPermalink 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")

Hilfsfunktionen zur QuotenumrechnungPermalink 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() # HTTP-Client wird beim Beenden automatisch geschlossen

TypsicherheitPermalink for this section

Alle Antworten verwenden Pydantic-Modelle mit vollständigen Typhinweisen:

from sharpapi import EVOpportunity, ArbitrageOpportunity # IDE-Autovervollständigung funktioniert für alle Felder 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