Skip to Content
Referencia de la APIEstado del partido en directo

Estado de Juego en Vivo

Estado de juego en vivo agregado — marcadores, periodos, relojes, posesión y datos situacionales específicos de cada deporte — fusionado entre sportsbooks en una única vista autorizada por evento. Una fila por encuentro en vivo, seleccionada por consenso entre los libros que lo cubren. Un encuentro que acaba de terminar también permanece aquí durante una ventana breve como fila terminal — consulta Eventos finalizados.

GET /api/v1/gamestate GET /api/v1/gamestate/{sport}

AutenticaciónPermalink for this section

Requiere API key. Disponible con el complemento Game State ($79/mes) o el plan Enterprise. Las claves sin ninguno de los dos reciben 403 tier_restricted con addon: "game_state" en el cuerpo del error. Añade el complemento desde la página de facturación  en cualquier plan de pago (Hobby / Pro / Sharp); las claves Enterprise lo tienen incluido.

El estado de juego en vivo también se transmite a través del canal gamestate en SSE y WebSocket — consulta Streaming más abajo. (El acceso al streaming requiere el complemento WebSocket o Enterprise, además del requisito de Game State indicado anteriormente.)

Parámetros de RutaPermalink for this section

ParámetroTipoDescripción
sportstring(opcional) Un único deporte a devolver. Coincide con un deporte conocido del Atlas (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). Sin distinción entre mayúsculas y minúsculas. Un deporte desconocido devuelve un objeto data vacío — y también un deporte admitido que no tenga nada en directo en este momento; consulta la nota siguiente.

Un objeto data vacío no significa que el deporte no esté admitido. El game state se combina por deporte en una clave gamestate:{sport} con un TTL de 60 segundos (GAMESTATE_TTL, deliberadamente el doble de la ventana livestate de 30 segundos); si no hay nada en directo la clave expira y la respuesta es {"data":{}}, idéntica a la de un deporte no cubierto. Varios de los deportes anteriores son estacionales.

Usa GET /api/v1/sports para ver event_count y live_count por deporte. other es el bucket de reserva para deportes que Atlas no ha mapeado.

Sin el parámetro de ruta, devuelve todos los deportes con eventos en vivo.

Filtros de consultaPermalink for this section

Ambos se aplican a /api/v1/gamestate y /api/v1/gamestate/{sport}. Los valores no distinguen mayúsculas y se recortan. Si omites ambos, obtienes el conjunto por defecto: todo lo que está en vivo, más las filas finalizadas que siguen dentro de la ventana de arrastre.

ParámetroAceptadoDescripción
statusfinalDevuelve únicamente las filas finalizadas arrastradas — el reflejo de /events?status=final. Cualquier otro valor es un 400 validation_error (ver abajo). No existe el valor live: las filas en vivo son el conjunto por defecto, y ?is_live=true las pide por separado.
is_livetrue, falsetrue devuelve solo filas en vivo, lo que excluye todas las filas finalizadas. false devuelve solo filas no en vivo, que hoy son las filas finalizadas arrastradas. Cualquier otro valor — incluidos 1 y 0 — es un 400 validation_error.

Un valor no reconocido se rechaza, no se ignora. Tanto ?status=zzz como ?is_live=1 devuelven 400 con un cuerpo validation_error que nombra el conjunto aceptado, en lugar de devolver en silencio las filas sin filtrar o las del lado equivocado.

{ "error": { "code": "validation_error", "message": "is_live must be one of: true, false", "details": { "parameter": "is_live", "accepted": ["true", "false"] } } }

Antes ?is_live=1 se leía como false y devolvía exactamente las filas que quien llamaba intentaba excluir; ahora es un 400. Si envías 1 / 0, cambia a true / false.

Sobre de RespuestaPermalink for this section

{ "data": { "<sport>": { "<event_id>": { ...event state... } } }, "updated_at": "2026-04-23T23:55:01.234Z" }

Cada estado de evento se indexa por su event_id canónico dentro del contenedor de su deporte. updated_at es la hora del servidor en la que se construyó esta respuesta — úsala para evaluar la frescura si haces polling.

Campos del Estado del EventoPermalink for this section

Todos los campos son opcionales excepto donde se indique. La presencia varía según el deporte — los eventos de tenis incluyen sets_home / server; los eventos de hockey incluyen power_play; los eventos de fútbol incluyen corners_* / yellow_cards_*; etc.

Siempre presentesPermalink for this section

