Qué es la API de cuotas de Pinnacle

La API de cuotas de Pinnacle que documenta esta guía es un feed de terceros que sirve los precios en vivo y prepartido de Pinnacle como JSON por REST, envía caídas de precio por server-sent events y, como complemento de pago, retransmite frames WebSocket sin procesar. Es un producto de datos: lee precios, no hace apuestas ni toca una cuenta de Pinnacle.

No es la API de la propia Pinnacle. Pinnacle cerró el registro público en su API el 23 de julio de 2025 y ahora concede acceso caso por caso a socios comerciales y a algunos usos individuales y de investigación. La página alternativa a la API de Pinnacle en pnclFEED explica qué cambió y qué vía encaja con cada uso; esta guía cubre el feed.

Todo lo que sigue procede de la documentación publicada del feed, comprobada el 26 de septiembre de 2026. Donde la documentación se contradice, la guía lo dice. Nada de esto es un resultado medido salvo que la sección diga cómo se midió.

Autenticación y la primera solicitud

Toda solicitud lleva tu clave en el encabezado x-portal-apikey. Los endpoints de streaming también la aceptan como parámetro de consulta ?key= para clientes que no pueden fijar encabezados, pero una clave en la URL acaba en los registros, así que mantén la forma de encabezado en los servidores. La URL base viene de tu cuenta y de la documentación; los ejemplos de esta página la leen de una variable de entorno.

Al registrarse se emite una clave gratuita, sin tarjeta y sin caducidad, que sirve además como inicio de sesión de la cuenta. Permite 20 solicitudes por minuto y 100 al día en REST, suficiente para validar un parser, ejecutar todos los ejemplos de esta página y sondear un tablero cada quince minutos durante todo el día. Una demo completa de tres días, con SSE, WebSocket sin procesar y 10 solicitudes por segundo, está disponible bajo petición (comprobado el 26 de septiembre de 2026).

export ODDS_API_KEY="your key"
export ODDS_BASE_URL="https://..."   # from your account

curl -s -H "x-portal-apikey: $ODDS_API_KEY" \
  "$ODDS_BASE_URL/kit/v1/markets?sport_id=1" \
  | jq '{last, events: (.events | length)}'

Un 200 con un cursor last y un recuento de eventos distinto de cero significa que la clave funciona y el tablero de fútbol está activo. El quickstart de curl y jq en pnclFEED lo extiende a un listado de una línea por partido, y la página clave de la API de Pinnacle en pnclODDS cubre el registro y la primera solicitud paso a paso.

Endpoints

El feed tiene una superficie pequeña. Cuatro rutas REST, dos streams, una comprobación de salud y un WebSocket sin procesar opcional cubren todo lo que describe la documentación.

RutaPropósitoNotas
GET /kit/v1/markets?sport_id=NEl tablero en vivo de un deporteDevuelve un objeto con events y un cursor last. Envía last de vuelta como since para recibir solo lo que cambió.
GET /kit/v1/prematch/fixtures?sport_id=NEl tablero prepartido de un deporteFixtures con sus mercados. Una ruta de nombre parecido, más abajo, recibe un evento en lugar de un deporte.
GET /kit/v1/prematch/markets?event_id=NLos mercados de un evento prepartidoConfundirla con la ruta de fixtures hace que un feed válido parezca vacío o devuelve un error de parámetro.
GET /api/drops?mode=live|prematchCaídas recientes, bajo demandamin_drop_pct fija el tamaño del movimiento y max_age_sec la ventana. Conserva aproximadamente las últimas tres horas.
GET /odds-drop?min_drop=NAlertas de caída en vivo, en streamServer-sent events. Una conexión por clave y stream; una segunda conexión en vivo cierra la primera.
GET /odds-drop-prematch?min_drop=NAlertas de caída prepartido, en streamAñade recheck=N para retener cada caída N segundos y enviarla solo si el precio no ha rebotado.
Endpoint de saludCuánto hace que el feed en vivo escribió por última vezIndicado en la documentación; consúltalo junto a un stream silencioso para distinguir un mercado tranquilo de un feed roto.
WebSocket en brutoCada frame, sin procesarUn complemento de pago en los tres planes REST. El estado del mercado lo reconstruyes tú.

