Skip to main content
API de cuotas en vivo

API de cuotas en vivo: cuotas en tiempo real por WebSocket

Una API de cuotas en vivo envía a tu código la cuota de una casa de apuestas en el momento en que cambia. Odds-API.io transmite cuotas prepartido y en vivo de 450+ casas de apuestas en 34 deportes por una sola conexión WebSocket, con marcadores en vivo, estado del partido y reloj del partido en el mismo stream.

El streaming por WebSocket es un complemento para cualquier plan de pago. REST está incluido en todos los planes.

Casas de apuestas
450+
Deportes
34
Latencia
<150ms
Ventana de reenvío
24h

Por qué WebSocket en lugar de polling

Con polling pides las cuotas cada cierto tiempo y casi siempre recibes datos sin cambios. Un WebSocket se mantiene abierto y solo transporta los precios que se movieron, así que ves cada cambio antes y transfieres muchos menos datos.

Por qué WebSocket en lugar de polling
CaracterísticaPolling RESTWebSocket
Cómo te llegan los cambiosVuelves a preguntar cada cierto tiempoSe envían en cuanto cambia un precio
LatenciaTu intervalo de polling más la petición<150ms
ConexionesUna petición HTTP nueva en cada consultaUna conexión que se mantiene abierta
Mercados retiradosHay que volver a leer el evento para detectarlosMensajes explícitos deleted y no_markets
Ideal paraScripts, cargas históricas y consultas rápidasApps de apuestas en vivo, modelos in-play y escáneres de arbitraje

Conéctate en pocas líneas

Abre wss://api.odds-api.io/v3/ws con tu API key y los mercados que quieres. El primer mensaje es una bienvenida que confirma tus filtros y, a partir de ahí, las cuotas llegan a medida que cambian.

Node.js
// npm i ws
import WebSocket from 'ws'

const API = 'https://api.odds-api.io/v3'
const apiKey = process.env.ODDS_API_KEY
const markets = 'ML,Spread,Totals'
const eventIds = '63017989,63017990' // up to 10, for example from GET /v3/events/live
const bookmakers = 'Bet365,Unibet' // the bookmakers selected on your account
const board = new Map()
let lastSeq = 0
let needsRebuild = false

function connect() {
  const params = new URLSearchParams({ apiKey, markets, eventIds, channels: 'odds,scores,status' })
  if (lastSeq) params.set('lastSeq', String(lastSeq))

  const ws = new WebSocket(`wss://api.odds-api.io/v3/ws?${params}`)

  ws.on('message', (raw) => {
    const msg = JSON.parse(raw)
    if (msg.type === 'resync_required') {
      needsRebuild = true
      return ws.close()
    }
    if (msg.seq > lastSeq) lastSeq = msg.seq
    const key = `${msg.id}:${msg.bookie}`

    if (msg.type === 'created' || msg.type === 'updated') board.set(key, msg.markets)
    if (msg.type === 'deleted' || msg.type === 'no_markets') board.delete(key)
    if (msg.type === 'score') console.log(msg.id, msg.scores.home, msg.scores.away)
  })

  ws.on('error', (err) => console.error('WebSocket error:', err.message || err.code))

  ws.on('close', async () => {
    if (needsRebuild) lastSeq = await rebuildFromRest()
    setTimeout(connect, 2000)
  })
}

// Replay was not possible: reload the subscription from REST and resume from its seq
async function rebuildFromRest() {
  for (let attempt = 1; ; attempt++) {
    try {
      const params = new URLSearchParams({ apiKey, eventIds, bookmakers, markets, includeSeq: 'true' })
      const res = await fetch(`${API}/odds/multi?${params}`)
      const seq = Number(res.headers.get('X-OddsAPI-Seq'))
      if (!res.ok || !(seq > 0)) throw new Error(`snapshot not usable: ${res.status}`)
      const events = await res.json()
      board.clear()
      for (const event of events) {
        for (const [bookie, odds] of Object.entries(event.bookmakers)) board.set(`${event.id}:${bookie}`, odds)
      }
      needsRebuild = false
      return seq
    } catch (err) {
      console.error(`${err.message}, retry ${attempt}`)
      await new Promise((resolve) => setTimeout(resolve, Math.min(30000, 1000 * 2 ** attempt)))
    }
  }
}

connect()
Python
# pip install websockets
import asyncio
import json
import os
from urllib.parse import urlencode

import websockets

params = urlencode({
    "apiKey": os.environ["ODDS_API_KEY"],
    "markets": "ML,Spread,Totals",
    "leagues": "england-premier-league",
    "status": "live",
})

async def main():
    async with websockets.connect(f"wss://api.odds-api.io/v3/ws?{params}") as ws:
        async for message in ws:
            msg = json.loads(message)
            if msg["type"] == "updated":
                for market in msg["markets"]:
                    print(msg["id"], msg["bookie"], market["name"], market["odds"][0])

asyncio.run(main())

La compresión es obligatoria