CampoTipoNotas
home_teamstringNombre normalizado del equipo local
away_teamstringNombre normalizado del equipo visitante
sportstringContenedor del deporte (baseball, soccer, …)
leaguestringIdentificador de liga del Atlas (p. ej. mlb, england_-_premier_league)
home_scoreintegerPuntuación actual del local en la unidad natural del deporte (carreras, puntos, goles, sets — el tenis usa la suma de puntos por set)
away_scoreintegerPuntuación actual del visitante
is_livebooleantrue en un evento en vivo. false en una fila finalizada arrastrada — consulta Eventos finalizados
primary_bookstringEl libro del que se seleccionaron las puntuaciones fusionadas. Respeta el consenso — los libros de mayor calidad ganan los empates
book_countintegerNúmero de sportsbooks que aportaron estado en vivo para este evento en el momento de la fusión

«Siempre presentes» significa siempre presentes en una fila en vivo. Una fila finalizada arrastrada se construye a partir del registro de finalización, no de una fusión en vivo, así que no lleva ni primary_book ni book_count, ni game_period / game_clock. Sus campos de puntuación también dependen del deporte: los deportes por sets llevan sets_home / sets_away en lugar de home_score / away_score. Comprueba status === "final" antes de leer cualquiera de estos campos. La forma terminal completa está en Eventos finalizados.

Temporales (la mayoría de deportes de equipo)Permalink for this section

CampoTipoEjemplo
game_periodstring"T5" (parte alta de la 5.ª entrada de MLB), "Q3" (Q3 de NBA), "2H" (2.ª parte de fútbol), "P2" (NHL), "S2" (2.º set de tenis), "FT" (tiempo completo)
game_clockstring"49:06" para deportes con cronómetro ascendente (fútbol), "5:42" para deportes con cuenta atrás (baloncesto, hockey)

SituacionalesPermalink for this section

CampoTipoNotas
possession"home" | "away"Qué equipo tiene la posesión (deportes de equipo) o está sirviendo (tenis)
server"home" | "away"Solo tenis — sacador actual
last_playstringDescripción de la última jugada específica del deporte cuando esté disponible
is_timeoutbooleantrue durante un tiempo muerto

Específicos por deportePermalink for this section

CampoDeporteTipo
fouls_home, fouls_awaybaloncesto, fútbolinteger
corners_home, corners_awayfútbolinteger
yellow_cards_home, yellow_cards_awayfútbolinteger
red_cards_home, red_cards_awayfútbolinteger
power_playhockey"home" | "away"
sets_home, sets_awaytenisinteger (sets ganados)
hits_home, hits_awaybéisbolinteger
home_pitcher, away_pitcherbéisbolstring — nombre visible del pitcher inicial
wickets_home, wickets_awaycricketinteger
overscricketfloat
batting_teamcricket"home" | "away"

FrescuraPermalink for this section

CampoTipoSignificado
staletrue (omitido en caso contrario)El TTL de la clave de livestate del primary_book cayó por debajo del umbral de frescura del agregador (~10s) en el momento de la fusión. Las puntuaciones son los últimos valores conocidos; el libro ha quedado en silencio. Considera que las puntuaciones y el periodo del evento podrían ir retrasados respecto al estado real del partido.
aggregator_staletrue (omitido en caso contrario)El propio agregador de SharpAPI no ha escrito un nuevo shard gamestate:{sport} para este deporte en más de ~30s. Señal más amplia que stale — indica un problema en el pipeline, no de un único libro. Si lo observas de forma persistente, contacta con soporte.

Ausencia = fresco. Tanto stale como aggregator_stale solo se escriben cuando son verdaderos, manteniendo el payload compacto. Si no los ves en un evento, los datos se consideran frescos.

Eventos finalizadosPermalink for this section

Antes, un encuentro desaparecía de /gamestate en el momento en que terminaba — el marcador final solo era visible para el cliente que casualmente hiciera polling durante el último ciclo en vivo. Ahora los eventos finalizados permanecen en este endpoint 15 minutos después de terminar como fila terminal, de modo que cualquier cliente con un intervalo razonable ve el resultado final al menos una vez.

La ventana es del lado servidor y actualmente es de 15 minutos (GAMESTATE_TERMINAL_CARRY_SECONDS, por defecto 900). Cuando expira, la fila desaparece definitivamente; el resultado ya cerrado sigue disponible en /api/v1/events?status=final, que es de donde procede la fila terminal.

Cómo es una fila terminalPermalink for this section

