DEV Community

pinnradar
pinnradar

Posted on

Track dropping Pinnacle odds in real time with Python

When a price at Pinnacle drops sharply, it usually means money hit the market and the line moved. Pinnacle is the reference book
for a lot of modellers because it runs a low margin and moves its line instead of limiting winning players, so its drops are

worth watching.

Pinnacle closed its public API in July 2025. In this post I'll use PinnRadar, an independent API that

serves Pinnacle's live and prematch odds, with its small Python client.

## Setup

  pip install pinnradar
Enter fullscreen mode Exit fullscreen mode

Grab a free key at pinnradar.com (just an email) and export it:

  export PINNRADAR_API_KEY=your_key_here
Enter fullscreen mode Exit fullscreen mode

## 1. Watch for drops (works on the free key)

The /api/drops endpoint keeps a rolling buffer of recent price drops across every sport. This script polls it and prints each

new drop once:

  import time
  from pinnradar import Client

  pr = Client()   # reads PINNRADAR_API_KEY
  seen = set()

  while True:
      for d in pr.drops(mode="live", min_drop_pct=5, max_age_sec=900):
          key = (d["ts"], d["market_key"], d["event_id"])
          if key in seen:
              continue
          seen.add(key)
          pick = d.get("designation") or d.get("side") or ""
          line = f' {d["points"]}' if d.get("points") is not None else ""
          print(f'{d["sport_name"]:<8} {d["home"]} v {d["away"]} | {d["market"]} {pick}{line} | '
                f'{d["from"]:.2f} -> {d["to"]:.2f}  (-{d["drop_pct"]:.1f}%)')
      time.sleep(900)   # the free key allows 100 calls a day
Enter fullscreen mode Exit fullscreen mode

Real output from a run:

  Basketball Mobis Phoebus v Goyang Skygunners | team_total over 88.5 | 1.77 -> 1.68  (-5.1%)
  Tennis   Adrian Oetzbach v Radu David Turcanu | spread home -1.5 | 2.77 -> 2.52  (-9.0%)
  Tennis   Adrian Oetzbach v Radu David Turcanu | moneyline home | 1.93 -> 1.79  (-6.9%)
Enter fullscreen mode Exit fullscreen mode

Useful filters: sport_id=1 for soccer only, mode="prematch" for upcoming games, markets=["total"] for totals only.

## 2. Turn prices into fair (no-vig) odds

Bookmaker prices include a margin. To get the market's actual estimate, remove it across every outcome of the market:

  from pinnradar import Client, no_vig

  pr = Client()
  board = pr.prematch_fixtures(sport_id=1)   # upcoming soccer

  for ev in board["events"]:
      ml = (ev.get("periods") or {}).get("num_0", {}).get("money_line") or {}
      if ml.get("home") and ml.get("draw") and ml.get("away"):
          prices = [ml["home"], ml["draw"], ml["away"]]
          print(ev["home"], "v", ev["away"], prices, "fair", [round(x, 2) for x in no_vig(prices)])
          break
Enter fullscreen mode Exit fullscreen mode
  Liverpool v Manchester United [1.719, 4.28, 4.45] fair [1.79, 4.45, 4.63]
Enter fullscreen mode Exit fullscreen mode

Those fair odds are what people compare against softer bookmakers when hunting for value, or use as the closing line to measure

CLV. If you just want to play with the math, there is a no-vig calculator that

loads live Pinnacle prices.

## 3. Going real-time

Polling every 15 minutes is fine for research, but drops matter most in the seconds after they happen. Paid plans can stream

every drop over Server-Sent Events, and the client handles reconnects:

  for d in pr.stream_drops(min_drop=5):
      print(d["home"], d["sect"], d["outcome"], d["from_price"], "->", d["to_price"], "fair", d["nvp"])
Enter fullscreen mode Exit fullscreen mode

There is also a WebSocket feed with every price change, and an MCP server so Claude or Cursor can query odds directly.

## Links

Questions or ideas for the next post? Drop them in the comments.

Data only, not betting advice. PinnRadar is independent and not affiliated with Pinnacle.

Top comments (0)