Los ids de deporte son propios del feed, del 1 al 13, y cada respuesta publica junto a ellos el id interno de Pinnacle. Fútbol es 1, tenis 2, baloncesto 3, hockey 4, fútbol americano 5, béisbol 6, rugby 7, MMA 8, boxeo 9, voleibol y balonmano 10, esports 11, golf 12 y críquet 13. Mapéalos una vez en el borde de tu código y nunca mezcles los dos espacios de nombres.

La forma de la respuesta

La respuesta de un tablero es un objeto, no una lista. events contiene un registro por partido y last es el cursor de la siguiente llamada. Cada evento lleva sus equipos, su hora de inicio, el id de deporte del feed y el de Pinnacle, y sus mercados agrupados bajo periods. periods.num_0 es el partido completo; las demás claves son mitades, cuartos, sets o mapas según el deporte.

{
  "last": 141,
  "events": [
    {
      "home": "Home FC",
      "away": "Away FC",
      "periods": {
        "num_0": {
          "money_line": { "home": 2.10, "draw": 3.40, "away": 3.60 },
          "spreads": { "-0.5": { "home": 2.05, "away": 1.85 } },
          "totals":  { "2.5":  { "over": 1.95, "under": 1.90 } },
          "team_total": { "home": { "points": 1.5, "over": 2.00, "under": 1.85 } }
        }
      }
    }
  ]
}

Los precios son cuotas decimales. Money line, spreads, totales y totales por equipo tienen formas distintas, y un total del partido completo y un total de la primera mitad son mercados distintos aunque compartan una línea. La presencia varía por evento y período: un mercado puede estar ausente, vacío o suspendido, y tu modelo debe mantener esos tres estados separados. Nunca sustituyas un precio ausente por cero.

Los mercados especiales son una expansión opcional mediante include_specials. Llegan como filas extra enlazadas por parent_id y estructuras special_markets separadas, aumentan el payload, y no todos sus cambios de precio siguen el mismo comportamiento de alerta que los mercados principales. Mantén separados el parser de mercado completo y el parser de alertas de caída. La guía de normalización muestra las claves de evento, período, mercado, línea, selección y timestamp que hacen comparables los registros de este feed con cualquier otro.

Sondeo con el cursor since

El cursor es todo el truco para sondear este feed a bajo coste. La primera llamada de un deporte devuelve el tablero completo y un valor last; cada llamada siguiente envía ese valor como since y recibe solo los eventos que cambiaron. Guarda el cursor con tu snapshot para que un reinicio reanude en lugar de volver a descargar, y trata el tablero completo como la forma de resincronizar, no como la forma de sondear.

import os, time, requests

BASE = os.environ["ODDS_BASE_URL"]
HEADERS = {"x-portal-apikey": os.environ["ODDS_API_KEY"]}

def poll(sport_id=1, interval=900):
    since = None
    while True:
        params = {"sport_id": sport_id}
        if since is not None:
            params["since"] = since
        r = requests.get(f"{BASE}/kit/v1/markets", headers=HEADERS, params=params, timeout=30)
        if r.status_code == 429:
            time.sleep(int(r.headers.get("Retry-After", "5")))
            continue
        r.raise_for_status()
        body = r.json()
        since = body["last"]
        for event in body["events"]:
            line = event["periods"].get("num_0", {}).get("money_line")
            if line:
                print(event["home"], event["away"], line)
        time.sleep(interval)

poll()