Tu cliente debe ofrecer permessage-deflate al conectarse. Reduce el ancho de banda de 3 a 5 veces, y una conexión sin ella se rechaza con HTTP 426. La mayoría de las librerías lo gestionan por ti:

La compresión es obligatoria
PlataformaLibreríaCompresión
Node.jswsActivada por defecto
NavegadorWebSocket nativoActivada por defecto
PythonwebsocketsActivada por defecto
Pythonwebsocket-clientNo compatible, usa websockets
PHPamphp/websocket-clientActivada por defecto (requiere zlib)
PHPtextalk/websocketNo compatible, usa amphp/websocket-client
Gogorilla/websocketConfigura EnableCompression = true
.NETClientWebSocketConfigura DangerousDeflateOptions (.NET 6+)

Tipos de mensaje

Cada mensaje es un objeto JSON con un type. Los mensajes de cuotas usan el mismo formato que el endpoint /v3/odds, así que un solo parser sirve para ambos.

created
Una casa de apuestas añadió un evento al que estás suscrito
updated
Las cuotas cambiaron. El mensaje lleva el conjunto actual completo de tus mercados para ese evento y esa casa
deleted
El evento se eliminó o su casa de apuestas dejó de estar disponible
no_markets
El evento sigue publicado, pero ninguno de tus mercados está abierto
score
El marcador en vivo cambió (canal scores)
status
El evento pasó a pendiente, en vivo, liquidado o cancelado, con el marcador actual (canal status)
clock
Una instantánea del reloj del partido aproximadamente cada 30 segundos para cada evento en vivo con el reloj en marcha

Reemplaza, nunca combines

Un mercado suspendido no aparece en el siguiente mensaje updated, en lugar de venir marcado. Sobrescribe con cada mensaje lo que tienes para ese evento y esa casa, y una cuota retirada desaparece de tu app por sí sola.

updated
{
  "type": "updated",
  "seq": 482917,
  "timestamp": 1723992773,
  "id": "63017989",
  "bookie": "Bet365",
  "markets": [
    {
      "name": "ML",
      "updatedAt": "2026-11-08T15:42:10Z",
      "odds": [{ "home": "1.85", "draw": "3.25", "away": "2.10" }]
    },
    {
      "name": "Totals",
      "updatedAt": "2026-11-08T15:42:10Z",
      "odds": [{ "hdp": 2.5, "over": "1.85", "under": "2.10" }]
    }
  ]
}

Canales y filtros

Elige los streams que quieres con channels y acótalos con parámetros de consulta para recibir solo los eventos que muestras. No puedes combinar leagues y eventIds.

Canales

odds
El canal por defecto. created, updated, deleted y no_markets para las casas de apuestas seleccionadas en tu cuenta
scores
Mensajes score en cada cambio de marcador, más instantáneas clock
status
Mensajes status para pendiente, en vivo, liquidado y cancelado, más instantáneas clock

Filtros

markets
Obligatorio para el canal odds. Hasta 40 nombres de mercado, como ML,Spread,Totals
sport
Hasta 10 deportes, como football,basketball
leagues
Hasta 20 slugs de liga, como england-premier-league
eventIds
Hasta 50 IDs de evento
status
live solo para en vivo, prematch solo para prepartido

channels es una lista de permitidos. Envía channels=odds,scores,status para mantener las cuotas junto a los marcadores; channels=scores,status por sí solo deja de enviar las cuotas.

Reconecta sin huecos

Las redes se caen. Cada mensaje de cuotas lleva un número seq, y el feed lo usa para ponerte al día tras una reconexión en lugar de obligarte a empezar de cero.

  1. 1

    Guarda el último seq

    Almacena el seq más alto que hayas recibido. Los seq solo aumentan dentro de una conexión, pero se comparten entre todos los suscriptores, así que un salto no es un hueco.

  2. 2

    Reconecta con lastSeq

    Añade lastSeq a la URL de conexión. Recibes el estado más reciente de cada evento y casa de apuestas que cambió mientras no estabas, y después continúa el stream en vivo.

  3. 3

    Sabe cuándo estás en vivo

    Añade replayComplete=true para recibir un mensaje replay_complete cuando termine la puesta al día. Los datos de reenvío se conservan hasta 24 horas.

  4. 4

    Reconstruye si se te pide

    Si no es posible un reenvío limpio, recibes resync_required. Obtén una instantánea de /v3/odds con includeSeq=true y reconecta con el seq de la cabecera X-OddsAPI-Seq.

Cada API key admite dos conexiones a la vez, por ejemplo una principal y otra de reserva. Hay más disponibles bajo petición.

Marcadores en vivo y reloj del partido

Las cuotas en vivo tienen más sentido junto al marcador. Suscríbete a los canales scores y status en la misma conexión, o consulta el estado actual por REST.