{ "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 }
CampoTipoNotas
status"final"Presente solo en una fila terminal. Las filas en vivo no llevan ninguna clave status, así que status === "final" es la comprobación fiable — no lo deduzcas de is_live.
completed_atstringISO 8601 — cuándo detectó SharpAPI la finalización, no el pitido final oficial de la liga. La ventana de arrastre se mide desde esta marca de tiempo.
score_type"points" | "sets"Qué campos llevan el resultado. "sets" para los deportes por sets (tennis, table_tennis, volleyball, badminton); "points" en todo lo demás.
winner"home" | "away" | "draw"Opcional — se omite cuando el resultado no puede determinarse; ver abajo. "draw" solo se produce en deportes donde un marcador igualado es un resultado real (soccer, hockey, football, rugby_union, rugby_league).
is_livefalseSiempre false de forma explícita, para que cualquier renderizado condicionado por is_live trate la fila como no en vivo.
stalefalseSiempre false de forma explícita. Es el único sitio donde stale aparece sin ser verdadero — la regla «ausencia = fresco» de arriba vale solo para filas en vivo.

Los marcadores finales usan los nombres de campo en vivo, según el deporte. Un deporte por puntos pone su cómputo en home_score / away_score. Un deporte por sets pone su recuento de sets en sets_home / sets_away y no lleva home_score / away_score — un final de tenis es 2-1 en sets, y ponerlo en home_score chocaría con los puntos de juego de la fila en vivo:

{ "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 puede faltar, y eso significa algo. Se omite cuando el resultado registrado no cierra el encuentro:

  • un marcador igualado en un deporte que no admite empate (baloncesto, balonmano, esports, béisbol…) — el partido fue a la prórroga, se suspendió, o el feed se cortó antes de la resolución;
  • un recuento de sets sub-terminal en un deporte por sets (quien lidera con menos de dos sets no puede haber ganado ningún formato);
  • un snapshot cuyas puntuaciones nunca llegaron, que se registra como 0-0. Por eso un 0-0 sin winner no es lo mismo que un empate a cero real — el real lleva "winner": "draw".

Trata un winner ausente como «sin resolver», no como un empate. Si estás calificando o liquidando algo, salta estas filas.

Qué campos no lleva una fila terminalPermalink for this section

Una fila terminal se proyecta desde el registro de finalización, no desde una fusión en vivo entre libros, así que estos campos en vivo simplemente no están: primary_book, book_count, game_period, game_clock y todos los campos situacionales (possession, corners_*, power_play, in_play, la familia de probabilidad de victoria de tenis, etc.). Léelos solo después de comprobar que la fila no es status: "final".

Filtrar eventos finalizadosPermalink for this section

Lo que quieresPetición
Solo filas en vivo (comportamiento previo al arrastre)?is_live=true
Solo filas finalizadas?status=final
Ambas (por defecto)ningún parámetro
# Solo los finales de los últimos 15 minutos, un deporte curl "https://api.sharpapi.io/api/v1/gamestate/soccer?status=final" \ -H "X-API-Key: YOUR_API_KEY" # Exactamente lo que obtenías antes de que existiera el arrastre curl "https://api.sharpapi.io/api/v1/gamestate?is_live=true" \ -H "X-API-Key: YOUR_API_KEY"

Si un evento finalizado sigue en vivo en algún sitio, gana la fila en vivo. Un event_id que aparece tanto en el conjunto en vivo como en el arrastrado se devuelve una sola vez, como fila en vivo. Un libro que siga cotizando el partido lo mantiene en vivo hasta que todos lo hayan soltado.

Ejemplos de SolicitudesPermalink for this section

# Todos los eventos en vivo de cada deporte curl "https://api.sharpapi.io/api/v1/gamestate" \ -H "X-API-Key: YOUR_API_KEY" # Un solo deporte curl "https://api.sharpapi.io/api/v1/gamestate/soccer" \ -H "X-API-Key: YOUR_API_KEY"

Ejemplo de RespuestaPermalink for this section

{ "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" }

La tercera fila es una fila terminal: el partido terminó unos seis minutos antes de que se construyera esta respuesta, así que se arrastra con status: "final" y su marcador final, y no lleva primary_book, book_count, game_period ni game_clock.

Modelo de Fusión Entre LibrosPermalink for this section

Los campos de cada evento se fusionan a partir del snapshot de livestate de cada sportsbook mediante un algoritmo de tres clases:

  • Clase A — Puntuaciones se eligen por consenso: entre los libros con el rango de periodo más avanzado, gana la puntuación total más alta con ≥2 respaldos. Una puntuación atípica reportada por un único libro (común en los primeros segundos de un cambio de marcador, o por un adaptador con mal comportamiento) se rechaza.
  • Clase B — Campos temporales (game_period, game_clock) se seleccionan según la dirección del reloj específica del deporte — cuenta atrás frente a cuenta ascendente — y el rango del periodo.
  • Clase C — Campos situacionales (possession, corners_*, etc.) se rellenan por prioridad a partir de una clasificación fija de libros.

primary_book indica de qué libro provienen las puntuaciones ganadoras. book_count es el número total de libros que aportaron cualquier estado para el evento en el momento de la fusión.

StreamingPermalink for this section

Cada actualización en vivo que el endpoint REST mostraría en el siguiente poll también dispara un evento gamestate:update en los canales de streaming:

Los clientes de streaming reciben un gamestate:snapshot inicial con el estado actual completo (una lista plana de filas de eventos), después eventos gamestate:update que llevan las filas modificadas conforme se fusionan, y eventos gamestate:removed cuando los eventos salen del conjunto en vivo.

Un evento finalizado se queda en el slate transmitido como su fila status: "final" durante la ventana descrita en Eventos finalizados, y se anuncia una vez con un frame gamestate:final.

Avisos de evento finalizadoPermalink for this section

Cuando un evento termina, cada transporte envía un único frame gamestate:final para él. Lleva la fila terminal del evento — la misma forma que la fila REST, más su 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 (el mismo envoltorio {"data": [...]} que 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}]}
  • Mismo acceso y mismos filtros que gamestate:update. Si recibes gamestate:update, recibes gamestate:final, y tus filtros sport/league se aplican. Sin acceso a game state no recibes ninguno de los dos.
  • WebSocket lo reproduce. Igual que gamestate:update y gamestate:removed, queda en el buffer de replay y se reenvía en una reconexión con from_seq. Los frames de gamestate por SSE no son reanudables.
  • Es un aviso, no la única entrega. La misma fila está también en el slate (gamestate:snapshot y gamestate:update) durante toda la ventana.