Quince minutos entre llamadas mantienen un tablero dentro de las 100 solicitudes al día de la clave gratuita. En un nivel de pago, el intervalo es una decisión de presupuesto: la página de planificación de capacidad en pnclENGINE convierte deportes, fases y un intervalo en solicitudes por segundo y por día, y la calculadora de coste de la API de aquí convierte la misma programación en una cifra mensual. El quickstart de Python y el quickstart de Node.js en pnclFEED traen versiones más largas de este bucle.

Alertas de caída por SSE

Los streams envían las caídas de precio en el momento en que el feed las detecta. Un GET a /odds-drop para mercados en vivo o /odds-drop-prematch para los prepartido, con la clave y Accept: text/event-stream, devuelve un 200 que nunca termina. Fija min_drop tú mismo: el ejemplo documentado usa 5 y el mínimo es 1, y el valor por defecto está documentado de forma inconsistente, así que fiarse de él es un error.

Cada mensaje es una línea data: con JSON. La primera es un objeto de control, {"type": "connected"}; después, cada lote de alertas es un array. Un objeto más adelante en el stream es otro mensaje de control, como plan_lacks_sse cuando el plan de la clave no tiene stream o rate_limited cuando un cliente reconecta en bucle cerrado, que además lleva un encabezado Retry-After: 60.

data: {"type": "connected", "id": "d648beb8-..."}
data: [{"home": "Sunshine Coast Phoenix", "away": "Cairns Dolphins",
        "league": "Australia - NBL1 Women", "sport": "Basketball",
        "sect": "Moneyline", "outcome": "Home", "period": 4,
        "from_price": 2.86, "to_price": 2.7, "nvp": 3.04,
        "id": 1629729400, "alerted": 1777625196}]

Cada alerta identifica la línea con sport, league, sect, outcome y period, lleva el precio antes y después como from_price y to_price, añade nvp, el precio justo sin margen tras el movimiento, y marca el alerta en segundos Unix como alerted. El porcentaje lo calculas tú. Los alertas perdidos no se reenvían: rellena un hueco desde /api/drops, cuyos registros usan nombres de campo distintos (from, to, drop_pct, market, side), así que comprueba cuál lee cada ejemplo.

// Node.js 18+: read the live drop stream and reconnect with backoff.
const BASE = process.env.ODDS_BASE_URL;
const KEY = process.env.ODDS_API_KEY;

async function listen(minDrop = 5) {
  let delay = 1000;
  for (;;) {
    try {
      const res = await fetch(`${BASE}/odds-drop?min_drop=${minDrop}`, {
        headers: { "x-portal-apikey": KEY, accept: "text/event-stream" },
      });
      if (res.status === 429) {
        await new Promise((r) => setTimeout(r, Number(res.headers.get("retry-after") || 60) * 1000));
        continue;
      }
      delay = 1000;
      let buffer = "";
      for await (const chunk of res.body) {
        buffer += Buffer.from(chunk).toString("utf8");
        let end;
        while ((end = buffer.indexOf("\n\n")) >= 0) {
          const frame = buffer.slice(0, end);
          buffer = buffer.slice(end + 2);
          const line = frame.split("\n").find((l) => l.startsWith("data:"));
          if (!line) continue;
          const payload = JSON.parse(line.slice(5));
          if (Array.isArray(payload)) payload.forEach(handle);
          else if (payload.type !== "connected") console.warn("control", payload);
        }
      }
    } catch (err) {
      console.error("stream closed", err.message);
    }
    await new Promise((r) => setTimeout(r, delay + Math.random() * delay));
    delay = Math.min(delay * 2, 60000);
  }
}

function handle(alert) {
  const pct = ((alert.from_price - alert.to_price) / alert.from_price) * 100;
  console.log(alert.sport, alert.home, alert.away, alert.sect, alert.outcome, pct.toFixed(1) + "%");
}

listen();

Los umbrales, los conjuntos de alertas vistos y las ventanas de deduplicación van junto al proceso lector, no en una pestaña del navegador. Las páginas de configuración SSE y de deduplicación y cooldowns en pnclPULSE cubren el lado de las reglas, y la página de alertas en Telegram en pnclDATA muestra el mismo stream entregado en un teléfono.

