DEV Community

Cover image for How to get Pinnacle odds (and no-vig fair prices) with Python in 2026 — after Pinnacle killed its public API
Sami
Sami

Posted on

How to get Pinnacle odds (and no-vig fair prices) with Python in 2026 — after Pinnacle killed its public API

If you build betting models, run a +EV or arbitrage tool, or just track closing-line value, you already know the one line everyone benchmarks against: Pinnacle. Lowest margins, highest limits, no winner bans — its price is the closest thing the industry has to a "true price."

The catch: in July 2025 Pinnacle shut down its public API, restricting access to "select high-value bettors and commercial partnerships." A lot of tooling broke overnight. Here's a practical, keyless way to still pull Pinnacle's lines in Python — and, more importantly, turn them into the number you actually bet on: the no-vig fair price.

The demo: a live Pinnacle line, de-vigged

Pulled a few minutes ago — Pinnacle's moneyline on a Champions League qualifier:

Thun vs Dinamo Zagreb (h2h) — overround 7.1%:

Outcome Pinnacle price Implied % No-vig fair % Fair odds
Thun (home) 3.45 29.0% 27.1% 3.70
Draw 3.68 27.2% 25.4% 3.94
Dinamo (away) 1.96 51.0% 47.6% 2.10

The raw prices bake in Pinnacle's ~7% margin. Strip it out and Dinamo's fair price is 2.10, not 1.96 — that de-vigged number is what you compare every other book against to find +EV. The math is tiny:

prices = {"home": 3.45, "draw": 3.68, "away": 1.96}
implied = {k: 1/v for k, v in prices.items()}
overround = sum(implied.values())                 # 1.071  -> 7.1% vig
fair_prob = {k: v/overround for k, v in implied.items()}
fair_odds = {k: 1/p for k, p in fair_prob.items()}  # away -> 2.10
Enter fullscreen mode Exit fullscreen mode

Do that across every book and the gaps are your edge. The hard part was never the math — it's getting Pinnacle's price reliably.

Pulling the odds

Pinnacle Odds Scraper does the fetch, keyless (no login, no API key). Minimal input:

{
  "mode": "pre_match_and_live",
  "sports": ["soccer", "tennis"],
  "markets": ["h2h", "spreads", "totals"]
}
Enter fullscreen mode Exit fullscreen mode

You get one normalized row per outcome — homeTeam, awayTeam, league, marketType, outcomeLabel, price, commenceTime, isLive — for pre-match and in-play. Add "specials" for Pinnacle's 5,000+ per-sport markets (futures, exact-totals, first-to-score, team props) — the depth most odds feeds don't carry. Pay-per-use: $0.01 per pre-match snapshot, $0.02 live, $0.04 for the specials tier — no $249/mo subscription, you pay for what you poll.

Honest note: The Odds API also carries Pinnacle's main markets from $30/mo if you want a fixed-quota REST API — this actor's edge is the specials depth, the built-in no-vig/CLV derivation, and per-use pricing.

Why it's a recurring job, not a one-off

Two numbers matter to a serious bettor, and both need repeated pulls:

  • The line when you bet — odds move every few seconds; you snapshot at decision time.
  • The closing line — the single best predictor of long-run edge. Capturing CLV means pulling the price again right before kickoff, every event you track.

So the natural setup is a schedule: point it at your leagues, run it every minute pre-match and near-continuously toward kickoff, and log the series. Turn on delta mode + an Apify Schedule and each run only returns what changed — a gap-free odds history you pay for only when the line actually moves.

Sports don't have an off-season

Soccer, tennis, basketball, MMA, baseball — something sharp is priced year-round, so a Pinnacle feed is about as evergreen as alt-data gets. If you're building anything that needs a true-price benchmark, this is the input.

Ready-to-clone presets (closing-line EV, live in-play, specials/futures) are on the Actor's examples tab. Questions or a market you need? Open an issue on the Actor page.

Top comments (0)