“Una vez por evento” puede repetirse ocasionalmente — deduplica por (event_id, completed_at). Puedes recibir gamestate:final más de una vez para el mismo evento. event_id y completed_at son idénticos en cada repetición: descarta un frame cuyo par ya procesaste. Siempre es seguro, porque la fila sigue en el slate.

Rate Limits y CuotasPermalink for this section

Los rate limits siguen tu plan base, no el complemento — las claves Hobby mantienen sus 120 req/min, Pro mantiene 300, Sharp mantiene 1.000, Enterprise personalizado. Consulta Precios . /gamestate cuenta igual que otras llamadas REST en la cuota por minuto.

Resolución de ProblemasPermalink for this section

  • 403 tier_restricted con addon: "game_state" — tu clave no tiene el complemento Game State. Añádelo desde Facturación  por $79/mes, o actualiza a Enterprise.
  • 400 validation_error con parameter: "status" — el único valor aceptado de ?status= es final. No existe el valor live; las filas en vivo son el conjunto por defecto, o pídelas por separado con ?is_live=true.
  • 400 validation_error con parameter: "is_live" — ?is_live= solo acepta true / false. 1 y 0 se rechazan; antes se leían como false y devolvían lo contrario de lo pedido.
  • Una fila con status: "final" y is_live: false — no es un fallo. Es un evento finalizado dentro de su ventana de 15 minutos. Filtra con ?is_live=true para el comportamiento anterior.
  • Faltan primary_book / book_count — estás leyendo una fila terminal. Comprueba status antes de leer campos que solo existen en vivo.
  • Objeto data vacío — no hay eventos en vivo en el ámbito. Esto es habitual entre eventos de calendarios deportivos más pequeños. Vuelve a hacer polling.
  • Eventos con stale: true — el libro principal ha quedado en silencio. Las puntuaciones pueden ir con retraso; considera filtrarlos o mostrar un indicador en la interfaz.
  • Eventos con aggregator_stale: true — el agregador de SharpAPI no ha refrescado ese deporte en >30s. Si es persistente, contacta con soporte; un breve aumento puede ocurrir durante los redespliegues.
  • El mismo partido aparece dos veces bajo event_ids diferentes — limitación conocida en algunas ligas regionales donde los sportsbooks usan nomenclatura de liga inconsistente. Usa (home_team, away_team) como clave secundaria de deduplicación en el cliente hasta que se cierren los huecos de alias restantes.
Last updated on