Odds da Pinnacle num DataFrame do pandas
Resposta: chame GET /v1/odds e percorra eventos → mercados → resultados para criar uma linha por resultado. Você obtém um DataFrame organizado com decimal, fair, fairProb, margin e maxStake por preço, pronto para groupby, Parquet ou um notebook. Funciona em todos os planos; no Free os dados têm 30 segundos de atraso.
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)
Próximos passos#
- Mercados com tamanho:
df[df.max_stake >= 100](maxStake). - Mais de 200 eventos: pagine com
offset(paginação). - Fechamentos para CLV no Builder: linhas de fechamento em Python.
- Mudanças ao vivo sem polling: exemplo de WebSocket em Python.
Planos#
Free (€0): 30 s de atraso, 2 esportes, mercados principais, 1.000 requisições por dia. Hobby (€29/mês): REST em tempo real. Builder (€99/mês): WebSocket, todos os mercados e histórico de fechamentos. Teste de 3 dias; é preciso cartão e a cobrança acontece depois do dia 3, se você não cancelar.
Conteúdo educativo para maiores de 18 anos. Para pesquisa e modelagem, não é conselho de apostas. No Brasil, aposte apenas em operadores autorizados pela SPA/MF.
Perguntas frequentes#
Por que uma linha por resultado?#
Assim cada preço fica numa só coluna e filtros, tabelas dinâmicas e joins ficam simples. Para uma linha por mercado: df.pivot_table(index=["event_id","market","line"], columns="outcome", values="decimal").
Funciona no Jupyter ou no Colab?#
Sim. Defina a chave com os.environ["PINNFEED_KEY"] = "ps_live_..." numa célula que você não compartilhe.
Tem versão com polars?#
Passe a mesma lista rows para polars.DataFrame(rows).