Live-Spielstatus
Aggregierter Live-Spielstatus — Punktestände, Perioden, Uhren, Ballbesitz und sportartspezifische situative Daten — aus Sportsbooks zusammengeführt zu einer einzigen autoritativen Ansicht pro Event. Eine Zeile pro Live-Spiel, konsensbasiert ausgewählt aus den Büchern, die es abdecken. Ein gerade beendetes Spiel bleibt hier ebenfalls für ein kurzes Zeitfenster als terminale Zeile — siehe Beendete Events.
GET /api/v1/gamestate
GET /api/v1/gamestate/{sport}Authentifizierung
Erfordert API-Schlüssel. Verfügbar mit dem Game State Add-on (79 $/Monat) oder der
Enterprise-Stufe. Schlüssel ohne eines von beiden erhalten 403 tier_restricted
mit addon: "game_state" im Fehler-Body. Fügen Sie das Add-on über die
Abrechnungsseite bei jedem kostenpflichtigen Plan
hinzu (Hobby / Pro / Sharp); Enterprise-Schlüssel haben es bereits enthalten.
Live-Spielstatus wird auch über den Kanal gamestate per
SSE und WebSocket gestreamt
— siehe Streaming unten. (Streaming-Zugriff erfordert das
WebSocket Add-on oder Enterprise, zusätzlich zur oben genannten Game State-
Anforderung.)
Pfadparameter
| Parameter | Typ | Beschreibung |
|---|---|---|
sport | string | (optional) Einzelne zurückzugebende Sportart. Entspricht einer bekannten Atlas-Sportart (soccer, tennis, esports, basketball, table_tennis, baseball, hockey, cricket, volleyball, handball, football, olympics, darts, boxing, rugby_league, mma, aussie_rules, snooker, rugby_union, golf, floorball, water_polo, lacrosse, futsal, other). Groß-/Kleinschreibung wird ignoriert. Eine unbekannte Sportart gibt ein leeres data-Objekt zurück — ebenso eine unterstützte Sportart, für die gerade nichts live ist; siehe Hinweis unten. |
Ein leeres data-Objekt bedeutet nicht, dass die Sportart nicht unterstützt wird.
Game State wird pro Sportart in einem gamestate:{sport}-Key mit 60 Sekunden TTL
zusammengeführt (GAMESTATE_TTL, bewusst das Doppelte des 30-Sekunden-livestate-Fensters).
Ist nichts live, läuft der Key ab und die Antwort lautet
{"data":{}} — identisch zur Antwort für eine nicht abgedeckte Sportart. Mehrere der
oben genannten Sportarten sind saisonal.
Mit GET /api/v1/sports sehen Sie event_count und live_count je Sportart.
other ist der Fallback-Bucket für von Atlas nicht zugeordnete Sportarten.
Ohne den Pfadparameter werden alle Sportarten mit Live-Events zurückgegeben.
Abfrageparameter
Beide gelten für /api/v1/gamestate und /api/v1/gamestate/{sport}.
Werte sind case-insensitiv und werden getrimmt. Lassen Sie beide weg,
erhalten Sie die Standardmenge: alles Live plus die beendeten Zeilen, die
sich noch im Übertragungsfenster befinden.
| Parameter | Akzeptiert | Beschreibung |
|---|---|---|
status | final | Gibt ausschließlich die übertragenen beendeten Zeilen zurück — das Gegenstück zu /events?status=final. Jeder andere Wert ist ein 400 validation_error (siehe unten). Es gibt keinen Wert live: Live-Zeilen sind die Standardmenge, und ?is_live=true fordert sie allein an. |
is_live | true, false | true gibt nur Live-Zeilen zurück und schließt damit jede beendete Zeile aus. false gibt nur Nicht-Live-Zeilen zurück, was heute die übertragenen beendeten Zeilen bedeutet. Jeder andere Wert — einschließlich 1 und 0 — ist ein 400 validation_error. |
Ein unbekannter Wert wird abgelehnt, nicht ignoriert. ?status=zzz und
?is_live=1 liefern beide 400 mit einem validation_error-Body, der die
akzeptierte Menge nennt, statt stillschweigend die ungefilterten oder die
falschen Zeilen zurückzugeben.
{
"error": {
"code": "validation_error",
"message": "is_live must be one of: true, false",
"details": { "parameter": "is_live", "accepted": ["true", "false"] }
}
}?is_live=1 wurde früher als false gelesen und lieferte genau die Zeilen,
die der Aufrufer ausschließen wollte; jetzt ist es ein 400. Wenn Sie 1 /
0 senden, stellen Sie auf true / false um.
Antwort-Hülle
{
"data": {
"<sport>": {
"<event_id>": { ...event state... }
}
},
"updated_at": "2026-04-23T23:55:01.234Z"
}Jeder Event-Status ist über seine kanonische event_id innerhalb seines Sportart-
Buckets indiziert. updated_at ist die Serverzeit, zu der diese Antwort erstellt
wurde — verwenden Sie sie, um die Aktualität bei Polling abzuschätzen.
Event-Status-Felder
Jedes Feld ist optional, sofern nicht anders angegeben. Das Vorhandensein variiert je nach Sportart —
Tennis-Events enthalten sets_home / server; Hockey-Events enthalten
power_play; Soccer-Events enthalten corners_* / yellow_cards_*; usw.
Immer vorhanden
| Feld | Typ | Hinweise |
|---|---|---|
home_team | string | Normalisierter Name des Heimteams |
away_team | string | Normalisierter Name des Auswärtsteams |
sport | string | Sportart-Bucket (baseball, soccer, …) |
league | string | Atlas-Liga-ID (z. B. mlb, england_-_premier_league) |
home_score | integer | Aktueller Heim-Punktestand in der natürlichen Einheit für die Sportart (Runs, Punkte, Tore, Sätze — Tennis verwendet summierte Satzpunkte) |
away_score | integer | Aktueller Auswärts-Punktestand |
is_live | boolean | true bei einem Live-Event. false bei einer übertragenen beendeten Zeile — siehe Beendete Events |
primary_book | string | Das Buch, aus dem die zusammengeführten Punktestände ausgewählt wurden. Konsensbasiert — höherwertige Bücher gewinnen bei Gleichstand |
book_count | integer | Anzahl der Sportsbooks, die zum Zeitpunkt des Mergens Live-Status für dieses Event beigetragen haben |
„Immer vorhanden” heißt: immer vorhanden auf einer Live-Zeile. Eine
übertragene beendete Zeile wird aus dem Abschluss-Datensatz gebildet, nicht
aus einem Live-Merge, und trägt daher weder primary_book noch book_count
und kein game_period / game_clock. Auch ihre Punktestandsfelder hängen
von der Sportart ab: satzbasierte Sportarten tragen sets_home /
sets_away statt home_score / away_score. Prüfen Sie
status === "final", bevor Sie eines dieser Felder lesen. Die vollständige
terminale Form steht unter Beendete Events.
Zeitlich (die meisten Mannschaftssportarten)
| Feld | Typ | Beispiel |
|---|---|---|
game_period | string | "T5" (MLB Anfang des 5.), "Q3" (NBA Q3), "2H" (Soccer 2. Halbzeit), "P2" (NHL), "S2" (Tennis 2. Satz), "FT" (Spielende) |
game_clock | string | "49:06" für hochzählende Sportarten (Soccer), "5:42" für herunterzählende Sportarten (Basketball, Hockey) |
Situativ
| Feld | Typ | Hinweise |
|---|---|---|
possession | "home" | "away" | Welches Team in Ballbesitz ist (Mannschaftssportarten) oder aufschlägt (Tennis) |
server | "home" | "away" | Nur Tennis — aktueller Aufschläger |
last_play | string | Sportartspezifische Beschreibung des letzten Spielzugs, sofern verfügbar |
is_timeout | boolean | true während eines Timeouts |
Sportartspezifisch
| Feld | Sportart | Typ |
|---|---|---|
fouls_home, fouls_away | basketball, soccer | integer |
corners_home, corners_away | soccer | integer |
yellow_cards_home, yellow_cards_away | soccer | integer |
red_cards_home, red_cards_away | soccer | integer |
power_play | hockey | "home" | "away" |
sets_home, sets_away | tennis | integer (gewonnene Sätze) |
hits_home, hits_away | baseball | integer |
home_pitcher, away_pitcher | baseball | string — Anzeigename des Starting Pitchers |
wickets_home, wickets_away | cricket | integer |
overs | cricket | float |
batting_team | cricket | "home" | "away" |
Aktualität
| Feld | Typ | Bedeutung |
|---|---|---|
stale | true (sonst weggelassen) | Die TTL des Livestate-Schlüssels von primary_book ist zum Zeitpunkt des Mergens unter den Aktualitätsschwellenwert des Aggregators (~10s) gefallen. Punktestände sind die zuletzt bekannten Werte; das Buch ist verstummt. Behandeln Sie Punktestände und Periode des Events als möglicherweise hinter dem tatsächlichen Spielstatus zurückbleibend. |
aggregator_stale | true (sonst weggelassen) | Der SharpAPI-Aggregator selbst hat seit über ~30s keinen neuen gamestate:{sport}-Shard für diese Sportart geschrieben. Breiteres Signal als stale — weist auf eine Pipeline-Störung hin, nicht auf ein Problem mit einem einzelnen Buch. Wenn Sie dies anhaltend sehen, kontaktieren Sie den Support. |
Abwesenheit = aktuell. Sowohl stale als auch aggregator_stale werden nur
geschrieben, wenn sie zutreffen, was die Payload kompakt hält. Wenn Sie sie
nicht bei einem Event sehen, gelten die Daten als aktuell.
Beendete Events
Ein Spiel verschwand früher in dem Moment aus /gamestate, in dem es endete —
das Endergebnis war nur für Clients sichtbar, die zufällig während des letzten
Live-Zyklus gepollt haben. Beendete Events bleiben jetzt 15 Minuten nach
Spielende als terminale Zeile auf diesem Endpoint, sodass jeder Poller mit
vernünftigem Intervall das Endergebnis mindestens einmal sieht.
Das Fenster ist serverseitig und beträgt derzeit 15 Minuten
(GAMESTATE_TERMINAL_CARRY_SECONDS, Standard 900). Danach verschwindet die
Zeile endgültig; das abgeschlossene Ergebnis bleibt über
/api/v1/events?status=final verfügbar — die Quelle,
aus der die terminale Zeile stammt.
So sieht eine terminale Zeile aus
{
"home_team": "Skelleftea AIK",
"away_team": "Geneva Servette HC",
"sport": "hockey",
"league": "champions_hockey_league",
"home_score": 2,
"away_score": 1,
"score_type": "points",
"winner": "home",
"status": "final",
"completed_at": "2026-09-10T18:54:03Z",
"is_live": false,
"stale": false
}| Feld | Typ | Hinweise |
|---|---|---|
status | "final" | Nur auf einer terminalen Zeile vorhanden. Live-Zeilen tragen überhaupt keinen status-Schlüssel, daher ist status === "final" der verlässliche Test — leiten Sie ihn nicht aus is_live ab. |
completed_at | string | ISO 8601 — der Zeitpunkt, zu dem SharpAPI den Abschluss erkannt hat, nicht der offizielle Schlusspfiff der Liga. Das Übertragungsfenster wird ab diesem Zeitstempel gemessen. |
score_type | "points" | "sets" | Welche Felder das Ergebnis tragen. "sets" für die satzbasierten Sportarten (tennis, table_tennis, volleyball, badminton); "points" überall sonst. |
winner | "home" | "away" | "draw" | Optional — wird weggelassen, wenn das Ergebnis nicht bestimmbar ist; siehe unten. "draw" entsteht nur bei Sportarten, in denen ein Gleichstand ein echtes Resultat ist (soccer, hockey, football, rugby_union, rugby_league). |
is_live | false | Immer explizit false, damit jede is_live-gesteuerte Darstellung die Zeile als nicht live behandelt. |
stale | false | Immer explizit false. Dies ist die einzige Stelle, an der stale erscheint, ohne zuzutreffen — die Regel „Abwesenheit = aktuell” oben gilt nur für Live-Zeilen. |
Endergebnisse verwenden die Live-Feldnamen, je nach Sportart. Eine
punktbasierte Sportart legt ihren Zählstand in home_score / away_score. Eine
satzbasierte Sportart legt ihre Satzanzahl in sets_home / sets_away und
trägt kein home_score / away_score — ein Tennis-Endstand ist 2:1 in
Sätzen, und das in home_score zu legen würde mit den Spielpunkten der
Live-Zeile kollidieren:
{
"home_team": "Mikhail Biserov",
"away_team": "Vasily Yugov",
"sport": "table_tennis",
"league": "pro_league",
"sets_home": 2,
"sets_away": 1,
"score_type": "sets",
"winner": "home",
"status": "final",
"completed_at": "2026-09-10T18:55:25Z",
"is_live": false,
"stale": false
}winner kann fehlen, und das hat eine Bedeutung. Das Feld wird
weggelassen, wenn das erfasste Ergebnis das Spiel nicht entscheidet:
- ein Gleichstand in einer Sportart ohne Unentschieden (Basketball, Handball, E-Sport, Baseball …) — das Spiel ging in die Verlängerung, wurde abgebrochen, oder der Feed endete vor der Entscheidung;
- eine sub-terminale Satzanzahl in einer satzbasierten Sportart (wer weniger als zwei Sätze führt, kann kein Format gewonnen haben);
- ein Snapshot, dessen Punktestände nie eintrafen, der als
0-0erfasst wird. Deshalb ist ein0-0ohnewinnernicht dasselbe wie ein echtes torloses Unentschieden — ein echtes trägt"winner": "draw".
Behandeln Sie ein fehlendes winner als „nicht entschieden”, nicht als
Gleichstand. Wenn Sie werten oder abrechnen, überspringen Sie diese Zeilen.
Welche Felder eine terminale Zeile nicht trägt
Eine terminale Zeile wird aus dem Abschluss-Datensatz projiziert, nicht aus
einem Live-Merge über mehrere Bücher, daher fehlen diese Live-Felder schlicht:
primary_book, book_count, game_period, game_clock sowie jedes situative
Feld (possession, corners_*, power_play, in_play, die
Tennis-Siegwahrscheinlichkeits-Familie und so weiter). Lesen Sie sie erst,
nachdem Sie geprüft haben, dass die Zeile nicht status: "final" ist.
Beendete Events filtern
| Sie möchten | Anfrage |
|---|---|
| Nur Live-Zeilen (Verhalten vor der Übertragung) | ?is_live=true |
| Nur beendete Zeilen | ?status=final |
| Beides (Standard) | kein Parameter |
# Nur die Endergebnisse der letzten 15 Minuten, eine Sportart
curl "https://api.sharpapi.io/api/v1/gamestate/soccer?status=final" \
-H "X-API-Key: YOUR_API_KEY"
# Genau das, was Sie vor der Übertragung bekommen haben
curl "https://api.sharpapi.io/api/v1/gamestate?is_live=true" \
-H "X-API-Key: YOUR_API_KEY"Ist ein beendetes Event irgendwo noch live, gewinnt die Live-Zeile. Eine
event_id, die sowohl in der Live-Menge als auch in der übertragenen Menge
auftaucht, wird einmal zurückgegeben — als Live-Zeile. Ein Buch, das das
Spiel noch quotiert, hält es live, bis jedes Buch es fallengelassen hat.
Beispielanfragen
cURL
# Alle Live-Events über alle Sportarten
curl "https://api.sharpapi.io/api/v1/gamestate" \
-H "X-API-Key: YOUR_API_KEY"
# Eine Sportart
curl "https://api.sharpapi.io/api/v1/gamestate/soccer" \
-H "X-API-Key: YOUR_API_KEY"Beispielantwort
{
"data": {
"soccer": {
"argentina_-_primera_division_bocajuniors_defensayjusticia_2026-04-23": {
"home_team": "Defensa y Justicia",
"away_team": "Boca Juniors",
"sport": "soccer",
"league": "argentina_-_primera_division",
"home_score": 0,
"away_score": 1,
"game_period": "2H",
"game_clock": "49:06",
"is_live": true,
"possession": "away",
"corners_home": 1,
"corners_away": 0,
"fouls_home": 0,
"fouls_away": 0,
"yellow_cards_home": 0,
"yellow_cards_away": 0,
"red_cards_home": 0,
"red_cards_away": 0,
"primary_book": "draftkings",
"book_count": 6
},
"chile_-_primera_division_concepcion_palestino_2026-04-24": {
"home_team": "Palestino",
"away_team": "Concepción",
"sport": "soccer",
"league": "chile_-_primera_division",
"home_score": 0,
"away_score": 0,
"is_live": true,
"primary_book": "unibet",
"book_count": 1,
"stale": true
},
"montenegro_-_prva_liga_bokelj_otrantolympiculcinj_2026-04-23": {
"home_team": "Bokelj",
"away_team": "FK Otrant-Olympic Ulcinj",
"sport": "soccer",
"league": "montenegro_-_prva_liga",
"home_score": 2,
"away_score": 1,
"score_type": "points",
"winner": "home",
"status": "final",
"completed_at": "2026-04-23T23:48:55Z",
"is_live": false,
"stale": false
}
}
},
"updated_at": "2026-04-23T23:55:01.234Z"
}Die dritte Zeile ist eine terminale Zeile: Das Spiel endete
rund sechs Minuten, bevor diese Antwort erzeugt wurde, wird also mit
status: "final" und seinem Endstand übertragen und trägt kein primary_book,
book_count, game_period oder game_clock.
Cross-Book-Merge-Modell
Die Felder jedes Events werden aus dem Livestate-Snapshot jedes Sportsbooks über einen dreiklassigen Algorithmus zusammengeführt:
- Klasse A — Punktestände werden per Konsens ausgewählt: Unter den Büchern mit dem am weitesten fortgeschrittenen Periodenrang gewinnt der höchste Gesamtpunktestand mit ≥2 Unterstützern. Ein einzelnes Buch, das einen Ausreißer-Score meldet (häufig in den ersten Sekunden einer Punkteänderung oder von einem fehlerhaften Adapter), wird abgelehnt.
- Klasse B — Zeitliche Felder (
game_period,game_clock) werden basierend auf der sportartabhängigen Uhrenrichtung — herunterzählend vs. hochzählend — und dem Periodenrang ausgewählt. - Klasse C — Situative Felder (
possession,corners_*, etc.) werden prioritätsbasiert aus einem festen Buchranking gefüllt.
primary_book gibt an, aus welchem Buch die siegreichen Punktestände stammten.
book_count ist die Gesamtzahl der Bücher, die zum Zeitpunkt des Mergens
irgendeinen Status zum Event beigetragen haben.
Streaming
Jede Live-Aktualisierung, die der REST-Endpoint beim nächsten Poll anzeigen
würde, löst auch ein gamestate:update-Event auf den Streaming-Kanälen aus:
- SSE:
GET /api/v1/stream/gamestate - WebSocket: Abonnieren Sie
{channels: ["gamestate"]}aufwss://ws.sharpapi.io/ws
Streaming-Clients erhalten einen initialen gamestate:snapshot mit dem vollständigen
aktuellen Status (eine flache Liste von Event-Zeilen), dann gamestate:update-
Events mit geänderten Zeilen während des Mergens und gamestate:removed-
Events, wenn Events aus der Live-Menge fallen.
Ein beendetes Event bleibt als status: "final"-Zeile für das Fenster aus
Beendete Events in der gestreamten Slate und wird
einmal mit einem gamestate:final-Frame angekündigt.
Abschlussmeldungen
Wenn ein Event endet, sendet jeder Transport genau einen
gamestate:final-Frame dafür. Er trägt die terminale Zeile des Events —
dieselbe Form wie die REST-Zeile, plus event_id.
WebSocket:
{"type":"gamestate:final","seq":48213,"global_seq":"48213","data":[{"event_id":"atp_jdoe_rroe_2026-07-03","home_team":"J. Doe","away_team":"R. Roe","sport":"tennis","league":"atp","sets_home":1,"sets_away":2,"score_type":"sets","winner":"away","status":"final","completed_at":"2026-07-03T19:42:10Z","is_live":false,"stale":false}]}SSE (dieselbe {"data": [...]}-Hülle wie gamestate:update):
event: gamestate:final
data: {"data":[{"event_id":"atp_jdoe_rroe_2026-07-03","home_team":"J. Doe","away_team":"R. Roe","sport":"tennis","league":"atp","sets_home":1,"sets_away":2,"score_type":"sets","winner":"away","status":"final","completed_at":"2026-07-03T19:42:10Z","is_live":false,"stale":false}]}- Gleicher Zugang, gleiche Filter wie
gamestate:update. Wergamestate:updateerhält, erhält auchgamestate:final; Ihresport/league-Filter gelten. Ohne Game-State-Zugang erhalten Sie keines von beiden. - WebSocket spielt ihn erneut ab. Wie
gamestate:updateundgamestate:removedliegt er im Replay-Puffer und wird bei einem Reconnect mitfrom_seqerneut gesendet. SSE-Gamestate-Frames sind nicht fortsetzbar. - Eine Ankündigung, nicht die einzige Zustellung. Dieselbe Zeile
steht während des ganzen Fensters auch in der Slate
(
gamestate:snapshotundgamestate:update).
„Einmal pro Event“ kann sich gelegentlich wiederholen — deduplizieren Sie auf
(event_id, completed_at). Sie können gamestate:final für dasselbe
Event mehr als einmal erhalten. event_id und completed_at sind bei
jeder Wiederholung identisch; verwerfen Sie einen Frame, dessen Paar Sie
schon verarbeitet haben. Das ist immer
sicher, weil die Zeile weiterhin in der Slate steht.
Rate Limits & Kontingente
Rate Limits richten sich nach Ihrer Basisstufe, nicht nach dem Add-on — Hobby-Schlüssel behalten
ihre 120 req/min, Pro behalten 300, Sharp behalten 1.000, Enterprise individuell.
Siehe Pricing . /gamestate zählt
genauso wie andere REST-Aufrufe zum Pro-Minute-Kontingent.
Fehlerbehebung
- 403
tier_restrictedmitaddon: "game_state"— Ihr Schlüssel hat das Game State Add-on nicht. Fügen Sie es über Billing für 79 $/Monat hinzu oder upgraden Sie auf Enterprise. - 400
validation_errormitparameter: "status"— der einzige akzeptierte?status=-Wert istfinal. Es gibt keinen Wertlive; Live-Zeilen sind die Standardmenge, oder fordern Sie sie allein mit?is_live=truean. - 400
validation_errormitparameter: "is_live"—?is_live=akzeptiert nurtrue/false.1und0werden abgelehnt; früher wurden sie alsfalsegelesen und lieferten das Gegenteil des Gewünschten. - Eine Zeile mit
status: "final"undis_live: false— kein Fehler. Das ist ein beendetes Event innerhalb seines 15-Minuten-Fensters. Filtern Sie mit?is_live=truefür das Verhalten von vorher. primary_book/book_countfehlen — Sie lesen eine terminale Zeile. Prüfen Siestatus, bevor Sie Live-Felder lesen.- Leeres
data-Objekt — keine Live-Events im Bereich. Dies ist häufig zwischen Events bei kleineren Sportplänen. Pollen Sie erneut. - Events mit
stale: true— das primäre Buch ist verstummt. Punktestände können hinterherhinken; erwägen Sie, sie herauszufiltern oder einen UI- Indikator anzuzeigen. - Events mit
aggregator_stale: true— der SharpAPI-Aggregator hat diese Sportart seit >30s nicht aktualisiert. Wenn anhaltend, kontaktieren Sie den Support; ein kurzer Anstieg kann während Redeploys auftreten. - Dieselbe Partie erscheint zweimal unter verschiedenen
event_ids — bekannte Einschränkung bei einigen regionalen Ligen, in denen Sportsbooks inkonsistente Liganamen verwenden. Verwenden Sie(home_team, away_team)als sekundären Dedup-Schlüssel auf dem Client, bis die verbleibenden Alias-Lücken geschlossen sind.