The Odds Gap Data
Every price we scan across US sportsbooks and prediction markets, in one clean CSV. Fair odds and a best-price flag on every row, ready for models, research and backtests.
A sample week, September 15 to 21, 2026, is free to test with. No signup, no key.
This week's lines are free on the Linesheet; past lines are in the data files.
Full history is a paid subscription: $10 the first month, then $25 a month.
A free sample week, September 15 to 21, 2026, stays free to test with; recent and ongoing data are part of the subscription. The first 25 paying people get the founding rate, $25 a month. Paying by card: your first month is $10, then $25 a month until you cancel.
What's in the File
Moneyline, spread and total from every book, one row per snapshot.
First seen, six hours out, one hour out and the close.
De-vigged probability on every row, with the method published.
Prediction-market prices, with any taker fee already applied.
Download
The free file is one sample week, September 15 to 21, 2026.
Paid access adds:
One request, one streaming CSV. Without a key, every request returns the same sample week, September 15 to 21, 2026 (ET game dates), whatever window it asks for, and says so in an X-Data-Sample header; recent and ongoing data need a paid key.
meta, for developers and agents.Every file comes in all three with the same rows and columns: change .csv to .json or .parquet, or add format=.
curl -o mlb.csv "https://theoddsgap.com/api/odds-export.csv?sport=baseball_mlb"
import pandas as pd
df = pd.read_csv("https://theoddsgap.com/api/odds-export.csv?sport=baseball_mlb&market=ml")
curl "https://theoddsgap.com/api/props-export?player=judge&anchor=close&format=json"
Access
$0
was $25 $10 first month 60% Off
$25 a month after. Cancel any time.
Founding Rate
Card checkout is through Stripe. Crypto is $25 in USDC, USDT or USDG on Ethereum, Base, Polygon, Arbitrum, Optimism, Robinhood Chain, Solana, Tron and TON, 30 days a payment, no email needed.
Paid files cover your full history up to 24 hours ago. Today's prices are on the game pages and the Line Shop.
One key per email, sent as the X-OddsGap-Data-Key header, or as key= in the URL for spreadsheets. First month $10, then $25 a month on your card until you cancel. Your key arrives by email within a minute of paying, with the cancel link in it.
The site stores each scan for 45 days, then it rolls off the per-scan file. Older scans stay in the full archive, one file per day back to June 4, 2026. Also kept for good: the Pinnacle open for each game, each finished game's closing prices on its game page, and each book's earliest and closing price at every line in the history file, back to June 4, 2026.
Reference
Long (tidy) format: one row per snapshot, book, market and side. Everything a modeler needs is below.
Long (tidy) format. Long is the modeling standard, and a wide layout with a column per book breaks every time a book is added or removed. Need wide? Pivot on book, or ask us and we will add it.
| Column | Meaning |
|---|---|
snapshot_ts | UTC ISO timestamp of the scan that captured this price. |
sport | Sport key, e.g. baseball_mlb, basketball_nba. |
away, home | Team names as the odds feed supplies them. |
commence_time | UTC ISO scheduled start. Rows where this is later than snapshot_ts are pre-game. |
market | ml, spread, or total. |
side | home / away / draw for moneyline, home / away for spread, over / under for totals. |
line | The spread or total number. Blank on moneyline rows. |
book | Book key, e.g. draftkings, pinnacle, kalshi. |
american_odds | Executable American odds. Exchange taker fees are already applied, so this compares directly to a sportsbook price. |
devig_fair_prob | Fair probability from 0 to 1 (not a percentage), from de-vigging this book's own two sides with the power method. Soccer moneylines are normalized across all three outcomes. Blank when the book posted only one side of the market, because there is no vig to strip and we will not invent a number. |
best_price_flag | 1 if this book tied or beat every other book in the best-price set on the same market, side and line at that snapshot. Ties all get a 1. |
books= or exclude_books=, and the X-Best-Books header says which set was used.1. If you are ranking books, group by (market, side, line) and require more than one book in the group.| Param | Meaning |
|---|---|
sport | Required. One sport key, tennis for every rotating tennis key, or all. Keys: americanfootball_ncaaf, americanfootball_nfl, baseball_mlb, basketball_nba, basketball_ncaab, basketball_wnba, icehockey_nhl, mma_mixed_martial_arts, soccer_epl, soccer_fifa_world_cup, soccer_france_ligue_one, soccer_germany_bundesliga, soccer_italy_serie_a, soccer_spain_la_liga, soccer_uefa_champs_league, soccer_usa_mls, tennis_atp_paris_masters, tennis_atp_swiss_indoors, tennis_atp_vienna_open, tennis_wta_pan_pacific_open. |
start | YYYY-MM-DD or a full ISO timestamp. Defaults to the start of your window. Without a key the window is the sample week, and a start outside it returns the whole week. |
end | YYYY-MM-DD or a full ISO timestamp. Defaults to now, or to the end of the sample week without a key. |
market | ml, spread, total, a comma-separated list, or all (default). |
format | csv (default), json or parquet, or use the .json or .parquet path. long still means CSV. Rows are long in every format. |
books, exclude_books | Comma-separated book keys deciding which books may carry best_price_flag. Every book's rows still appear. Part of paid access. |
Response headers carry the window that was actually served: X-Window-Start, X-Window-End, X-Access-Tier, and X-Window-Clamped when a requested start or end fell outside your window. A file from the free sample week also carries X-Data-Sample: 1, its dates in X-Sample-First-Day and X-Sample-Last-Day, and the same note in the JSON meta. Very large pulls are capped, and X-Next-Start hands back the timestamp to resume from. X-Provenance-Missing names any day in your window we do not hold, rather than leaving a silent hole.
A second export covers player props: /api/props-export. Different shape from the file above, on purpose. Props are recorded at up to four fixed checkpoints per prop rather than on every scan: when we first see it, around six hours before the game, around one hour before, and at the close. The close is the last scan before the game starts, and props rescan every hour on the live board, so it lands inside that final window rather than at the literal last tick. Most vendors' closing lines work the same way; we would rather say it than let you assume otherwise. Each row carries the consensus fair probability (the median of every quoting book's de-vigged price, minimum three books), the best price on the board at that moment, which book had it, and how many books were quoting. There is no per-book ladder in it; the checkpoints are what we store.
Every row quotes the Over side: consensus_fair is the de-vigged probability of the Over in percent, and best_odds is the best Over price. The Under fair is 100 minus consensus_fair.
Touchdown scorer markets are Yes-only at the sportsbooks, so the Over is the Yes. First and Last Touchdown Scorer have one winner per game, so each sportsbook's full list is de-vigged together like a moneyline and the fair is the median across three or more sportsbooks. Anytime Touchdown Scorer can have several winners, so no book's list can be de-vigged on its own: consensus_fair is blank on those rows until three or more sportsbooks also post the No, and best_odds is still the best Yes price.
Pull a player's closing lines as CSV or JSON:
curl "https://theoddsgap.com/api/props-export?player=judge&anchor=close"
curl "https://theoddsgap.com/api/props-export?player=judge&market=batter_hits&anchor=close&format=json"
Every row also carries a player_id column (the last column in the CSV): the league and the name as the feed spells it, lowercased with spaces and punctuation turned into hyphens, so it stays the same on every pull. Pass it back to get exactly one player:
curl "https://theoddsgap.com/api/props-export?player_id=nfl:amon-ra-st-brown&anchor=close"
To find a player's player_id, search by name. Returns each match with the markets they appear in and their last game date:
curl "https://theoddsgap.com/api/props-export/players?sport=nfl&q=st.%20brown"
To see every market key we have captured, with a plain-English label, the sport, the row count and the first and last game date:
curl "https://theoddsgap.com/api/props-export/markets"
Name searches are case-insensitive substrings, with spaces URL-encoded and punctuation as the feed spells it, so st.%20brown finds Amon-Ra St. Brown and st%20brown does not. Rows captured before September 14, 2026 on a market two leagues share (NFL and college, NBA and WNBA) sometimes cannot be pinned to one league; their player_id starts with the sport instead, like football:, rather than a guess.
Parameters: player (name substring, case-insensitive), player_id (exact), market (exact market key, e.g. batter_hits), anchor (first_seen, t6, t1, close, or all), start and end (game dates), format (csv, json or parquet), books and exclude_books. Access tiers match the file above: a sample week, September 15 to 21, 2026, free with no signup, recent data and the full window with paid access.
Which book is named best: each row stores the one best price seen when it was captured. It is shown when that book is in the best-price set (by default the same set as the file above, so never an offshore book or Pinnacle) and left blank when it is not. We do not fill the gap with a second-best, because we never stored one.
Honesty notes: the site stores each day of props for 45 days, so the oldest day in this file is that far back, and older days are in the full archive, back to August 4, 2026. Only props quoted by three or more books have a consensus fair, so thin markets are absent by design. ProphetX prices captured before the 2026-08-26 repair were not always takeable at $100 (see the exchange price autopsy), so a ProphetX best price from before then is left blank, and consensus fairs from those days lean on those prices too.
Five more files, the data behind the Trends pages. Same access as the files above: a sample week, September 15 to 21, 2026, free with no signup, and recent data and the full window with paid access at no extra cost. Each one comes as csv, json or parquet, by format= or the path extension. Every file but Line Moves is rebuilt once a day, at about 7 AM ET, after the overnight final scores are in (last built October 30, 2026, 7:19 AM ET), and each download says when in its X-Trends-Built header.
| File | One Row Is |
|---|---|
| Team Results | One team in one finished game: the closing spread and total, the final score, the cover margin, and the result against both. |
| Player Game Logs | One player in one game: every stat we store, each with the player's season average before that game. |
| Player Prop Results | One player prop in one game: the line at open and close, the final stat, and whether the Over hit. |
| Game Conditions | One finished game: roof, altitude, weather near kickoff, the closing total and how the game landed against it. |
| Line Moves | One change in one book's line or price on one game. |
One row per team per finished game we scanned: the same rows the Trends team records grade and their Full Game Log download prints, so a team's numbers here and on its page are the same numbers. Each row has the closing spread and total the game page shows (the consensus close across the books we scan, with how many books it was taken over), the final score, the cover margin (the team's margin plus its closing spread), and the result against the spread (win, loss or push) and the total (over, under or push). A game with no closing line, a soccer handicap ending in .25 or .75, or a knockout game that went to extra time has a blank result, the way the page leaves it ungraded. In MLB the spread is the run line, in the NHL the puck line and in soccer the handicap.
curl "https://theoddsgap.com/api/team-results-export?league=nfl&team=Green%20Bay%20Packers"
Columns: league, game_date, commence_time, game_id, game_number, team, opponent, venue, team_score, opp_score, close_spread, close_total, close_source, close_books, cover_margin, ats_result, ou_result, fav_dog, open_spread, open_total, open_kind, season, week, primetime, division_game, conference_game, rest_days. Parameters: league (or all), team (the exact name in the file), start, end, format.
Leagues: NFL, College Football, MLB, NBA, WNBA, NHL, MLS, Champions League, World Cup. Game dates June 7, 2026 to October 29, 2026; days after the sample week need paid access.
A real row from the sample week (NFL):
league nflgame_date 2026-09-17commence_time 2026-09-18T00:18:00Zgame_id 401872932game_number (blank)team Buffalo Billsopponent Detroit Lionsvenue hometeam_score 41opp_score 31close_spread -5.5close_total 54.5close_source consensus_closeclose_books 19cover_margin 4.5ats_result winou_result overfav_dog favoriteopen_spread (blank)open_total (blank)open_kind (blank)season 2026-27week 2primetime 1division_game 0conference_game 0rest_days 3
One row per player per game, from the ESPN box scores the Trends player pages read: every stat we store for that league, each in its own column named by its prop market (for example player_rush_yds), and beside each one <stat>_season_avg, the player's average of that stat over this season's earlier games that have it. The game itself is never in its own average, and season_games_before says how many earlier games there were. A stat the box score does not give for a player is blank, never zero. The stat columns differ by league, so league is required, and the JSON meta and the X-Columns header carry the exact list. Prop lines are in Player Prop Results, one row per prop.
curl "https://theoddsgap.com/api/player-game-logs-export?league=nfl&player=jacobs"
Columns: league, game_date, game_id, player, player_id (the props file's id, so the files join), espn_player_id, position (blank in football, where the box score gives none), team, opponent, the shared game columns below, season_games_before, then each stat and its season average. Parameters: league (required), player (name, any part), start, end, format.
Leagues: NFL, College Football, NHL, NBA, College Basketball. Game dates August 23, 2025 to October 29, 2026; days after the sample week need paid access.
A real row from the sample week (NFL):
league nflgame_date 2026-09-21game_id 401872947player Aaron Donaldplayer_id nfl:aaron-donaldespn_player_id 16716position (blank)team Los Angeles Ramsopponent New York Giantsvenue homeseason 2026-27week 2primetime 1division_game 0conference_game 1rest_days 10fav_dog favoriteopen_spread -17.5close_spread -6.5open_total 39.5close_total 47.5open_kind first_sharpseason_games_before 0player_pass_yds 0player_pass_yds_season_avg (blank)player_pass_tds 0player_pass_tds_season_avg (blank)player_rush_yds 0player_rush_yds_season_avg (blank)player_reception_yds 0player_reception_yds_season_avg (blank)player_receptions 0player_receptions_season_avg (blank)player_pass_attempts 0player_pass_attempts_season_avg (blank)player_pass_completions 0player_pass_completions_season_avg (blank)player_pass_interceptions 0player_pass_interceptions_season_avg (blank)player_pass_longest_completion (blank)player_pass_longest_completion_season_avg (blank)player_pass_rush_yds 0player_pass_rush_yds_season_avg (blank)player_pass_rush_reception_yds 0player_pass_rush_reception_yds_season_avg (blank)player_rush_attempts 0player_rush_attempts_season_avg (blank)player_rush_longest (blank)player_rush_longest_season_avg (blank)player_rush_reception_yds 0player_rush_reception_yds_season_avg (blank)player_tds_over 0player_tds_over_season_avg (blank)player_kicking_points 0player_kicking_points_season_avg (blank)player_field_goals 0player_field_goals_season_avg (blank)player_pats 0player_pats_season_avg (blank)player_sacks 0player_sacks_season_avg (blank)player_solo_tackles 1player_solo_tackles_season_avg (blank)player_tackles_assists 3player_tackles_assists_season_avg (blank)player_defensive_interceptions 0player_defensive_interceptions_season_avg (blank)player_anytime_td 0player_anytime_td_season_avg (blank)player_reception_longest (blank)player_reception_longest_season_avg (blank)
One row per player, game and prop: the line when we first saw it and at the close, the best Over price at the close and which book had it, how many books were quoting, the final stat, and whether the Over hit, missed or pushed against the closing line. The book is shown under the same rule as the props file above, and left blank rather than replaced. A prop with no captured close is not in the file, because a line from an hour out is not the close. Each row also has the player's season average for that stat before the game (season_avg, over season_avg_games games) and the closing line minus that average (line_vs_avg).
curl "https://theoddsgap.com/api/prop-results-export?league=nfl&market=player_reception_yds"
Columns: league, game_date, game_id, team, opponent, home_away, player, player_id (the same id as the props file, so the two join), market, open_line, close_line, close_book, close_odds, n_books, stat, result (hit, miss or push for the Over), graded_at, then the shared game columns not already named, then season_avg, season_avg_games, line_vs_avg. Parameters: league, player, market, start, end, format, books, exclude_books.
Leagues: NFL, College Football, NHL, NBA. Game dates October 1, 2026 to October 29, 2026; days after the sample week need paid access.
One row per finished game the Team Trends pages grade: indoors or outdoors, altitude, the weather around kickoff and when it was observed, the closing total, the final points, and whether the game went over, under or pushed. A game we hold no conditions for is left out, never guessed. The shared game columns are from the home team's side.
curl "https://theoddsgap.com/api/game-conditions-export?league=nfl"
Columns: league, game_date, game_id, commence_time, away, home, indoor, altitude_ft, wx_kind, wx_temp_f, wx_wind_mph, wx_precip, wx_at, wx_source, closing_total, final_points, total_result, then the shared game columns not already named. Parameters: league, start, end, format.
Leagues: NFL, College Football, NHL, NBA. Game dates June 8, 2026 to October 29, 2026; days after the sample week need paid access.
A real row from the sample week (NFL):
league nflgame_date 2026-09-17game_id 401872932commence_time 2026-09-18T00:18:00Zaway Detroit Lionshome Buffalo Billsindoor 0altitude_ft 880wx_kind observedwx_temp_f 67.0wx_wind_mph 4.6wx_precip 0.0wx_at 2026-09-17T23:54Zwx_source iemclosing_total 54.5final_points 72total_result overteam Buffalo Billsopponent Detroit Lionsvenue homeseason 2026-27week 2primetime 1division_game 0conference_game 0rest_days 3fav_dog favoriteopen_spread (blank)close_spread -5.5open_total (blank)close_total 54.5open_kind (blank)
One row each time a book changed its line or price on a game: the old and new line, the old and new price, that book's own fair probability before and after, and the change in price in probability points. Built from the same scans and the same rules as the per-scan file, so a price that file leaves out is never one end of a move, and ProphetX prices from before the 2026-08-26 repair are left out. The shared game columns are from the side's team for a moneyline or spread move and the home team's for a total, filled once the game is final and in the daily build.
curl "https://theoddsgap.com/api/line-moves-export?sport=americanfootball_nfl&market=spread"
Columns: ts, from_ts, sport, away, home, commence_time, market, side, book, from_line, to_line, from_odds, to_odds, from_fair_prob, to_fair_prob, move_pp (the price change; positive means the side got more expensive, and a line change at the same price reads 0), then the shared game columns. Parameters match the per-scan file: sport (required), market, start, end, format. A long pull pages the same way, with X-Next-Start.
All five files carry the same game columns, named the same way, from that row's team's side (the player's team on a player row). Every value comes from data we store; a column we cannot fill for a league is blank, never guessed.
| Column | Meaning |
|---|---|
team, opponent | Whose side the row is from, and who they played. |
venue | home, away or neutral. Neutral sites come from ESPN's schedule, so they are marked only for the leagues named under Where they are blank, below. In other leagues, and for any game without a match in that schedule, venue is the listed home or away side and a neutral site is not marked. |
season, week, game_date | The season (2026-27 for one that crosses the new year), the week number for football (the Weekly Slate's weeks; blank for every other league), and the game's date in Eastern time. |
primetime | 1 for a national primetime game. NFL: a 7 PM ET or later start, or Thanksgiving, Black Friday or Christmas. College: 7:30 PM ET or later on a national network. NBA and NHL: a national broadcast window. |
division_game, conference_game | 1 when both teams share a division or a conference. |
rest_days | Full days off since the team's previous game. |
fav_dog | favorite, underdog or pickem at the closing spread. |
open_spread, open_total, open_kind | The game's opening line the game page shows. open_kind says what it is: open is Pinnacle's price when the game was first listed, first_sharp is Pinnacle's earliest price after that, earliest is the earliest price we found when we could not prove it was quoted at the first listing, and _non_sharp on the end means it is BetOnline's, not Pinnacle's. |
close_spread, close_total | The consensus close the game page shows. |
Where they are blank: primetime, division_game, conference_game, rest_days and neutral sites come from the ESPN schedules we store for the NFL, college football, the NBA, the NHL and college basketball, and are blank for every other league. A game we did not price has no lines, so its spread, total and favorite columns are blank. On a player row, a game whose schedule we do not hold still has its home or away side from the box score.
In the full archive below, every file but Line Moves is also one file per league per finished month, back to the first game we stored. Line Moves are built from the per-scan file, whose own days are in the archive.
A third file, /api/odds-history.csv, keeps one row per game, market, side, line and book: the earliest price we captured for that book at that line, its last price before the game started, and the movement between them in probability points. It reaches back well past what the file above stores, to 2026-06-04.
curl -o nfl-history.csv "https://theoddsgap.com/api/odds-history.csv?sport=americanfootball_nfl"
Earliest means the first price this site saw, not the market's opening line. close_best_flag works like best_price_flag above, over the same default set or your own books=. The marks column notes ProphetX prices captured before the 2026-08-26 repair, and ProphetX prices from before August 6, 2026, which had the venue's 2% commission applied on the way out. The X-Provenance-Missing header names any scan day we could not read, so a gap is never silent.
The last four columns, pinn_open_state, pinn_open_line, pinn_open_price and pinn_open_ts, carry the game's PINN Open for that market and side, the same price game pages show: open when Pinnacle priced the game the moment it was first listed, first_sharp when it is Pinnacle's earliest price after the listing, with Pinnacle's own quote time. They are blank when we hold neither, which includes every game before August 5, 2026.
Every scan we have captured, one file per day. Game lines start on June 4, 2026 and player props on August 4, 2026. Each file has the same columns as the matching file above, plus a marks column at the end, so a day stacks under a recent export with no mapping.
The archive is part of paid access. Send your key as the X-OddsGap-Data-Key header, or as key= in the URL. A day is one UTC calendar day, and it can be downloaded 24 hours after it ends.
curl -H "X-OddsGap-Data-Key: YOUR_KEY" -o odds-2026-06-04.csv.gz "https://theoddsgap.com/api/archive/odds/2026-06-04.csv.gz"
curl -H "X-OddsGap-Data-Key: YOUR_KEY" -o props-2026-08-04.csv.gz "https://theoddsgap.com/api/archive/props/2026-08-04.csv.gz"
Files are gzip CSV. Two downloads can run at once across the site, and one key can take 300 files a day. The list of days is at /api/archive/days.
Some older prices have known problems, and we would rather you knew which. The marks column names the known issue on any row one touches, and the notes file lists every known issue by date range. The main ones:
Two kinds of row are left out instead of marked: college rows from Novig, ProphetX and Polymarket US whose match to a game cannot be verified, and exchange moneylines whose two sides could not be a real pair of prices.
Open this section to load the list of days.
Sizes are each day's stored file. The CSV download is a different encoding of the same rows and runs larger.
Prices are captured on every scan across the books we cover, stored raw, and served back unchanged apart from the exchange fee adjustment. The de-vig, the fee model, and the line-selection rules are all written out on the methodology page, including why the power method is used instead of simple multiplicative scaling.
Coverage gaps are real and we would rather you knew: a book that was not returning a price at a given scan simply has no row, scanning runs 7am to 1am ET, and a game can appear at one line at one book and a different line at another. Nothing is backfilled or smoothed.
Quoting any figure or finding published on this site is welcome, with attribution to The Odds Gap and a link to the page it came from. Our measurements, the matched-lines comparisons, the staleness and dispersion studies and the captured opening lines, are our own work and are licensed on that basis. The underlying odds are facts we aggregate from sources with their own terms. Bulk or automated reuse of the underlying data, rather than quoting a published figure, needs a conversation first: [email protected].
Free for research, modeling, and writing, with attribution to The Odds Gap and a link to theoddsgap.com. Not permitted: reselling the data, repackaging it as your own feed or product, or sustained bulk scraping (rate limits are enforced). Portions arrive through licensed feeds, so this exists for people who want to study the market, not as a data source for another product. Heavier or commercial use: [email protected].
Live JSON instead of history: API docs.
These terms are also part of our full Terms of Service.
A sample week, September 15 to 21, 2026, every book, no signup.
Download Free