GET /v3/events/live
Todos los eventos en juego ahora mismo, con el marcador y un objeto clock. Filtra por deporte
clock
minute, playedSeconds, period, running y statusDetail, más el tiempo añadido en fútbol y quién saca en tenis
Marcadores por periodo
Partes, cuartos, sets, prórroga y penaltis con un mismo conjunto de claves para todos los deportes
Reloj en marcha
Cuenta hacia delante desde playedSeconds mientras running sea true, y reajusta al valor del servidor en cada instantánea

REST como alternativa: /odds y /odds/updated

Todos los planes incluyen la API REST, que sirve las mismas cuotas en vivo. Úsala para instantáneas o haz polling donde un socket no encaje.

GET /v3/odds
Una instantánea completa de un evento en las casas de apuestas que elijas. /v3/odds/multi admite hasta 10 eventos en una petición
GET /v3/odds/updated
Un delta real: solo los mercados que cambiaron desde since, que puede tener como máximo 90 segundos de antigüedad. Pasa la cabecera de respuesta X-Next-Since como since en la siguiente llamada para continuar sin huecos
includeSeq=true
Añade la posición actual del stream como X-OddsAPI-Seq, para pasar de una instantánea REST al WebSocket sin perder ninguna actualización

Un delta no puede indicarte que un mercado se retiró, así que un cliente que hace polling debería volver a leer los eventos con /v3/odds de vez en cuando. El WebSocket envía deleted y no_markets en su lugar.

Qué cubre el feed de cuotas en vivo

Casas de apuestas
450+, entre ellas Bet365, DraftKings, FanDuel, BetMGM, Betfair Exchange y casas regionales de Europa, Asia, Australia y Latinoamérica
Deportes
34, entre ellos fútbol, baloncesto, tenis, fútbol americano, béisbol, hockey sobre hielo, críquet y esports
En vivo
Cuotas en vivo de todas las casas de apuestas que ofrecen apuestas en directo, actualizadas aproximadamente cada segundo
Mercados
Moneyline, hándicaps, totales, hándicap asiático, córners, tarjetas y props de jugadores, según la casa de apuestas
Latencia
<150ms para las actualizaciones enviadas por WebSocket
Formato
El mismo JSON que /v3/odds, por WebSocket y REST

Planes y acceso a WebSocket

Todos los planes de pago incluyen la API REST con cuotas en vivo. El streaming por WebSocket es un complemento que puedes añadir a cualquier plan.

  • El complemento cuesta otra vez el precio del plan, es decir, 2× el precio del plan en total, mensual o anual
  • El stream incluye todas las casas de apuestas seleccionadas en tu cuenta, así que el plan decide cuántas casas recibes
  • Marcadores, estado y reloj del partido llegan por la misma conexión
  • Cancela cualquier mes
Comparar planes →

Preguntas frecuentes sobre la API de cuotas en vivo

¿Qué es una API de cuotas en vivo?

Una API de cuotas en vivo da a tu código las cuotas actuales de los eventos en juego, en JSON, y las mantiene al día a medida que las casas de apuestas mueven sus precios. Odds-API.io transmite cuotas en vivo y prepartido de 450+ casas de apuestas por WebSocket, y sirve las mismas cuotas por REST.

¿Con qué rapidez llegan las cuotas en vivo?

Las actualizaciones por WebSocket llegan con una latencia de <150ms, y las cuotas en vivo se actualizan aproximadamente cada segundo. Hacer polling por REST suma tu intervalo de consulta, por eso recomendamos WebSocket para apps en vivo.

¿Qué pasa si se cae mi conexión WebSocket?

Reconecta con el parámetro lastSeq igual al último seq que recibiste. El servidor reenvía el estado más reciente de todo lo que cambió mientras no estabas, para cortes de hasta 24 horas, y luego continúa en vivo. Si no es posible un reenvío limpio, envía resync_required y reconstruyes a partir de una instantánea REST.

¿Qué clientes WebSocket puedo usar?

Cualquier cliente compatible con la compresión permessage-deflate, que es obligatoria. ws de Node.js, WebSocket del navegador y websockets de Python la activan por defecto. websocket-client de Python y textalk/websocket de PHP no la admiten y se rechazan con HTTP 426. En Go, activa EnableCompression en gorilla/websocket.

¿El feed incluye marcadores en vivo y el reloj del partido?

Sí. Añade channels=odds,scores,status a la URL de conexión para recibir cambios de marcador, cambios de estado y una instantánea del reloj del partido aproximadamente cada 30 segundos junto a las cuotas. GET /v3/events/live devuelve los mismos marcadores y reloj por REST.

¿Cómo obtengo acceso a WebSocket?

Elige un plan de pago y añade el streaming por WebSocket. Cuesta otra vez el precio del plan y transmite todas las casas de apuestas seleccionadas en tu cuenta, con dos conexiones por API key. La API REST, incluidas las cuotas en vivo y /v3/odds/updated, forma parte de todos los planes.

Recibe cuotas en vivo hoy

Elige un plan, añade el streaming por WebSocket y abre una sola conexión para recibir cuotas en vivo de las 450+ casas de apuestas del feed.

Cancela cuando quieras · El mismo JSON por REST y WebSocket