Vig Calculator in Python: Measure Sportsbook Margin Across 350+ Books
What Is the Vig (and Why You Should Calculate It)?
Every price a sportsbook posts has a hidden tax baked in. It’s called the vig (short for vigorish), also known as the juice, the margin, or the overround. It’s the reason that if you bet both sides of a coin-flip at the same book, you still lose money over time. The book isn’t pricing the true probability — it’s pricing the true probability plus its cut.
Here’s the part most bettors never measure: the vig on the exact same game varies wildly from book to book. We pulled a single 2026 World Cup group-stage fixture (June 11) and computed the margin on the 1X2 market for every book quoting an active line. The result:
| Book | Type | Vig (Margin) |
|---|---|---|
| Kalshi | Prediction market | 1.98% |
| Polymarket | Prediction market | 1.98% |
| Pinnacle | Sharp | 3.81% |
| Hard Rock Bet | US soft | 4.56% |
| DraftKings | US soft | 4.65% |
| Bet365 | Soft | 6.98% |
| Four Winds | US soft | 7.04% |
Same match. Same three outcomes. The worst active book charges 3.5× more juice than the cheapest. Bet the home side at Four Winds instead of Kalshi and you’re handing the house an extra 5 percentage points of edge before the ball is even kicked. Multiply that across a season and it dwarfs almost every other “edge” a recreational bettor chases.
A vig calculator turns that invisible tax into a number you can act on. This guide shows you the math, then builds a Python vig calculator that measures the margin on every bookmaker at once using the free OddsPapi odds API — 350+ books in a single call.
The Problem: Online Vig Calculators Do One Book at a Time
Search “vig calculator” and you’ll find dozens of web tools. They all share the same limitation: you paste in two or three odds by hand, for one book, on one market. To compare the juice across the field — which is the entire point — you’d be copy-pasting prices from 20 different sportsbook tabs into a form, one at a time, while the lines move underneath you.
That’s backwards. The vig is only useful as a comparison: lowest-juice book wins your action, highest-juice book tells you who to avoid, and the sharp/prediction-market margin tells you how efficient the market is. You need every book on one screen, refreshed in real time.
| The Old Way | With OddsPapi |
|---|---|
| Paste odds into a web form by hand | One API call returns every book |
| One book, one market at a time | 350+ books, every market, ranked |
| Stale by the time you finish | Live prices with active flags |
| No sharp/prediction-market benchmark | Pinnacle, Kalshi & Polymarket included |
| Can’t script it into a model | Pure JSON — drop into pandas, alerts, bots |
The Vig Math (Margin, Hold & No-Vig Fair Odds)
Decimal odds are just inverted probabilities. A price of 2.00 implies a 50% chance (1 / 2.00). Add up the implied probability of every outcome in a market and a fair, vig-free market would sum to exactly 1.00 (100%). Any sportsbook sums to more than 1.00 — that excess is the vig.
Two-Way Markets (Over/Under, BTTS, Moneyline)
For a market with two outcomes priced at p1 and p2:
overround = (1 / p1) + (1 / p2)
margin = overround - 1 # the "vig" most people quote
hold = margin / overround # share of total stakes the book keeps
Two terms get used loosely, so be precise:
- Margin / overround — how much the implied probabilities exceed 100%. This is what almost everyone means by “the vig.”
- Hold — the theoretical percentage of all money wagered the book expects to keep if action is balanced. Always slightly lower than the margin (
margin / (1 + margin)).
Pinnacle’s Over/Under 2.5 on our fixture was Over 2.18 / Under 1.735. That’s a margin of 3.51% and a hold of 3.39%.
Three-Way Markets (1X2 Soccer)
Same idea, one more term for the draw:
overround = (1 / home) + (1 / draw) + (1 / away)
margin = overround - 1
No-Vig Fair Odds (De-Vigging)
Once you know the overround, you can strip the vig back out to recover the book’s true estimate of each outcome — the no-vig fair odds. Divide each implied probability by the overround so they sum to 100% again:
fair_prob_i = (1 / price_i) / overround
fair_odds_i = 1 / fair_prob_i
This is the foundation of expected value betting and consensus fair-odds models: de-vig a sharp book like Pinnacle and you have a benchmark “true price” to measure every soft book against.
Build a Vig Calculator in Python
Now the code. Everything below is tested live against the OddsPapi API. Authentication is a single query parameter — no headers, no OAuth, no SDK.
Step 1: The Core Vig Functions
import requests, time
API_KEY = "YOUR_API_KEY" # grab a free key at oddspapi.io
BASE_URL = "https://api.oddspapi.io/v4"
def vig_2way(p1, p2):
"""Margin, hold and no-vig fair odds for a 2-outcome market."""
over = 1/p1 + 1/p2
margin = over - 1 # the overround (the "vig")
hold = margin / over # share of stakes the book keeps
fair = [(1/p1)/over, (1/p2)/over] # de-vigged probabilities
return margin, hold, [1/f for f in fair]
def vig_3way(home, draw, away):
over = 1/home + 1/draw + 1/away
margin = over - 1
hold = margin / over
fair = [(1/x)/over for x in (home, draw, away)]
return margin, hold, [1/f for f in fair]
margin, hold, fair = vig_3way(1.429, 4.54, 8.47) # Pinnacle 1X2
print(f"Margin {margin*100:.2f}% Hold {hold*100:.2f}% Fair {[round(f,3) for f in fair]}")
# Margin 3.81% Hold 3.67% Fair [1.483, 4.713, 8.793]
Step 2: Discover a Fixture
In OddsPapi terms, a league is a tournament, a game is a fixture, and a team is a participant. Pull fixtures for a sport in a date range (soccer is sportId=10) and keep the ones that actually carry odds:
r = requests.get(f"{BASE_URL}/fixtures", params={
"apiKey": API_KEY, "sportId": 10,
"from": "2026-06-11", "to": "2026-06-20",
})
fixtures = [f for f in r.json() if f.get("hasOdds")]
print(len(fixtures), "fixtures with odds")
fixture_id = fixtures[0]["fixtureId"]
Step 3: The Vig Leaderboard (Every Book, Ranked)
This is the function the web calculators can’t give you. One call to /odds returns every bookmaker’s prices for the fixture; we compute the margin for each and sort cheapest-first.
def vig_leaderboard(fixture_id, market_id="101", outcome_ids=("101","102","103")):
"""1X2 vig for every active book on a fixture, cheapest first."""
r = requests.get(f"{BASE_URL}/odds",
params={"apiKey": API_KEY, "fixtureId": fixture_id})
books = r.json().get("bookmakerOdds", {})
rows = []
for slug, data in books.items():
market = data.get("markets", {}).get(market_id)
if not market:
continue
outs = market.get("outcomes", {})
try:
prices = [outs[o]["players"]["0"]["price"] for o in outcome_ids]
actives = [outs[o]["players"]["0"]["active"] for o in outcome_ids]
except (KeyError, TypeError):
continue
# --- defensive filters (see warning below) ---
if not all(isinstance(p, (int, float)) and p > 1 for p in prices):
continue
if not all(actives): # skip suspended / stale lines
continue
over = sum(1/p for p in prices)
rows.append((slug, round((over - 1) * 100, 2)))
rows.sort(key=lambda x: x[1])
return rows
board = vig_leaderboard(fixture_id)
print(f"{len(board)} books quoting an active 1X2 line\n")
for slug, vig in board:
print(f"{slug:<16} {vig:>5.2f}%")
Output on our World Cup fixture:
15 books quoting an active 1X2 line
kalshi 1.98%
polymarket 1.98%
pinnacle 3.81%
hardrockbet 4.56%
draftkings 4.65%
circasports 4.66%
...
bet365 6.98%
fourwinds 7.04%
Step 4: Find the Lowest-Juice Book
best_slug, best_vig = board[0]
worst_slug, worst_vig = board[-1]
print(f"Cheapest juice: {best_slug} @ {best_vig}%")
print(f"Most juice: {worst_slug} @ {worst_vig}%")
print(f"You pay {worst_vig/best_vig:.1f}x more vig at the worst book.")
# Cheapest juice: kalshi @ 1.98%
# Most juice: fourwinds @ 7.04%
# You pay 3.6x more vig at the worst book.
Watch Out: Stale Lines Lie About the Vig
That active filter in Step 3 isn’t optional — it’s the single most important line in the whole calculator. On the same fixture, BetMGM and Borgata showed a 11.85% margin on 1X2. That number is garbage: both books had active: false on all three outcomes, meaning the line was suspended and the prices were stale snapshots, not bettable quotes. Feed those into your calculator and you’ll “discover” a 12% house edge that doesn’t exist.
The OddsPapi response ships an active boolean on every outcome precisely so you can filter this out. Always require active == True before computing vig (and require price > 1 — a handful of books occasionally ship a 0 price with active: true). De-vig math is only as honest as the prices you feed it.
Two-Way Vig Across the Field
Swap the market and outcome IDs and the same leaderboard works for any two-way market. Over/Under 2.5 is market 1010 (outcomes 1010 Over / 1011 Under); Both Teams To Score is 104 (104 Yes / 105 No). On our fixture the Over/Under 2.5 margins ran from Polymarket at 1.0% and Pinnacle at 3.51% up to several US books at 8.3%. Don’t hardcode market IDs — pull the live catalog and build your own lookup:
r = requests.get(f"{BASE_URL}/markets", params={"apiKey": API_KEY, "sportId": 10})
catalog = r.json()
market_names = {m["marketId"]: m["marketName"] for m in catalog}
# Over/Under 2.5 -> market 1010, outcomes 1010 (Over) / 1011 (Under)
ou_board = vig_leaderboard(fixture_id, market_id="1010", outcome_ids=("1010","1011"))
Why the Sharps and Prediction Markets Win on Vig
Look back at the leaderboard and the pattern is unmistakable. The two prediction markets — Kalshi and Polymarket — posted the tightest margins at 1.98%, beating even Pinnacle, the sharpest traditional sportsbook in the world, at 3.81%. The retail US books clustered at 4.5–7%. That ordering is the whole game: low-vig venues can afford thin margins because they run on volume and sharp money; high-vig books pad the price because their customers don’t shop around.
This is exactly the coverage that generic odds APIs can’t give you. Most competitors aggregate ~40 mainstream sportsbooks — all of them in that expensive 5–7% retail band. OddsPapi aggregates 350+ bookmakers, including the sharp books (Pinnacle, SBOBet, Singbet) and prediction markets (Kalshi, Polymarket) where the real low-vig pricing lives. You can’t calculate “who has the best juice” if your data source only carries the expensive books. And because OddsPapi gives away free historical odds on the free tier, you can track how a book’s vig moves from open to close — sharp books tighten their margin as the event approaches; soft books rarely do.
From here the natural next steps are line shopping for the single best price across those 350+ books, and feeding the no-vig fair odds into a Kelly staking calculator to size your bets.
FAQ
What is the vig in betting?
The vig (vigorish, juice, or margin) is the commission a sportsbook builds into its odds. It’s the amount by which the implied probabilities of all outcomes in a market exceed 100%. A 5% vig means the book’s prices sum to 105% implied probability — that extra 5% is the house edge.
How do you calculate vig from decimal odds?
Convert each price to its implied probability (1 / decimal_odds), sum them across all outcomes to get the overround, then subtract 1. For example, two outcomes at 1.91 each give 1/1.91 + 1/1.91 = 1.047, a vig of 4.7%.
What’s the difference between vig and hold?
Margin (overround) is how far the implied probabilities exceed 100%. Hold is the theoretical share of total money wagered the book keeps with balanced action, calculated as margin / (1 + margin). Hold is always slightly lower than the quoted margin.
Which sportsbook has the lowest vig?
It varies by market and event, which is why you measure it live. In our data, prediction markets (Kalshi, Polymarket) and sharp books (Pinnacle) consistently posted the lowest margins — often 2–4% — while retail US sportsbooks ran 5–7% on the same game. Running the vig leaderboard across OddsPapi’s 350+ books tells you the answer for any specific fixture.
Can I calculate vig for free?
Yes. The OddsPapi free tier returns live odds from 350+ bookmakers plus free historical data — everything you need to compute and compare vig across the entire market in Python, with no credit card required.
Stop Guessing What the House Is Charging
The vig is the most reliable edge a bettor can claw back, and it costs nothing but a few lines of Python to measure. Point the calculator above at any fixture and you’ll instantly see which books are robbing you and which are giving you a fair shake. Get your free OddsPapi API key and put a number on the juice.