DEV Community

Cover image for Pinnacle Killed Its Public API. Here's How to Get Pinnacle Odds in 2026 (With Code)
Ryan
Ryan

Posted on

Pinnacle Killed Its Public API. Here's How to Get Pinnacle Odds in 2026 (With Code)

If you build betting models, you already know the bad news: Pinnacle shut down public access to its API in July 2025. A decade of tutorials, GitHub repos, and Stack Overflow answers now point at endpoints that return nothing unless you're a commercial partner.

This post is the writeup I wish I'd found the week that happened. It covers the three realistic ways to get Pinnacle odds into your code today, and more importantly the architectural difference between them that most comparison posts skip entirely: polling vs push.

Why this one bookmaker matters

Skip this if you know it: Pinnacle is the sharpest major book, and its line is the closest public proxy for true probability. Arbitrage and value betting strategies are, mechanically, "react to a Pinnacle move before a soft bookmaker adjusts." Which means your entire edge lives inside a window of a few seconds and your data architecture decides whether you're inside or outside that window.

That's the lens for everything below.

The polling trap

Most odds APIs are REST-only. Your integration inevitably looks like this:

import time
import requests

while True:
    resp = requests.get(
        "https://api.example-odds-provider.com/v4/odds",
        params={"bookmakers": "pinnacle", "markets": "h2h"},
    )
    process(resp.json())
    time.sleep(10)  # rate limits say hi
Enter fullscreen mode Exit fullscreen mode

Count the delays stacked in that loop:

  1. Your polling interval (10s here — and on most providers' affordable tiers, rate limits or credit costs force it higher)
  2. The provider's own update cycle — how often they refresh from Pinnacle (often tens of seconds on cheaper plans)
  3. How they get the data — some providers scrape Pinnacle's public website rather than ingesting a feed. The Odds API, to its credit, says this in its own docs: Pinnacle "odds are from public website which may incur a delay."

Worst case, a price moved 40+ seconds before your bot sees it. For closing-line research, irrelevant. For arbitrage, that's not "slower" it's a different product. The opportunity was found, bet, and closed by faster actors while your time.sleep() was still counting down.

The push alternative

The fix is architectural, not a faster loop: the provider holds a connection open and pushes every change the moment it happens. The examples below use pinnapi, which is built around this model and exposes it two ways SSE for drop alerts, WebSocket for the full odds feed.

Option A: SSE drop stream

The simplest integration is the server-sent-events drop stream: you tell the server your threshold with min_drop, and it watches every Pinnacle price for you. One GET request, then drops arrive as they happen:

import json
import requests
from sseclient import SSEClient

resp = requests.get(
    "https://pinnapi.com/odds-drop?min_drop=3",
    headers={"x-portal-apikey": API_KEY},
    stream=True,
)

for event in SSEClient(resp).events():
    payload = json.loads(event.data)
    if isinstance(payload, list):          # first message is a "connected" handshake
        for drop in payload:
            print(f'{drop["home"]}: {drop["from_price"]} -> {drop["to_price"]}')
            evaluate_opportunity(drop)     # fires when Pinnacle moves, not when a loop wakes up
Enter fullscreen mode Exit fullscreen mode

There's a /odds-drop-prematch variant of the same stream (with an optional recheck parameter that confirms a drop is stable before alerting), so you can run live and prematch strategies off separate connections.

Notice what disappeared versus the polling version: the interval, the credit math, and the entire client-side diffing layer. Server-side drop detection is something polling structurally can't give you — you can't diff snapshots you haven't fetched yet.

Option B: WebSocket for the full feed

Drops are the signal; sometimes you want the whole market state. The WebSocket feed at wss://pinnapi.com/ws/feed gives you a snapshot on subscribe, then streams every add/update/delete as it happens:

import asyncio
import json
import websockets

async def main():
    async with websockets.connect(f"wss://pinnapi.com/ws/feed?key={API_KEY}") as ws:
        await ws.send(json.dumps({
            "type": "subscribe",
            "streams": ["live"],
            "sport_ids": [1, 2],           # 1 = soccer, 2 = tennis
        }))
        async for raw in ws:
            msg = json.loads(raw)
            if msg["type"] == "ping":      # heartbeat reply or get disconnected
                await ws.send(json.dumps({"type": "pong"}))
            elif msg["type"] == "snapshot":
                seed_order_book(msg["events"])
            elif msg["type"] == "live":
                apply_update(msg["rec"])   # a raw Pinnacle record, add/upd/del

asyncio.run(main())
Enter fullscreen mode Exit fullscreen mode

You subscribe by stream (live, prematch), by sport, or down to individual event_ids, so the connection carries only what your model actually consumes.

Either way, the latency budget collapses to network transit plus the provider's ingestion time pinnapi quotes 15–40 ms from a Pinnacle price change to your handler, and my own measurements land in that range. Against a 10-second polling loop, that's two-to-three orders of magnitude, on the exact metric the strategy lives on.

Your three options in 2026

OpticOdds — the enterprise answer. 200+ books including Pinnacle, excellent infrastructure, real push feeds. No public pricing; you talk to sales, and quotes generally start in the high hundreds per month and climb. If you're a funded operation, it's the best product in the space. If you're an individual, you're not the customer.

pinnapi — the specialist. Pinnacle-only by design: push delivery (SSE/WebSocket/REST), drop alerts, and no-vig fair prices built in, across 10+ sports live and prematch. Published pricing from $99 to $229/month, and a free tier (100 REST requests/day, no card) that's enough to benchmark the latency claims yourself before paying — which is how it should work. The tradeoff is the point: one book, done properly, at a price an individual can justify.

The Odds API — the generalist. Dozens of books in one normalized schema, genuinely free tier (500 credits/month), great docs, and the whole internet recommends it. But it's polling-only REST, credits burn as markets × regions per call, and its Pinnacle data specifically is website-scraped with a documented delay. Perfect for backtesting and multi-book dashboards; the wrong architecture for anything latency-sensitive.

OpticOdds pinnapi The Odds API
Delivery Push (enterprise) Push — SSE/WS, 15–40 ms Polling REST only
Pinnacle source Direct Direct, purpose-built Public-site scrape, documented delay
Drop alerts / no-vig Platform tooling Built in No
Pricing Custom quote $99–$229/mo published Cheapest, credit-based
Free tier No 100 req/day 500 credits/mo

Picking by use case, not by feature list

  • Backtesting or CLV research? The Odds API. Latency doesn't matter for historical analysis, and the free tier is real.
  • Live arbitrage or steam chasing as an individual or small team? pinnapi. It's the only push-based option priced for you, and drop alerts remove a whole component from your stack.
  • Building a sportsbook or trading desk? OpticOdds, and budget accordingly.

The one mistake I keep seeing in betting-dev communities: building a latency-sensitive strategy on a polling API because it was the first result, then blaming the strategy when it bleeds. Audit the delivery mechanism before you write the model. It's the highest-leverage architecture decision in the whole system.


I build betting models and infrastructure. No affiliate links here pricing and latency figures are from provider docs and my own testing as of mid-2026; check current docs before you commit.

Top comments (0)