Límites de tasa, errores y backoff

Todo plan tiene un techo por segundo en REST, y la clave gratuita tiene además techos por minuto y por día. Superar un límite devuelve 429 Too Many Requests, normalmente con un encabezado Retry-After en segundos. Ese encabezado manda: espera exactamente ese tiempo y continúa. Un recuento creciente de 429 es el aviso temprano de que la carga estable se acerca al techo.

EstadoSignificadoQué hacer
401Falta el encabezado o la clave es incorrectaCorrige la clave; no reintentes
403La clave es válida, pero el plan no incluye lo que pedisteRevisa el plan, o el derecho al stream
429Por encima de la cuota por segundo, por minuto o por díaEspera el Retry-After y luego reintenta con jitter
5xxEl feed tuvo un problemaReintenta unas pocas veces con esperas crecientes y luego da el ciclo por fallido

Mantén el uso estable por debajo de aproximadamente el 70 por ciento de la cuota por segundo para que los reintentos, las herramientas administrativas y un segundo entorno quepan en el resto. Comparte el estado de backoff entre los workers; diez procesos retrocediendo educadamente siguen sumando diez veces las solicitudes. La página de límites de tasa y el runbook de errores en pnclENGINE van estado por estado.

Planes y precios

Precios publicados en dólares, facturación mensual, comprobados el 26 de septiembre de 2026. El checkout muestra las cifras que se aplican el día de la compra, e impuestos, comisiones y prorrateo no están publicados.

PlanMensualAsignación RESTAlertas SSEWebSocket en bruto
Clave gratuita$020 por minuto, 100 al díaNoNo
Alertas de caída SSE$9920 por minuto, 100 por hora, 100 al díaIncluidoNo elegible
Snapshots REST$9910 solicitudes por segundoNo+99 $ por 30 días
REST + SSE$14910 solicitudes por segundoIncluido+99 $ por 30 días
REST de alto volumen$22930 solicitudes por segundoIncluido+99 $ por 30 días

Existen períodos prepagados: tres meses por 249 $ en los dos planes de 99 $, 379 $ en REST + SSE y 599 $ en REST de alto volumen, y seis meses por 449 $ y 679 $ solo en los dos planes REST. El complemento WebSocket sin procesar se cotiza en bloques de 30, 90 y 180 días a 99 $, 297 $ y 594 $. Un equivalente mensual es una cifra de comparación, no una factura mensual. La página precios de la API de Pinnacle en pnclODDS tiene el estimador y las decisiones de ajuste de plan, y el quickstart de precios en pnclFEED lee las mismas cifras desde el lado del desarrollador.

Cobertura

La documentación enumera fútbol, tenis, baloncesto, hockey, fútbol americano, béisbol, rugby, MMA, boxeo, voleibol y balonmano, esports, golf y críquet. En vivo y prepartido están ambos documentados, y la disponibilidad de eventos varía por deporte y por día. Los mercados REST son money line, spreads, totales y totales por equipo bajo períodos. Un deporte listado no garantiza todas las competiciones ni mercados, así que ejecuta una muestra representativa con la clave gratuita mientras los eventos que te importan estén en el tablero.

La cadencia de actualización se indica de forma inconsistente: la página de inicio habla de cinco segundos para el prepartido y la referencia describe treinta. Ninguna es una latencia medida de extremo a extremo. No hay archivo histórico; el feed es un servicio de datos actuales, y registrar el histórico es una tarea operativa tuya, con sus propios derechos de almacenamiento. La página de cobertura en pnclODDS mantiene la tabla de evidencias, y la página API de cuotas de esports en pnclHUB muestra un deporte documentado en la práctica.

Medir la latencia y la frescura por tu cuenta

