Pinnacle odds into a pandas DataFrame
Answer: call GET /v1/odds, then loop events → markets → outcomes and build one row per outcome. You get a tidy DataFrame with decimal, fair, fairProb, margin and maxStake per price, ready for groupby, Parquet or a notebook. Works on every plan; Free data is 30 seconds old.
Code#
# 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)
Useful next steps#
- Filter sharp-size markets:
df[df.max_stake >= 100](maxStake answer). - More than 200 events: page with
offset(pagination). - Closing prices for CLV on Builder: Python closing lines.
- Live updates instead of polling: Python WebSocket example.
Plans#
Free (€0): 30-second delay, 2 sports, main markets, 1,000 requests/day. Hobby (€29/mo): real-time REST. Builder (€99/mo): WebSocket, all markets and closing-line history. Paid plans: 3-day trial; a card is required and it is charged after day 3 unless you cancel.
Educational content for adults (18+). For research and modelling, not betting advice.
Questions people ask#
Why one row per outcome?#
It keeps every price in one column, so filters, pivots and joins stay simple. Pivot to one row per market with df.pivot_table(index=["event_id","market","line"], columns="outcome", values="decimal").
Does it work in Jupyter or Colab?#
Yes. Set the key with os.environ["PINNFEED_KEY"] = "ps_live_..." in a cell you do not share.
Is there a polars version?#
Pass the same rows list to polars.DataFrame(rows).