Understanding odds & probability
A field-by-field walkthrough of the /v1/events/{id}/intelligence probability payload, using a real MLB event. For the full field reference and type table, see API Reference → Get bet intelligence.
Definition: vig / de-vig. "Vig" (short for vigorish, a.k.a. "the juice") is the built-in margin a book bakes into a market so the implied probabilities of every outcome add up to more than 100%. Two sides both priced at -110 each imply ~52.4% — ~104.8% combined; that extra ~4.8% is the vig. "De-vig" is the reverse: normalizing those implied probabilities back down so they sum to exactly 100%, recovering the book's true underlying estimate. Every probability and fair.probability field in this payload is already de-vigged for you.
Every term on this page refers back to one real bet object — the Minnesota Twins moneyline, from a live GET /v1/events/17386/intelligence response (MLB, Twins vs. Orioles):
{
"bet_type": "ML_P1",
"player_name": "Minnesota Twins",
"probability": 0.50236,
"fair_price": -101,
"market": { "price": -105, "book": "pinnacle" },
"edge": null,
"tier": null,
"fair": {
"probability": 0.50699,
"books": ["circa", "pinnacle"],
"n_books": 2,
"is_consensus": true
},
"edges_by_book": {
"fanduel": -0.00551,
"hardrock": -0.01016,
"draftkings": -0.01471,
"caesars": -0.03626
},
"best": { "book": "fanduel", "price": -104, "edge": -0.00551 }
}
Two notions of "fair"
Every bet on the probability surface carries two independent fair-price estimates, and mixing them up is the single most common integration mistake:
- Single-book de-vig — market is the raw quoted line from one specific book, and it's an object with two distinct parts, not two names for the same thing: market.book is which bookmaker quoted it (e.g. "pinnacle"), and market.price is what odds they quoted (e.g. -105). probability is what you get by de-vigging that single book's price against itself — no other book is involved. fair_price is just probability converted back into American-odds format, so you can compare it directly to market.price.
- fair — a separate, independently-computed sharp-consensus fair probability, sourced from the sharpest book(s) available (Pinnacle alone, or Pinnacle+Circa on MLB when both quote). This is the reference every retail book gets compared against in edges_by_book.
These two numbers are usually close but rarely identical, because they can be sourced from different books entirely. Take the Twins ML from the response above:
| Field | Value | Source |
|---|---|---|
probability | 0.50236 | De-vig of market.book alone (here, Pinnacle at -105) |
fair.probability | 0.50699 | Sharp consensus across fair.books: ["circa", "pinnacle"] |
Use probability/fair_price when you want "what does the fair price look like for the book I'm quoting." Use fair as the anchor for cross-book comparison — that's exactly what edges_by_book does for you.
The fair_price arithmetic
fair_price is just probability converted to American odds — the standard vig-free conversion:
if probability >= 0.5:
fair_price = -100 * probability / (1 - probability)
else:
fair_price = 100 * (1 - probability) / probability
Plugging in the Twins moneyline (probability = 0.50236, which is ≥ 0.5):
-100 * 0.50236 / (1 - 0.50236) = -100 * 0.50236 / 0.49764 ≈ -101
That matches the fair_price: -101 in the response. Compare it to market.price: -105 — the book is charging 4 cents of vig on this side relative to its own de-vigged number. That gap (market.price vs. fair_price) is the book's built-in margin, not an edge you can act on — you'd need to compare against fair for that, which is what edges_by_book does next.
edges_by_book & best — line-shopping
edges_by_book answers a narrower question than "should I bet this": given the sharp-consensus fair.probability, how much of a price gap does each retail book's current line represent? The formula:
decimal_odds = american_to_decimal(book_price) edge_for_book = fair.probability * decimal_odds - 1
For the Twins ML, best picked fanduel at -104 with edge: -0.00551. Checking the math: -104 American → decimal odds 1 + 100/104 ≈ 1.9615. 0.50699 * 1.9615 - 1 ≈ -0.0054 — matches (small rounding from the displayed fair.probability).
A negative edge means that book's price is worse than sharp-fair for this side (you'd be laying more than the consensus says the outcome is worth); a positive edge means it's priced better. In this example every book is slightly negative — Pinnacle/Circa were tight that day, so no retail book actually beat the sharp line. best just reports whichever book had the smallest gap (i.e. the least-bad price to shop at), not a recommendation to bet it:
| Book | Edge (price gap vs. fair) |
|---|---|
| fanduel | -0.00551 ← best |
| hardrock | -0.01016 |
| draftkings | -0.01471 |
| caesars | -0.03626 |
best.quote_age_seconds tells you how stale that book's quote is — a large gap on a quote that's minutes old is far less meaningful than the same gap on a quote from seconds ago.
ev (Beta) — same gap, display packaging
best.edge / edges_by_book stay price gaps. bets[].ev is the one field that re-expresses a positive moneyline gap as an EV% and a full-Kelly stake so agents can display it without inventing the arithmetic. It is not a claim-ladder +EV badge and not a pick — has_recommend stays false.
On this Twins moneyline, every retail gap is negative, so ev is null. A positive FanDuel gap of 0.031 at +155 would package as:
{
"beta": true,
"book": "fanduel",
"price": 155,
"ev_pct": 3.1,
"kelly_fraction": 0.02,
"quote_age_seconds": 240.0,
"n_books": 1
}
Gates: moneyline tokens only; a published fair (n=1 Pinnacle is enough — soccer and tennis have no second sharp book); gap > 0 and ≤ 25%; suppressed MLB books (DraftKings / FanDuel proven-null, plus unscreened BetMGM / Bet365 / BetOnline) walk to the next eligible book. Scan a sport with GET /v1/intelligence/ev (MCP list_ev, Playground #list_ev).
match_overview & rationale — context, not a pick
These two top-level, event-level fields are a separate concern from odds entirely. match_overview is a short natural-language preview of the matchup. rationale is a list of factual matchup chips (e.g. starting pitchers, injuries, recent form). Both are deliberately not a recommendation surface — no edge, tier, or pick implied — and either can be null when not yet available for an event.
These are distinct from the legacy bets[].rationale array on points-model sports, which is a list of signal-derived bullets per bet, not event-level context.
Full annotated example
The full, real GET /v1/events/17386/intelligence response this page's examples are drawn from, with the fields discussed above called out inline on the first bet:
{
"event_id": 17386,
"available": true,
"odds_source": "pinnacle",
"sport": "mlb",
"league": "mlb",
"players": {
"home": { "name": "Minnesota Twins", "player_id": null, "team_id": 72 },
"away": { "name": "Baltimore Orioles", "player_id": null, "team_id": 63 }
},
"has_recommend": false,
"match_overview": "The Minnesota Twins (59-61) host the Baltimore Orioles (57-62) on August 12, 2026, at Target Field in a series rubber match. Both teams are below .500 and competing in the AL Central and AL East respectively. The game is scheduled for 1:40 p.m. ET with an over/under set at 8.5.", // "match_overview & rationale"
"rationale": [ // "match_overview & rationale"
"Twins RHP Zebby Matthews (5-8, 5.23 ERA, 73 SO) opposes Orioles RHP Shane Baz (4-11, 3.76 ERA, 120 SO); Matthews' team is 8-7 ATS in his starts while Baz's starts yield 10-12 ATS for Baltimore.",
"Minnesota is 32-12 in games when not allowing a home run; Orioles are 4-6 in their last 10 games with a .219 batting average.",
"Twins are 31-28 at home; Orioles are 26-32 as moneyline underdogs this season.",
"Series context shows Baltimore leads the season head-to-head 7-3 in wins, 4-6 in over/under outcomes."
],
"intelligence_updated_at": "2026-08-12T14:42:19Z",
"bets": [
{
"bet_type": "ML_P1",
"player_role": "home",
"player_id": null,
"team_id": 72,
"player_name": "Minnesota Twins",
"probability": 0.50236, // de-vig of market.book (Pinnacle) alone — "Two notions of fair"
"interval": [0.42902, 0.57569],
"fair_price": -101, // -100 * .50236 / (1-.50236) ≈ -101 — "fair_price arithmetic"
"market": { "price": -105, "line": null, "book": "pinnacle" },
"edge": null,
"tier": null,
"fair": {
"probability": 0.50699, // sharp consensus — a different source than "probability" above
"books": ["circa", "pinnacle"],
"n_books": 2,
"is_consensus": true
},
"edges_by_book": { // price gap vs fair.probability, per retail book — "edges_by_book & best"
"caesars": -0.03625661385006673,
"fanduel": -0.005513797120894615,
"hardrock": -0.01015658985142276,
"draftkings": -0.014711782719110666
},
"best": { // smallest gap — least-bad, not a pick
"book": "fanduel",
"price": -104,
"edge": -0.00551,
"quote_age_seconds": 717.102774
},
"ev": null, // Beta — null here because every gap is ≤ 0; moneyline only
"computed_at": "2026-08-12T14:42:19Z"
},
{
"bet_type": "ML_P2",
"player_role": "away",
"player_id": null,
"team_id": 63,
"player_name": "Baltimore Orioles",
"probability": 0.49764,
"interval": [0.42431, 0.57098],
"fair_price": 101,
"market": { "price": -103, "line": null, "book": "pinnacle" },
"edge": null,
"tier": null,
"fair": {
"probability": 0.49301,
"books": ["circa", "pinnacle"],
"n_books": 2,
"is_consensus": true
},
"edges_by_book": {
"caesars": -0.009055862009059834,
"fanduel": -0.03294774134064382,
"hardrock": -0.037462457767625024,
"draftkings": -0.018867190107979992
},
"best": { "book": "caesars", "price": 101, "edge": -0.00906, "quote_age_seconds": 717.106364 },
"computed_at": "2026-08-12T14:42:19Z"
},
{
"bet_type": "OVER",
"player_role": null,
"player_id": null,
"team_id": null,
"player_name": null,
"probability": 0.52598,
"interval": [0.44609, 0.60588],
"fair_price": -111,
"market": { "price": -118, "line": 8.5, "book": "pinnacle" },
"edge": null,
"tier": null,
"fair": {
"probability": 0.513,
"books": ["circa", "pinnacle"],
"n_books": 2,
"is_consensus": true
},
"edges_by_book": {
"betmgm": 0.02600147666367647,
"caesars": 0.02600147666367647,
"draftkings": 0.03113148404699473
},
"best": { "book": "draftkings", "price": 101, "edge": 0.03113, "quote_age_seconds": 717.111229 },
"computed_at": "2026-08-12T14:42:19Z"
},
{
"bet_type": "UNDER",
"player_role": null,
"player_id": null,
"team_id": null,
"player_name": null,
"probability": 0.47402,
"interval": [0.39412, 0.55391],
"fair_price": 111,
"market": { "price": 105, "line": 8.5, "book": "pinnacle" },
"edge": null,
"tier": null,
"fair": {
"probability": 0.487,
"books": ["circa", "pinnacle"],
"n_books": 2,
"is_consensus": true
},
"edges_by_book": {
"betmgm": -0.1071680202750368,
"caesars": -0.1071680202750368,
"draftkings": -0.11382101565301717
},
"best": { "book": "caesars", "price": -120, "edge": -0.10717, "quote_age_seconds": 717.106364 },
"computed_at": "2026-08-12T14:42:19Z"
}
]
}
For the complete field reference (types, nullability, every bet token per sport), see API Reference → Get bet intelligence, or open this endpoint live in the Playground.