Esta guía no publica ninguna cifra de latencia porque ninguna se ha medido bajo un protocolo declarado. La forma honesta de obtener una es medirla: registra el timestamp del propio feed junto a tu hora de recepción para cada alerta o actualización durante al menos una semana, alinea antes los relojes, compara mercados en lugar de eventos e informa de la distribución, no de una media.

El protocolo de benchmark define las cargas de trabajo, la alineación de relojes y el formato del informe, y la página de investigación enumera los relojes que necesita cada resultado. Cuando se publique una medición en esta red, aparecerá con ese método y su fecha.

Referencia de campos

Los campos que un parser encuentra con más frecuencia, por endpoint, con los nombres que usa la documentación. Los tipos son los que muestran los ejemplos documentados; valídalos en lugar de darlos por supuestos.

CampoDóndeSignificado
eventsRespuestas de tableroArray de registros de eventos del deporte solicitado.
lastRespuestas de tableroCursor que se devuelve como since en la siguiente llamada.
home, awayEvento, alerta, caídaNombres de equipos o jugadores tal como los imprime el feed.
periodsEventoObjeto con claves num_0, num_1 y así sucesivamente; cada período contiene sus propios mercados.
money_linePeriodohome, draw (cuando el deporte tiene empate) y away como cuotas decimales.
spreadsPeriodoIndexado por la línea de hándicap; cada línea contiene los precios home y away.
totalsPeriodoIndexado por la línea de total; cada línea contiene over y under.
team_totalPeriodoTotales por equipo con una línea points y precios over y under.
parent_id, special_marketsEvento, con include_specialsEnlaza una fila de mercado especial con el evento al que pertenece.
typeMensaje de control SSEconnected al abrir; después, nombres de error como plan_lacks_sse o rate_limited.
sport, league, sect, outcome, periodAlerta SSELa línea que se movió: deporte, competición, sección de mercado, lado y número de período.
from_price, to_price, nvpAlerta SSEPrecio antes, precio después y el precio justo sin margen tras el movimiento.
id, alertedAlerta SSEId del evento y hora del alerta en segundos Unix; junto con los campos de la línea forman una clave de deduplicación.
market, side, from, to, drop_pct, nvpREST /api/dropsEl mismo movimiento como registro REST, con el porcentaje ya calculado.
Retry-AfterRespuestas 429, frames rate_limitedSegundos que hay que esperar antes de la siguiente solicitud o reconexión.

Leer una caída por REST y por SSE

La misma detección alimenta el stream y el endpoint de caídas, pero los dos registros tienen formas distintas. Un parser escrito para uno leerá mal el otro en silencio, así que mapea los campos de forma explícita.

Qué quieresAlerta SSECaída REST
La líneasport, league, sect, outcome, periodsport_name, market, side
Precio antes y despuésfrom_price, to_pricefrom, to
Tamaño del movimientoCalcula: (from_price menos to_price) dividido por from_pricedrop_pct
Precio justo tras el movimientonvpnvp
Cuándoalerted, segundos UnixUn timestamp en cada registro
Filtradomin_drop en la conexión, recheck en prepartidomode, min_drop_pct, max_age_sec en la solicitud
ReenvíoNinguno; los alertas perdidos se pierdenAproximadamente las últimas tres horas

Glosario

Tablero
Todos los eventos que el feed lista en este momento para un deporte en una fase, en vivo o prepartido, con sus mercados.
Cursor
El valor last que lleva una respuesta de tablero; devuelto como since, limita la siguiente respuesta a lo que cambió.
Caída
Un precio que cae al menos el umbral que fijaste, medido desde el precio anterior que vio el feed hasta el actual.
Precio sin margen (nvp)
El precio justo implícito en un mercado una vez retirado el margen de la casa, de modo que las probabilidades de todos los resultados sumen uno.
Periodo
La parte del partido sobre la que se liquida un mercado: num_0 para el partido completo, luego mitades, cuartos, sets o mapas según el deporte.
Fase
Prepartido, antes de que empiece el evento, o en vivo, mientras se disputa. El feed sirve ambos por rutas y streams distintos.
Snapshot
Una respuesta de tablero guardada en tu propio almacén, el estado contra el que reconcilias y que actualizas desde el stream.

