Cuotas de Pinnacle en un DataFrame de pandas
Respuesta: llama a GET /v1/odds y recorre eventos → mercados → resultados para crear una fila por resultado. Obtienes un DataFrame ordenado con decimal, fair, fairProb, margin y maxStake por precio, listo para groupby, Parquet o un notebook. Funciona en todos los planes; en Free los datos llevan 30 segundos de retraso.
Código#
# pip install requests pandas
import os, requests, pandas as pd
r = requests.get(
"https://pinnfeed.io/v1/odds",
headers={"x-api-key": os.environ["PINNFEED_KEY"]}, # ps_live_...
params={"sport": "soccer", "main": "true", "limit": 200},
timeout=20,
)
r.raise_for_status()
rows = []
for ev in r.json()["events"]:
for m in ev.get("markets", []):
for o in m.get("outcomes", []):
rows.append({
"event_id": ev["id"], "start": ev.get("startTime"), "live": ev.get("live"),
"league": (ev.get("league") or {}).get("name"),
"home": ev.get("home"), "away": ev.get("away"),
"market": m.get("type"), "period": m.get("period"), "line": m.get("line"),
"margin": m.get("margin"), "max_stake": m.get("maxStake"),
"outcome": o.get("name"), "decimal": o.get("decimal"),
"fair": o.get("fair"), "fair_prob": o.get("fairProb"),
})
df = pd.DataFrame(rows)
df["start"] = pd.to_datetime(df["start"], utc=True)
print(df.groupby("market")["margin"].describe())
df.to_csv("pinnacle_soccer.csv", index=False)
Siguientes pasos#
- Mercados con tamaño:
df[df.max_stake >= 100](maxStake). - Más de 200 eventos: pagina con
offset(paginación). - Cierres para CLV en Builder: líneas de cierre en Python.
- Cambios en vivo sin polling: ejemplo de WebSocket en Python.
Planes#
Free (€0): 30 s de retraso, 2 deportes, mercados principales, 1.000 peticiones al día. Hobby (€29/mes): REST en tiempo real. Builder (€99/mes): WebSocket, todos los mercados e histórico de cierres. Prueba de 3 días; se requiere tarjeta y se cobra después del día 3 salvo que canceles.
Contenido educativo para mayores de 18 años. Para investigación y modelos, no es consejo de apuestas. En España, apuesta solo con operadores con licencia de la DGOJ.
Preguntas frecuentes#
¿Por qué una fila por resultado?#
Así cada precio está en una sola columna y los filtros, tablas dinámicas y joins son sencillos. Para una fila por mercado: df.pivot_table(index=["event_id","market","line"], columns="outcome", values="decimal").
¿Funciona en Jupyter o Colab?#
Sí. Define la clave con os.environ["PINNFEED_KEY"] = "ps_live_..." en una celda que no compartas.
¿Hay versión con polars?#
Pasa la misma lista rows a polars.DataFrame(rows).