{"id":58,"date":"2025-11-25T18:27:17","date_gmt":"2025-11-25T18:27:17","guid":{"rendered":"https:\/\/oddspapi.io\/blog\/?p=58"},"modified":"2026-07-28T20:50:01","modified_gmt":"2026-07-28T20:50:01","slug":"free-nfl-odds-api-guide","status":"publish","type":"post","link":"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/","title":{"rendered":"NFL Odds API: Free Python Guide to Lines, Spreads &#038; Totals (2026)"},"content":{"rendered":"<p>Fetch live NFL moneylines, spreads and totals in Python from 16 sportsbooks, including Pinnacle and SBOBet, on a free API key. Every code block and every number on this page ran against the live feed before it was published.<\/p>\n<p>If you are building a betting model, a line-movement tracker, an <a href=\"https:\/\/jedibets.com\/tools\/arbitrage-calculator\">arbitrage calculator<\/a> or a props screener, you have hit the same wall everyone hits. NFL odds data is either priced for enterprise buyers (Sportradar, Genius) or capped at a dozen retail books that all copy the same opening number. Neither gives you what you need, which is the sharp price next to the soft price.<\/p>\n<p>This guide fixes that. It also fixes the mistake that breaks most NFL scripts on day one: hardcoded market IDs.<\/p>\n<h2>What the NFL feed looks like right now<\/h2>\n<p>Numbers pulled live on 28 July 2026, six weeks before kickoff:<\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Window<\/th>\n<th>Fixtures with odds<\/th>\n<th>Depth<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>NFL Preseason, from 6 Aug<\/td>\n<td>10<\/td>\n<td>Opener carries 14 books including Pinnacle, SBOBet, Kalshi and Polymarket<\/td>\n<\/tr>\n<tr>\n<td>NFL Week 1, 10 to 13 Sep<\/td>\n<td>14 (all of them)<\/td>\n<td>16 books, 28 to 31 distinct markets per game<\/td>\n<\/tr>\n<tr>\n<td>NCAA, 5 to 14 Sep<\/td>\n<td>51<\/td>\n<td>14 books, SBOBet the only sharp so far<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p>Lines are already live and moving. You do not have to wait for September to build against real data.<\/p>\n<h2>NFL odds API providers compared<\/h2>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Provider<\/th>\n<th>Bookmakers<\/th>\n<th>Sharp books<\/th>\n<th>Historical odds<\/th>\n<th>Free tier<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><strong>OddsPapi<\/strong><\/td>\n<td>350+ (381 live)<\/td>\n<td>Pinnacle, SBOBet, Circa, exchanges<\/td>\n<td>Yes, free<\/td>\n<td>Yes<\/td>\n<\/tr>\n<tr>\n<td>The Odds API<\/td>\n<td>~15 US books<\/td>\n<td>Pinnacle<\/td>\n<td>Paid add-on<\/td>\n<td>Yes, capped<\/td>\n<\/tr>\n<tr>\n<td>SportsDataIO<\/td>\n<td>~10 books<\/td>\n<td>No<\/td>\n<td>Limited<\/td>\n<td>Trial only<\/td>\n<\/tr>\n<tr>\n<td>Sportradar<\/td>\n<td>Enterprise<\/td>\n<td>Yes<\/td>\n<td>Yes<\/td>\n<td>None<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p>The difference that matters for NFL: retail books shade their lines toward public money, so a feed made only of DraftKings, FanDuel and BetMGM shows you the same shaded number three times. Pinnacle priced our Week 1 test game at 3.18% vig against Bet365&#8217;s 4.40%. You need both sides of that to know what anything is worth.<\/p>\n<h2>Terminology: US sports on a global feed<\/h2>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>US concept<\/th>\n<th>API term<\/th>\n<th>What to watch for<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>League<\/td>\n<td><code>tournament<\/code><\/td>\n<td>NFL is <code>tournamentId<\/code> 31 inside sport 14<\/td>\n<\/tr>\n<tr>\n<td>Game<\/td>\n<td><code>fixture<\/code><\/td>\n<td>Everything keys off <code>fixtureId<\/code><\/td>\n<\/tr>\n<tr>\n<td>Team<\/td>\n<td><code>participant<\/code><\/td>\n<td><code>participant1Name<\/code> and <code>participant2Name<\/code><\/td>\n<\/tr>\n<tr>\n<td>Moneyline<\/td>\n<td>Market <code>141<\/code><\/td>\n<td>Stable. Outcomes 141 and 142<\/td>\n<\/tr>\n<tr>\n<td>Spread<\/td>\n<td>Handicap markets<\/td>\n<td><strong>One market ID per line.<\/strong> -3.5 is a different ID from -4<\/td>\n<\/tr>\n<tr>\n<td>Total<\/td>\n<td>Total markets<\/td>\n<td><strong>One market ID per line.<\/strong> 44.5 is a different ID from 45<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p>The last two rows are where scripts break. There is no single &#8220;spreads&#8221; endpoint or single totals market ID. Read on.<\/p>\n<h2>Step 1: Authenticate and find the NFL<\/h2>\n<p>The API key goes in the query string, not a header. That means you can paste any of these URLs straight into a browser to check them.<\/p>\n<pre class=\"wp-block-code\"><code>import time\nimport requests\n\nAPI_KEY = \"YOUR_API_KEY\"\nBASE_URL = \"https:\/\/api.oddspapi.io\/v4\"\nNFL_SPORT_ID = 14\n\n\ndef api(path, **params):\n    \"\"\"GET with the retry the free tier expects.\"\"\"\n    for _ in range(4):\n        r = requests.get(f\"{BASE_URL}\/{path}\",\n                         params={\"apiKey\": API_KEY, **params})\n        body = r.json()\n        if r.status_code == 200 and not (isinstance(body, dict) and body.get(\"error\")):\n            return body\n        wait = (body.get(\"error\") or {}).get(\"retryMs\", 2000) \/ 1000\n        time.sleep(wait + 0.3)\n    raise RuntimeError(f\"{path} kept rate-limiting\")\n\n\ndef find_tournament(name):\n    for t in api(\"tournaments\", sportId=NFL_SPORT_ID):\n        if t[\"tournamentName\"] == name:\n            return t\n\n\nnfl = find_tournament(\"NFL\")\nprint(nfl[\"tournamentId\"], nfl[\"futureFixtures\"])   # 31 105\n<\/code><\/pre>\n<p>Rate limits are per endpoint and return a real HTTP 429 with a <code>retryMs<\/code> value in the body. Sleep about a second between calls to the same endpoint and honour <code>retryMs<\/code> when you see it. Do not thread these calls. Firing twelve concurrent requests at <code>\/odds<\/code> gets eleven of them rejected.<\/p>\n<h2>Step 2: Pull the schedule<\/h2>\n<p>The <code>from<\/code> and <code>to<\/code> parameters accept a maximum ten-day span. Filter on <code>hasOdds<\/code> so you never waste an odds call on a game nobody has priced.<\/p>\n<pre class=\"wp-block-code\"><code>def nfl_games(start, end):\n    games = api(\"fixtures\", sportId=NFL_SPORT_ID, **{\"from\": start, \"to\": end})\n    return [g for g in games\n            if g.get(\"hasOdds\") and g.get(\"tournamentName\") == \"NFL\"]\n\n\ngames = nfl_games(\"2026-09-05\", \"2026-09-14\")\ng = games[0]\nprint(g[\"participant1Name\"], \"v\", g[\"participant2Name\"], g[\"fixtureId\"])\n# Seattle Seahawks v New England Patriots id1400003171515752\n<\/code><\/pre>\n<p>Filtering on <code>tournamentName<\/code> matters in September, when the same sport ID also carries 551 NCAA fixtures and the CFL.<\/p>\n<h2>Step 3: Parse the moneyline<\/h2>\n<p>The odds payload nests four levels deep: bookmaker, market, outcome, then a <code>players<\/code> dict holding the price. On game lines the players key is always <code>\"0\"<\/code>.<\/p>\n<pre class=\"wp-block-code\"><code>def prices(odds, market_id):\n    \"\"\"{bookmaker: {outcome_id: price_object}} for one market.\"\"\"\n    out = {}\n    for slug, book in odds.get(\"bookmakerOdds\", {}).items():\n        market = book.get(\"markets\", {}).get(str(market_id))\n        if not market:\n            continue\n        row = {}\n        for outcome_id, outcome in market[\"outcomes\"].items():\n            p = outcome[\"players\"].get(\"0\")\n            if p and p.get(\"active\") is not False:\n                row[outcome_id] = p\n        if row:\n            out[slug] = row\n    return out\n\n\ndef american(dec):\n    return f\"+{round((dec - 1) * 100)}\" if dec >= 2 else f\"{round(-100 \/ (dec - 1))}\"\n\n\nodds = api(\"odds\", fixtureId=g[\"fixtureId\"])\nfor slug, row in sorted(prices(odds, 141).items(), key=lambda kv: kv[1][\"141\"][\"price\"]):\n    p1, p2 = row[\"141\"][\"price\"], row[\"142\"][\"price\"]\n    print(f\"{slug:18} {american(p1):>5} \/ {american(p2)}\")\n<\/code><\/pre>\n<p>Real output from Seahawks at Patriots, fifteen books deep:<\/p>\n<pre class=\"wp-block-code\"><code>betparx              -233 \/ +185\nballybet             -233 \/ +185\nfourwinds            -233 \/ +185\nbetrivers            -233 \/ +180\nhardrockbet          -210 \/ +165\npinnacle             -206 \/ +179\nbet365               -200 \/ +165\nbetmgm               -200 \/ +165\nborgata              -200 \/ +165\ncaesars              -200 \/ +166\nwilliamhill          -200 \/ +166\npointsbet.com.au     -200 \/ +165\ndraftkings           -192 \/ +160\ncircasports          -190 \/ +165\nkalshi               -186 \/ +178\n<\/code><\/pre>\n<p>Two things to take from that list. Seattle ranges from -233 to -186 across fifteen books, so shopping the same side is worth roughly 7.6% on price. And those fifteen slugs are only nine independent opinions: <code>betparx<\/code>, <code>ballybet<\/code> and <code>fourwinds<\/code> quote identical numbers, as do <code>betmgm<\/code> and <code>borgata<\/code>, and <code>caesars<\/code> and <code>williamhill<\/code>. Dedupe on the price tuple before you average anything or you will triple-weight one trading desk.<\/p>\n<p>The feed also ships <code>priceAmerican<\/code> and <code>priceFractional<\/code> on every outcome, so the converter above is optional. Write it once if you want the control, skip it if you do not.<\/p>\n<h2>Step 4: Spreads, and why hardcoding market IDs fails<\/h2>\n<p>NFL spreads live in Handicap markets, and every line is a separate market ID. There is no fixed ID for &#8220;the spread&#8221;. A guide that tells you to fetch market 1076 for -3.5 is quoting soccer IDs and will hand you an empty dict on every NFL game.<\/p>\n<p>Build the index from <code>\/markets<\/code> instead, then ask the fixture which lines are actually quoted:<\/p>\n<pre class=\"wp-block-code\"><code>def market_index(sport_id):\n    catalog = api(\"markets\", sportId=sport_id)\n    by_id = {m[\"marketId\"]: (m[\"marketName\"], m.get(\"handicap\")) for m in catalog}\n    outcomes = {(m[\"marketId\"], o[\"outcomeId\"]): o[\"outcomeName\"]\n                for m in catalog for o in m.get(\"outcomes\", [])}\n    return by_id, outcomes\n\n\ndef find_market(odds, by_id, name):\n    \"\"\"Which lines are on the board, and how many books quote each.\"\"\"\n    hits = {}\n    for slug, book in odds.get(\"bookmakerOdds\", {}).items():\n        for mid in book.get(\"markets\", {}):\n            label, handicap = by_id.get(int(mid), (\"\", None))\n            if label == name:\n                hits.setdefault((int(mid), handicap), []).append(slug)\n    return dict(sorted(hits.items(), key=lambda kv: -len(kv[1])))\n\n\nby_id, outcome_names = market_index(NFL_SPORT_ID)\nfor (mid, line), books in find_market(odds, by_id, \"Handicap (incl. overtime)\").items():\n    print(f\"market {mid:<7} line {line:>6}  {len(books)} books\")\n<\/code><\/pre>\n<pre class=\"wp-block-code\"><code>market 14272   line   -3.5  7 books\nmarket 14270   line     -4  3 books\nmarket 14302   line      4  3 books\nmarket 14300   line    3.5  2 books\nmarket 14268   line   -4.5  2 books\nmarket 14244   line  -10.5  1 books\nmarket 14266   line     -5  1 books\nmarket 14262   line     -6  1 books\nmarket 14274   line     -3  1 books\nmarket 14264   line   -5.5  1 books\n<\/code><\/pre>\n<p>The consensus line is -3.5 on seven books. Everything below it is Pinnacle alone, walking its alternate ladder out to -10.5. The book with the most lines on the board is the one that will take a bet on any of them, which tells you something before you have priced anything yourself.<\/p>\n<p>Sort by book count and take the top entry and you have the main line, without hardcoding a thing. That pattern survives a season of line moves.<\/p>\n<h2>Step 5: Totals work the same way<\/h2>\n<pre class=\"wp-block-code\"><code>for (mid, line), books in find_market(odds, by_id, \"Total (incl. overtime)\").items():\n    if len(books) &gt;= 2:\n        print(f\"market {mid:<7} total {line:>6}  {len(books)} books\")\n\n# market 1464    total   44.5  12 books   &lt;- main line\n# market 1466    total     45   5 books\n# market 1432    total   36.5   3 books\n# market 1460    total   43.5   3 books\n<\/code><\/pre>\n<p>Market 1464 carries Over as outcome 1464 and Under as 1465. Line shop it and the spread across books is real money:<\/p>\n<pre class=\"wp-block-code\"><code>totals = prices(odds, 1464)\nfor side, label in ((\"1464\", \"Over 44.5\"), (\"1465\", \"Under 44.5\")):\n    quotes = sorted(((s, r[side][\"price\"]) for s, r in totals.items() if side in r),\n                    key=lambda x: -x[1])\n    print(f\"{label}: best {quotes[0][1]} @ {quotes[0][0]}, worst {quotes[-1][1]} @ {quotes[-1][0]}\")\n\n# Over 44.5:  best 2.02 @ pinnacle,  worst 1.9 @ draftkings\n# Under 44.5: best 1.91 @ betmgm,    worst 1.819 @ pinnacle\n<\/code><\/pre>\n<p>Pinnacle is the best price on the Over and the worst on the Under, which is what a low-margin book looks like when its number sits slightly off the retail consensus. Taking the best of each side across books beats any single book on both. For the full version of this across every market, see our guide to <a href=\"https:\/\/oddspapi.io\/blog\/line-shopping-python-best-odds\/\">line shopping in Python<\/a>.<\/p>\n<h2>Step 6: Measure the vig<\/h2>\n<p>Sum the inverse decimal prices on both sides. Anything above 1.0 is the book&#8217;s margin.<\/p>\n<pre class=\"wp-block-code\"><code>ml = prices(odds, 141)\nfor slug in (\"pinnacle\", \"draftkings\", \"bet365\", \"caesars\"):\n    if slug in ml:\n        p1, p2 = ml[slug][\"141\"][\"price\"], ml[slug][\"142\"][\"price\"]\n        print(f\"{slug:12} vig {(1\/p1 + 1\/p2 - 1) * 100:.2f}%\")\n\n# pinnacle     vig 3.18%\n# draftkings   vig 4.25%\n# caesars      vig 4.26%\n# bet365       vig 4.40%\n<\/code><\/pre>\n<p>Strip that margin out and you get the market&#8217;s honest probability, which is the benchmark any model has to beat. Three ways to do it are in our <a href=\"https:\/\/oddspapi.io\/blog\/no-vig-odds-api\/\">no-vig odds guide<\/a>.<\/p>\n<h2>Step 7: Check the limit before you trust the price<\/h2>\n<p>Pinnacle and the exchanges publish a <code>limit<\/code> on every outcome, which is the maximum they will accept. It doubles as a confidence signal, and in July the NFL numbers are small:<\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Market<\/th>\n<th>Price<\/th>\n<th>Pinnacle limit<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Moneyline, Seattle<\/td>\n<td>1.485<\/td>\n<td>$1,546<\/td>\n<\/tr>\n<tr>\n<td>Moneyline, New England<\/td>\n<td>2.79<\/td>\n<td>$750<\/td>\n<\/tr>\n<tr>\n<td>Spread -3.5, Seattle<\/td>\n<td>1.877<\/td>\n<td>$1,710<\/td>\n<\/tr>\n<tr>\n<td>Spread -3.5, New England<\/td>\n<td>1.99<\/td>\n<td>$1,515<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p>Pinnacle will take more than twice as much on the spread as on the moneyline, and it caps a live MLB moneyline at eight thousand dollars while capping this one at fifteen hundred. Six weeks out, the sharpest book in the market is telling you it does not trust its own September number yet. Those limits climb as kickoff approaches. US retail books return <code>null<\/code> here, because their maximum is set per account rather than per market, so null-check before you do arithmetic.<\/p>\n<h2>Step 8: Historical odds and closing line value<\/h2>\n<p>Every price the feed has ever seen is retrievable through <code>\/historical-odds<\/code>, on the free tier. Competitors charge for this or do not offer it.<\/p>\n<pre class=\"wp-block-code\"><code>def price_history(fixture_id, book, market_id, outcome_id):\n    data = api(\"historical-odds\", fixtureId=fixture_id, bookmakers=book)\n    market = data[\"bookmakers\"][book][\"markets\"][str(market_id)]\n    snaps = market[\"outcomes\"][str(outcome_id)][\"players\"][\"0\"]\n    return [(s[\"createdAt\"][:16], s[\"price\"], s.get(\"limit\")) for s in snaps]\n<\/code><\/pre>\n<p>Note the shape change: the live endpoint keys on <code>bookmakerOdds<\/code> and gives you one price, the historical endpoint keys on <code>bookmakers<\/code> and gives you a list of snapshots. Mixing them up is the second most common NFL parsing bug after the market IDs. The endpoint takes a maximum of three bookmakers per call, so loop and merge for wider coverage. From there you have opening lines, closing lines, and everything between, which is all you need to grade your bets against the close or <a href=\"https:\/\/oddspapi.io\/blog\/historical-odds-csv-excel-backtesting\/\">export a season to CSV<\/a> for backtesting.<\/p>\n<h2>Player props: where things stand<\/h2>\n<p>The NFL prop markets exist in the catalog, including Player To Score TD (market 14388) and Player To Score First TD (14390). Pricing is a different question. Across six Week 1 games sampled in late July, DraftKings had an anytime touchdown market on two of them and no other book had posted a single prop.<\/p>\n<p>That is normal. Prop menus fill in as kickoff approaches, so a scan six weeks out reads empty and the same scan on game day returns a full board. When they do arrive, the parse changes: on prop markets the <code>players<\/code> dict is keyed by player ID rather than <code>\"0\"<\/code>, with a <code>playerName<\/code> on each entry in &#8220;Last, First&#8221; format. Hardcode <code>players[\"0\"]<\/code> and every prop market will look empty to you. Our <a href=\"https:\/\/oddspapi.io\/blog\/player-props-api-nfl-nba-mlb-odds-python\/\">player props API guide<\/a> has the full pattern.<\/p>\n<h2>College football<\/h2>\n<p>Same sport ID, same code, different tournament. NCAA carried 51 priced fixtures for the 5 to 14 September window when we checked, across 14 books, with up to 90 markets on the bigger matchups. Pinnacle had not posted yet and SBOBet was the only sharp on the board, so treat early college numbers as retail consensus rather than a sharp reference. Swap the filter and everything else in this guide runs unchanged:<\/p>\n<pre class=\"wp-block-code\"><code>ncaa = [g for g in api(\"fixtures\", sportId=14, **{\"from\": \"2026-09-05\", \"to\": \"2026-09-14\"})\n        if g.get(\"hasOdds\") and g[\"tournamentName\"].startswith(\"NCAA\")]\n<\/code><\/pre>\n<h2>Frequently Asked Questions<\/h2>\n<h3>What is the market ID for NFL spreads?<\/h3>\n<p>There is no single one. Each spread line is its own market ID, so -3.5 and -4 are different markets. On our Week 1 test game, -3.5 was market 14272 and -4 was 14270. Query <code>\/markets?sportId=14<\/code>, match on the market name &#8220;Handicap (incl. overtime)&#8221;, and pick the line the most books are quoting rather than hardcoding an ID.<\/p>\n<h3>Which bookmakers does the NFL odds API cover?<\/h3>\n<p>A Week 1 fixture returned 16 books: Pinnacle, SBOBet, DraftKings, BetMGM, Caesars, Bet365, BetRivers, William Hill, Circa Sports, HardRock Bet, PointsBet, Kalshi, plus several regional US skins. The wider catalogue runs to 381 bookmakers, though any single fixture carries a subset.<\/p>\n<h3>Is NFL player prop data available?<\/h3>\n<p>The markets exist year round, but books post prop menus close to kickoff. In late July only DraftKings had posted an anytime touchdown market on Week 1 games. Expect a full board in the days before each game rather than weeks out.<\/p>\n<h3>How do I get historical NFL odds for backtesting?<\/h3>\n<p>Call <code>\/historical-odds<\/code> with a fixture ID and up to three bookmakers. It returns timestamped snapshots, including the limit at each timestamp, so you can reconstruct opening lines, closing lines and how the market moved. It is included in the free tier.<\/p>\n<h3>Does the API include NFL scores or player stats?<\/h3>\n<p>No. The feed carries schedules, fixture status and odds. For results and box scores you will need a separate stats provider, which you can join on the IDs in the <code>externalProviders<\/code> object on every fixture.<\/p>\n<h2>Start building<\/h2>\n<p>You now have working code that finds the NFL, pulls the schedule, parses moneylines across fifteen books, discovers whichever spread and total lines are actually on the board, measures the vig, reads the sharp limits and reconstructs price history. None of it needs a sales call.<\/p>\n<p><a href=\"https:\/\/oddspapi.io\/blog\/free-odds-api-350-bookmakers\/\">Get your free API key<\/a> and have the preseason board on your screen before August.<\/p>\n<p><script type=\"application\/ld+json\">\n{\n  \"@context\": \"https:\/\/schema.org\",\n  \"@type\": \"FAQPage\",\n  \"mainEntity\": [\n    {\n      \"@type\": \"Question\",\n      \"name\": \"What is the market ID for NFL spreads?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"There is no single one. Each spread line is its own market ID, so -3.5 and -4 are different markets. On a Week 1 test game, -3.5 was market 14272 and -4 was 14270. Query \/markets?sportId=14, match on the market name 'Handicap (incl. overtime)', and pick the line the most books are quoting rather than hardcoding an ID.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"Which bookmakers does the NFL odds API cover?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"A Week 1 fixture returned 16 books: Pinnacle, SBOBet, DraftKings, BetMGM, Caesars, Bet365, BetRivers, William Hill, Circa Sports, HardRock Bet, PointsBet, Kalshi, plus several regional US skins. The wider catalogue runs to 381 bookmakers, though any single fixture carries a subset.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"Is NFL player prop data available?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"The markets exist year round, but books post prop menus close to kickoff. In late July only DraftKings had posted an anytime touchdown market on Week 1 games. Expect a full board in the days before each game rather than weeks out.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"How do I get historical NFL odds for backtesting?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Call \/historical-odds with a fixture ID and up to three bookmakers. It returns timestamped snapshots, including the limit at each timestamp, so you can reconstruct opening lines, closing lines and how the market moved. It is included in the free tier.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"Does the API include NFL scores or player stats?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"No. The feed carries schedules, fixture status and odds. For results and box scores you will need a separate stats provider, which you can join on the IDs in the externalProviders object on every fixture.\"\n      }\n    }\n  ]\n}\n<\/script><\/p>\n<p><!--\nFocus Keyphrase: nfl odds api\nSEO Title: NFL Odds API: Free Python Guide to Lines, Spreads & Totals (2026)\nMeta Description: Free NFL odds API with tested Python code. Pull live moneylines, spreads and totals from 16 books including Pinnacle, plus free historical odds.\nSlug: free-nfl-odds-api-guide\n--><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Free NFL odds API with tested Python code. Pull live moneylines, spreads and totals from 16 books including Pinnacle, plus free historical odds.<\/p>\n","protected":false},"author":2,"featured_media":60,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7,6],"tags":[8,52,9,11,10],"class_list":["post-58","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-how-to-guides","category-nfl","tag-free-api","tag-nfl","tag-odds-api","tag-python","tag-sports-betting-api"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.4 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>NFL Odds API: Free Python Guide to Lines, Spreads &amp; Totals (2026) | OddsPapi Blog<\/title>\n<meta name=\"description\" content=\"Learn to fetch real-time NFL lines, spreads, and props with Python. The best free NFL Odds API featuring 300+ bookmakers, native American odds, and historical data.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"NFL Odds API: Free Python Guide to Lines, Spreads &amp; Totals (2026) | OddsPapi Blog\" \/>\n<meta property=\"og:description\" content=\"Learn to fetch real-time NFL lines, spreads, and props with Python. The best free NFL Odds API featuring 300+ bookmakers, native American odds, and historical data.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/\" \/>\n<meta property=\"og:site_name\" content=\"OddsPapi Blog\" \/>\n<meta property=\"article:published_time\" content=\"2025-11-25T18:27:17+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-07-28T20:50:01+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2025\/11\/free-nfl-odds-api-guide.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1408\" \/>\n\t<meta property=\"og:image:height\" content=\"768\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Odds API Writer\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/oddspapi.io\/logo-v2.webp\" \/>\n<meta name=\"twitter:creator\" content=\"@oddspapiapi\" \/>\n<meta name=\"twitter:site\" content=\"@oddspapiapi\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Odds API Writer\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/\"},\"author\":{\"name\":\"Odds API Writer\",\"@id\":\"https:\/\/oddspapi.io\/blog\/#\/schema\/person\/b6f21e649c4f556f0a95c23a0f1efa13\"},\"headline\":\"NFL Odds API: Free Python Guide to Lines, Spreads &#038; Totals (2026)\",\"datePublished\":\"2025-11-25T18:27:17+00:00\",\"dateModified\":\"2026-07-28T20:50:01+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/\"},\"wordCount\":1695,\"commentCount\":2,\"publisher\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2025\/11\/free-nfl-odds-api-guide.jpg\",\"keywords\":[\"Free API\",\"NFL\",\"Odds API\",\"Python\",\"Sports Betting API\"],\"articleSection\":[\"How To Guides\",\"NFL\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/\",\"url\":\"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/\",\"name\":\"NFL Odds API: Free Python Guide to Lines, Spreads & Totals (2026) | OddsPapi Blog\",\"isPartOf\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2025\/11\/free-nfl-odds-api-guide.jpg\",\"datePublished\":\"2025-11-25T18:27:17+00:00\",\"dateModified\":\"2026-07-28T20:50:01+00:00\",\"description\":\"Learn to fetch real-time NFL lines, spreads, and props with Python. The best free NFL Odds API featuring 300+ bookmakers, native American odds, and historical data.\",\"breadcrumb\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/#primaryimage\",\"url\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2025\/11\/free-nfl-odds-api-guide.jpg\",\"contentUrl\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2025\/11\/free-nfl-odds-api-guide.jpg\",\"width\":1408,\"height\":768},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/oddspapi.io\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"NFL Odds API: Free Python Guide to Lines, Spreads &#038; Totals (2026)\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/oddspapi.io\/blog\/#website\",\"url\":\"https:\/\/oddspapi.io\/blog\/\",\"name\":\"OddsPapi\",\"description\":\"Sports Odds API Tutorials &amp; Guides\",\"publisher\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/#organization\"},\"alternateName\":\"Odds Papi\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/oddspapi.io\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/oddspapi.io\/blog\/#organization\",\"name\":\"OddsPapi\",\"url\":\"https:\/\/oddspapi.io\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/oddspapi.io\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2025\/11\/oddspapi.png\",\"contentUrl\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2025\/11\/oddspapi.png\",\"width\":135,\"height\":135,\"caption\":\"OddsPapi\"},\"image\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/x.com\/oddspapiapi\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/oddspapi.io\/blog\/#\/schema\/person\/b6f21e649c4f556f0a95c23a0f1efa13\",\"name\":\"Odds API Writer\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/oddspapi.io\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/33b204f24af3d02e35b25ae730c0536121ca6a783fdb196e7611c9e49fcd13eb?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/33b204f24af3d02e35b25ae730c0536121ca6a783fdb196e7611c9e49fcd13eb?s=96&d=mm&r=g\",\"caption\":\"Odds API Writer\"},\"url\":\"https:\/\/oddspapi.io\/blog\/author\/andy-lavelle\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"NFL Odds API: Free Python Guide to Lines, Spreads & Totals (2026) | OddsPapi Blog","description":"Learn to fetch real-time NFL lines, spreads, and props with Python. The best free NFL Odds API featuring 300+ bookmakers, native American odds, and historical data.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/","og_locale":"en_US","og_type":"article","og_title":"NFL Odds API: Free Python Guide to Lines, Spreads & Totals (2026) | OddsPapi Blog","og_description":"Learn to fetch real-time NFL lines, spreads, and props with Python. The best free NFL Odds API featuring 300+ bookmakers, native American odds, and historical data.","og_url":"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/","og_site_name":"OddsPapi Blog","article_published_time":"2025-11-25T18:27:17+00:00","article_modified_time":"2026-07-28T20:50:01+00:00","og_image":[{"width":1408,"height":768,"url":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2025\/11\/free-nfl-odds-api-guide.jpg","type":"image\/jpeg"}],"author":"Odds API Writer","twitter_card":"summary_large_image","twitter_image":"https:\/\/oddspapi.io\/logo-v2.webp","twitter_creator":"@oddspapiapi","twitter_site":"@oddspapiapi","twitter_misc":{"Written by":"Odds API Writer","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/#article","isPartOf":{"@id":"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/"},"author":{"name":"Odds API Writer","@id":"https:\/\/oddspapi.io\/blog\/#\/schema\/person\/b6f21e649c4f556f0a95c23a0f1efa13"},"headline":"NFL Odds API: Free Python Guide to Lines, Spreads &#038; Totals (2026)","datePublished":"2025-11-25T18:27:17+00:00","dateModified":"2026-07-28T20:50:01+00:00","mainEntityOfPage":{"@id":"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/"},"wordCount":1695,"commentCount":2,"publisher":{"@id":"https:\/\/oddspapi.io\/blog\/#organization"},"image":{"@id":"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/#primaryimage"},"thumbnailUrl":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2025\/11\/free-nfl-odds-api-guide.jpg","keywords":["Free API","NFL","Odds API","Python","Sports Betting API"],"articleSection":["How To Guides","NFL"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/","url":"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/","name":"NFL Odds API: Free Python Guide to Lines, Spreads & Totals (2026) | OddsPapi Blog","isPartOf":{"@id":"https:\/\/oddspapi.io\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/#primaryimage"},"image":{"@id":"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/#primaryimage"},"thumbnailUrl":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2025\/11\/free-nfl-odds-api-guide.jpg","datePublished":"2025-11-25T18:27:17+00:00","dateModified":"2026-07-28T20:50:01+00:00","description":"Learn to fetch real-time NFL lines, spreads, and props with Python. The best free NFL Odds API featuring 300+ bookmakers, native American odds, and historical data.","breadcrumb":{"@id":"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/#primaryimage","url":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2025\/11\/free-nfl-odds-api-guide.jpg","contentUrl":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2025\/11\/free-nfl-odds-api-guide.jpg","width":1408,"height":768},{"@type":"BreadcrumbList","@id":"https:\/\/oddspapi.io\/blog\/free-nfl-odds-api-guide\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/oddspapi.io\/blog\/"},{"@type":"ListItem","position":2,"name":"NFL Odds API: Free Python Guide to Lines, Spreads &#038; Totals (2026)"}]},{"@type":"WebSite","@id":"https:\/\/oddspapi.io\/blog\/#website","url":"https:\/\/oddspapi.io\/blog\/","name":"OddsPapi","description":"Sports Odds API Tutorials &amp; Guides","publisher":{"@id":"https:\/\/oddspapi.io\/blog\/#organization"},"alternateName":"Odds Papi","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/oddspapi.io\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/oddspapi.io\/blog\/#organization","name":"OddsPapi","url":"https:\/\/oddspapi.io\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/oddspapi.io\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2025\/11\/oddspapi.png","contentUrl":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2025\/11\/oddspapi.png","width":135,"height":135,"caption":"OddsPapi"},"image":{"@id":"https:\/\/oddspapi.io\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/oddspapiapi"]},{"@type":"Person","@id":"https:\/\/oddspapi.io\/blog\/#\/schema\/person\/b6f21e649c4f556f0a95c23a0f1efa13","name":"Odds API Writer","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/oddspapi.io\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/33b204f24af3d02e35b25ae730c0536121ca6a783fdb196e7611c9e49fcd13eb?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/33b204f24af3d02e35b25ae730c0536121ca6a783fdb196e7611c9e49fcd13eb?s=96&d=mm&r=g","caption":"Odds API Writer"},"url":"https:\/\/oddspapi.io\/blog\/author\/andy-lavelle\/"}]}},"_links":{"self":[{"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/posts\/58","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/comments?post=58"}],"version-history":[{"count":14,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/posts\/58\/revisions"}],"predecessor-version":[{"id":3164,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/posts\/58\/revisions\/3164"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/media\/60"}],"wp:attachment":[{"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/media?parent=58"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/categories?post=58"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/tags?post=58"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}