Elegir un transporte

El sondeo REST es el valor por defecto correcto para snapshots y reconciliación: es barato con el cursor, se recupera solo y no necesita una conexión de larga duración. Los streams SSE son lo correcto cuando tu aplicación reacciona a caídas y a nada más, porque el feed ya ha hecho la detección. Los frames WebSocket sin procesar solo son lo correcto cuando necesitas cada movimiento de precio, incluidos los demasiado pequeños para alertar, y estás preparado para reconstruir el estado del mercado y recuperar huecos por tu cuenta.

La mayoría de las configuraciones en producción usan dos: REST para el arranque y la reconciliación periódica, un stream para la ruta caliente. La guía de transportes sopesa recuperación, frescura, estado y coste operativo, y la página stream SSE frente a endpoint REST en pnclPULSE compara las dos formas de leer caídas.

Preguntas frecuentes

¿Es esta la API oficial de Pinnacle?
No. Pinnacle cerró el registro público en su propia API el 23 de julio de 2025 y concede acceso previa solicitud. El feed documentado aquí sirve los precios de Pinnacle a cualquiera con una clave, y solo lee precios.
¿Existe una API de cuotas de Pinnacle gratuita?
Sí. La clave gratuita permite 20 solicitudes por minuto y 100 al día en REST, sin tarjeta y sin caducidad. Los streams y las tasas más altas necesitan un plan de pago o la demo de tres días.
¿La API tiene cuotas históricas?
No. La documentación describe un servicio de datos actuales, sin archivo histórico. Registra lo que necesites desde el día en que empieces y comprueba los derechos de almacenamiento que se aplican a tu uso.
¿Con qué frecuencia se actualizan los precios?
Las páginas del proveedor discrepan: cinco segundos en la página de inicio, treinta segundos en la referencia, para el prepartido. Trata ambos como cadencia documentada, no latencia medida, y mide tu propio camino si importa.
¿Puedo realizar apuestas a través de él?
No. Es un feed de datos. Apostar a través de una API significa la API de apuestas de la propia Pinnacle, disponible solo para usuarios aprobados.
¿Qué plan necesito para las alertas de caída?
El plan de alertas de caída SSE de 99 $, o REST + SSE a 149 $ y REST de alto volumen a 229 $, que incluyen los streams. El plan de snapshots REST de 99 $ no tiene stream.
¿Qué pasa si supero el límite de tasa?
La API responde 429 con un encabezado Retry-After. Espera esos segundos y continúa; un bucle de reintentos cerrado convierte un límite corto en una restricción más larga, y en los streams produce un mensaje de control rate_limited.

Dónde se profundiza cada tema

Esta guía es la referencia; siete sitios del mismo editor llevan más lejos, cada uno, una parte del feed.

  • pnclFEED: quickstarts en curl, Python, Node.js, Google Sheets y WebSocket sin procesar, todos ejecutables con la clave gratuita.
  • pnclENGINE: planificación de capacidad REST, el cursor since, límites de tasa, manejo de errores, almacenamiento de histórico y monitorización de pipeline.
  • pnclPULSE: alertas de caída por SSE, de umbrales y ventanas a deduplicación, entrega y reconexiones.
  • pnclODDS: planes, precios, acceso, cobertura y el complemento WebSocket, comparados al completo.
  • pnclDATA: alertas de cuotas a la baja con precios justos, y calculadoras de EV, cuotas a la baja, CLV y Kelly.
  • pnclHUB: una mesa de partidos de esports en vivo para CS2, League of Legends y Dota 2, con guías de mercado.
  • Herramientas de pnclAPI: el conversor de cuotas, la calculadora sin margen y la calculadora de coste de la API, aquí en este sitio.

Las correcciones de cualquier dato de esta página siguen la política editorial: la página cambia, y también su fecha de actualización.