{"id":2529,"date":"2026-03-24T10:00:00","date_gmt":"2026-03-24T10:00:00","guid":{"rendered":"https:\/\/oddspapi.io\/blog\/?p=2529"},"modified":"2026-03-13T13:27:58","modified_gmt":"2026-03-13T13:27:58","slug":"odds-comparison-dashboard-python-streamlit","status":"publish","type":"post","link":"https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/","title":{"rendered":"Build a Live Odds Dashboard with Python &#038; Streamlit (Free API)"},"content":{"rendered":"<p>Stop tabbing between 5 sportsbook sites. Build your own odds comparison dashboard in 20 minutes \u2014 one screen, every bookmaker, best prices highlighted automatically.<\/p>\n<p>Manual line shopping is the worst kind of grind. You open Bet365, check the line, switch to DraftKings, check it again, flip to FanDuel \u2014 and by the time you&#8217;ve finished, the line you wanted has already moved. And that&#8217;s with 3-4 books. You&#8217;re leaving serious edge on the table because you simply can&#8217;t see what&#8217;s happening across 300+ bookmakers simultaneously.<\/p>\n<p>This guide fixes that. We&#8217;ll build a live odds comparison dashboard using Python, Streamlit, and the OddsPapi API \u2014 free to run, free to host on Streamlit Cloud, and genuinely more powerful than most $100\/month tools.<\/p>\n<h2>Why Manual Line Shopping Doesn&#8217;t Work<\/h2>\n<p>Here&#8217;s what you&#8217;re actually up against when you try to shop lines manually:<\/p>\n<ul>\n<li><strong>Account limits:<\/strong> You physically can&#8217;t have accounts at 300+ bookmakers. Most bettors are comparing 3-5 books at best.<\/li>\n<li><strong>Speed:<\/strong> By the time you&#8217;ve tabbed through your accounts, the sharp money has already moved the line at Pinnacle. You&#8217;re reacting to yesterday&#8217;s price.<\/li>\n<li><strong>No automated alerts:<\/strong> There&#8217;s no way to get notified when an arbitrage window opens or a line diverges significantly across books.<\/li>\n<li><strong>Paid tools are expensive:<\/strong> OddsJam, OddsBoom, and similar services charge $50\u2013200\/month for a curated subset of bookmakers \u2014 and you don&#8217;t control the logic.<\/li>\n<\/ul>\n<p>The better path: build it yourself with a real odds aggregator API, take full control of the logic, and pay nothing.<\/p>\n<h2>Manual Tab-Switching vs. Paid Tools vs. Building Your Own<\/h2>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>Feature<\/th>\n<th>Manual Tab-Switching<\/th>\n<th>Paid Tools ($99\/mo)<\/th>\n<th>Build Your Own (OddsPapi)<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Bookmakers covered<\/td>\n<td>3\u20135<\/td>\n<td>15\u201340<\/td>\n<td>300+<\/td>\n<\/tr>\n<tr>\n<td>Real-time data<\/td>\n<td>No<\/td>\n<td>Delayed<\/td>\n<td>Yes (REST + WebSocket)<\/td>\n<\/tr>\n<tr>\n<td>Custom alerts<\/td>\n<td>No<\/td>\n<td>Limited presets<\/td>\n<td>Full control<\/td>\n<\/tr>\n<tr>\n<td>Historical data<\/td>\n<td>No<\/td>\n<td>Extra $$<\/td>\n<td>Free tier included<\/td>\n<\/tr>\n<tr>\n<td>Arbitrage detection<\/td>\n<td>No<\/td>\n<td>Basic alerts<\/td>\n<td>Custom logic<\/td>\n<\/tr>\n<tr>\n<td>Monthly cost<\/td>\n<td>$0<\/td>\n<td>$99\u2013199\/mo<\/td>\n<td>Free<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<h2>What You&#8217;ll Build<\/h2>\n<p>By the end of this tutorial, you&#8217;ll have a working Streamlit dashboard with:<\/p>\n<ul>\n<li><strong>Sport + fixture selector<\/strong> \u2014 pick Premier League, NBA, or any supported tournament<\/li>\n<li><strong>Bookmaker comparison table<\/strong> \u2014 all available books side-by-side, with best prices auto-highlighted<\/li>\n<li><strong>Bar chart visualisation<\/strong> \u2014 Plotly chart comparing featured bookmakers (Pinnacle, Bet365, DraftKings, 1xBet) per outcome<\/li>\n<li><strong>Line shopping edge calculator<\/strong> \u2014 shows best available price per outcome and which book offers it<\/li>\n<li><strong>Arbitrage detector<\/strong> \u2014 fires an alert when combined implied probabilities fall below 100%<\/li>\n<li><strong>Auto-refresh toggle<\/strong> \u2014 polls the API every 60 seconds without manual page reloads<\/li>\n<\/ul>\n<p>The stack: <strong>Python + Streamlit + Plotly + Pandas + OddsPapi REST API<\/strong>. No paid subscriptions. No vendor lock-in.<\/p>\n<h2>Step-by-Step Tutorial<\/h2>\n<h3>Step 1: Install Dependencies<\/h3>\n<p>Create a new project folder and install the required packages:<\/p>\n<pre class=\"wp-block-code\"><code>pip install streamlit plotly requests pandas<\/code><\/pre>\n<p>That&#8217;s it. No heavy ML frameworks, no complex setup. Streamlit handles the UI, Plotly handles the charts, and <code>requests<\/code> handles the API calls.<\/p>\n<h3>Step 2: Authenticate with the OddsPapi API<\/h3>\n<p>Create a file called <code>dashboard.py<\/code> and start with your credentials and a connection test:<\/p>\n<pre class=\"wp-block-code\"><code>import requests\n\nAPI_KEY = \"YOUR_API_KEY\"  # Get free at oddspapi.io\nBASE = \"https:\/\/api.oddspapi.io\/v4\"\n\n# Test connection\nr = requests.get(f\"{BASE}\/sports\", params={\"apiKey\": API_KEY})\nprint(r.status_code)  # Should be 200\nprint(r.json())\n<\/code><\/pre>\n<p><strong>Critical note:<\/strong> <code>apiKey<\/code> is a <strong>query parameter<\/strong>, not an Authorization header. Do not put it in <code>headers={}<\/code> \u2014 it won&#8217;t authenticate. Pass it via <code>params={\"apiKey\": API_KEY}<\/code> on every request.<\/p>\n<p>Get your free API key at <a href=\"https:\/\/oddspapi.io\">oddspapi.io<\/a>. The free tier gives you 250 requests per day, access to 59 sports, and free historical odds data \u2014 enough to run this dashboard and backtest your logic.<\/p>\n<h3>Step 3: Fetch Upcoming Fixtures<\/h3>\n<p>OddsPapi uses its own terminology that differs from US sportsbook conventions:<\/p>\n<figure class=\"wp-block-table\">\n<table>\n<thead>\n<tr>\n<th>US Term<\/th>\n<th>OddsPapi Term<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>League<\/td>\n<td>Tournament<\/td>\n<\/tr>\n<tr>\n<td>Game<\/td>\n<td>Fixture<\/td>\n<\/tr>\n<tr>\n<td>Team<\/td>\n<td>Participant<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p>Now fetch upcoming fixtures for a given sport and tournament:<\/p>\n<pre class=\"wp-block-code\"><code>import time\n\ndef fetch_fixtures(sport_id, tournament_id):\n    params = {\n        \"apiKey\": API_KEY,\n        \"sportId\": sport_id,\n        \"tournamentId\": tournament_id,\n        \"limit\": 300\n    }\n    r = requests.get(f\"{BASE}\/fixtures\", params=params, timeout=15)\n    r.raise_for_status()\n    data = r.json()\n    now = time.strftime(\"%Y-%m-%dT%H:%M:%S\")\n    # Filter to upcoming fixtures with odds available\n    return [f for f in data\n            if f.get(\"startTime\", \"\") >= now\n            and f.get(\"hasOdds\")][:20]\n<\/code><\/pre>\n<p>We filter for fixtures where <code>hasOdds<\/code> is true \u2014 no point fetching odds data for fixtures that don&#8217;t have any yet. We also cap at 20 to keep API usage manageable on the free tier.<\/p>\n<h3>Step 4: Pull Odds from All Bookmakers<\/h3>\n<p>This is where OddsPapi&#8217;s power becomes obvious. One API call returns odds from every available bookmaker for a given fixture. The JSON is deeply nested \u2014 here&#8217;s the exact path to a price:<\/p>\n<p><code>bookmakerOdds[slug] \u2192 markets[marketId] \u2192 outcomes[outcomeId] \u2192 players[\"0\"] \u2192 price<\/code><\/p>\n<pre class=\"wp-block-code\"><code>def fetch_odds(fixture_id):\n    r = requests.get(f\"{BASE}\/odds\",\n                     params={\"apiKey\": API_KEY, \"fixtureId\": fixture_id},\n                     timeout=15)\n    r.raise_for_status()\n    return r.json().get(\"bookmakerOdds\", {})\n\ndef extract_prices(bookmaker_odds, market_id, outcomes):\n    # Extract prices from all bookmakers for a given market.\n    prices = {}\n    for slug, book_data in bookmaker_odds.items():\n        market = book_data.get(\"markets\", {}).get(market_id, {})\n        outs = market.get(\"outcomes\", {})\n        book_prices = {}\n        for oid, label in outcomes.items():\n            price = (outs.get(oid, {})\n                     .get(\"players\", {})\n                     .get(\"0\", {})\n                     .get(\"price\"))\n            if price and price > 1:\n                book_prices[label] = price\n        if book_prices:\n            prices[slug] = book_prices\n    return prices\n<\/code><\/pre>\n<p>A few things to note:<\/p>\n<ul>\n<li><code>market_id<\/code> is a string key in the JSON (not an integer), so pass <code>\"101\"<\/code> not <code>101<\/code><\/li>\n<li>The <code>players[\"0\"]<\/code> key is literal \u2014 it&#8217;s always the string <code>\"0\"<\/code> for standard market outcomes<\/li>\n<li>We filter out prices \u2264 1.0 (invalid or suspended odds appear as 1.0 in some feeds)<\/li>\n<li>Soccer Full Time Result (1X2) uses market ID <code>101<\/code>; Basketball moneyline uses <code>111<\/code><\/li>\n<\/ul>\n<h3>Step 5: Line Shopping \u2014 Find Best Prices and Detect Arbitrage<\/h3>\n<p>Line shopping is straightforward: for each outcome, find the highest decimal odds across all bookmakers. If the combined implied probability of the best prices per outcome is below 100%, you have an arbitrage opportunity.<\/p>\n<pre class=\"wp-block-code\"><code>def find_best_prices(prices, outcome_labels):\n    # Find the highest price per outcome across all bookmakers.\n    best = {}\n    for label in outcome_labels:\n        best_price = 0\n        best_book = \"\"\n        for slug, p in prices.items():\n            if p.get(label, 0) > best_price:\n                best_price = p[label]\n                best_book = slug\n        best[label] = {\"price\": best_price, \"book\": best_book}\n    return best\n\ndef detect_arb(prices, outcome_labels):\n    # Check if best prices create an arbitrage opportunity.\n    # An arb exists when sum(1\/best_price_per_outcome) &lt; 1.0\n    # Margin = (1 - total_implied) * 100 = guaranteed profit %\n    best = find_best_prices(prices, outcome_labels)\n    total_implied = sum(\n        1 \/ best[lbl][\"price\"]\n        for lbl in outcome_labels\n        if best[lbl][\"price\"] > 0\n    )\n    if total_implied < 1.0:\n        margin = round((1 - total_implied) * 100, 2)\n        return {\"arb\": True, \"margin\": margin, \"best\": best}\n    return {\"arb\": False,\n            \"overround\": round((total_implied - 1) * 100, 2)}\n<\/code><\/pre>\n<p>Example: if the best Home price is 2.10 at Pinnacle, best Draw is 3.60 at 1xBet, and best Away is 4.20 at Singbet \u2014 implied probabilities are 47.6% + 27.8% + 23.8% = 99.2%. That 0.8% gap is a guaranteed profit window, no matter the outcome.<\/p>\n<p>OddsPapi's 300+ bookmaker coverage dramatically increases the frequency of these windows compared to tools that only show 15-40 books. More data sources = more divergence opportunities.<\/p>\n<h3>Step 6: Build the Streamlit Dashboard<\/h3>\n<p>Now wire everything together into an interactive dashboard:<\/p>\n<pre class=\"wp-block-code\"><code>import streamlit as st\nimport plotly.graph_objects as go\nimport pandas as pd\n\nst.set_page_config(page_title=\"Odds Dashboard\", layout=\"wide\")\nst.title(\"Live Odds Comparison Dashboard\")\nst.caption(\"Powered by OddsPapi \u2014 300+ bookmakers\")\n\nSPORT_CFG = {\n    \"Soccer \u2014 Premier League\": {\n        \"sportId\": 10, \"tournamentId\": 17,\n        \"marketId\": \"101\",\n        \"outcomes\": {\"101\": \"Home\", \"102\": \"Draw\", \"103\": \"Away\"},\n    },\n    \"Basketball \u2014 NBA\": {\n        \"sportId\": 11, \"tournamentId\": 132,\n        \"marketId\": \"111\",\n        \"outcomes\": {\"111\": \"Home\", \"112\": \"Away\"},\n    },\n}\n\nsport = st.selectbox(\"Sport\", list(SPORT_CFG.keys()))\ncfg = SPORT_CFG[sport]\nauto = st.toggle(\"Auto-refresh (60s)\", value=False)\noutcome_labels = list(cfg[\"outcomes\"].values())\n\nfixtures = fetch_fixtures(cfg[\"sportId\"], cfg[\"tournamentId\"])\n\nfor fx in fixtures[:5]:\n    name = f\"{fx['participant1Name']} vs {fx['participant2Name']}\"\n    bo = fetch_odds(fx[\"fixtureId\"])\n    prices = extract_prices(bo, cfg[\"marketId\"], cfg[\"outcomes\"])\n    best = find_best_prices(prices, outcome_labels)\n    arb = detect_arb(prices, outcome_labels)\n\n    st.markdown(f\"### {name}\")\n    st.caption(f\"{len(prices)} bookmakers available\")\n\n    # Best price metrics\n    cols = st.columns(len(outcome_labels))\n    for i, lbl in enumerate(outcome_labels):\n        with cols[i]:\n            st.metric(f\"Best {lbl}\", f\"{best[lbl]['price']:.2f}\",\n                      help=f\"at {best[lbl]['book']}\")\n\n    # Bar chart\n    featured = [\"pinnacle\", \"bet365\", \"draftkings\",\n                \"fanduel\", \"bwin\", \"1xbet\"]\n    fig = go.Figure()\n    for slug in featured:\n        if slug not in prices:\n            continue\n        vals = [prices[slug].get(lbl, 0) for lbl in outcome_labels]\n        fig.add_trace(go.Bar(name=slug, x=outcome_labels, y=vals,\n                             text=[f\"{v:.2f}\" for v in vals],\n                             textposition=\"outside\"))\n    fig.update_layout(barmode=\"group\", template=\"plotly_dark\",\n                      yaxis_title=\"Decimal Odds\", height=400)\n    st.plotly_chart(fig, use_container_width=True)\n\n    if arb[\"arb\"]:\n        st.success(f\"ARB: {arb['margin']}% guaranteed profit!\")\n\nif auto:\n    import time\n    time.sleep(60)\n    st.rerun()\n<\/code><\/pre>\n<p>Run it with:<\/p>\n<pre class=\"wp-block-code\"><code>streamlit run dashboard.py<\/code><\/pre>\n<p>Streamlit opens a browser tab automatically. The dashboard is fully interactive \u2014 change the sport, toggle auto-refresh, or expand any fixture to see all bookmakers.<\/p>\n<h2>The Complete Script<\/h2>\n<p>The full working script combining all functions above is available in the <a href=\"https:\/\/github.com\/oddspapi\" target=\"_blank\" rel=\"noopener\">OddsPapi GitHub examples repository<\/a>. It includes <code>fetch_fixtures<\/code>, <code>fetch_odds<\/code>, <code>extract_prices<\/code>, <code>find_best_prices<\/code>, <code>detect_arb<\/code>, and the Streamlit UI in a single ready-to-run file.<\/p>\n<p>To deploy for free on Streamlit Cloud:<\/p>\n<ol>\n<li>Push your <code>dashboard.py<\/code> and a <code>requirements.txt<\/code> to a GitHub repo<\/li>\n<li>Connect the repo at <a href=\"https:\/\/streamlit.io\/cloud\" target=\"_blank\" rel=\"noopener\">streamlit.io\/cloud<\/a><\/li>\n<li>Add your <code>API_KEY<\/code> as a Streamlit secret (<code>st.secrets[\"API_KEY\"]<\/code>)<\/li>\n<li>Deploy \u2014 your dashboard is live at a public URL, refreshing automatically<\/li>\n<\/ol>\n<h2>What to Build Next<\/h2>\n<h3>Upgrade to WebSocket for Sub-Second Updates<\/h3>\n<p>The REST API approach polls every 60 seconds. For in-play markets, that's too slow. OddsPapi supports real-time WebSocket streaming that pushes odds updates the moment they change. See the <a href=\"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/\">WebSocket Odds API guide<\/a> to upgrade your dashboard to a live feed.<\/p>\n<h3>Add Historical Line Movement Tracking<\/h3>\n<p>OddsPapi includes free historical odds data on the free tier \u2014 something competitors charge separately for. Store odds snapshots in a SQLite database and chart how lines move before kick-off. Pinnacle line movement is one of the most reliable signals in sports betting, and you get it free.<\/p>\n<h3>Build Telegram or Discord Alerts<\/h3>\n<p>Add a notification layer so you don't need the dashboard open. When <code>detect_arb()<\/code> returns <code>True<\/code>, fire a message to a Telegram bot or Discord webhook with the fixture, margin, and best prices.<\/p>\n<h3>Add More Markets<\/h3>\n<p>This tutorial used 1X2 (market ID <code>101<\/code>) for soccer and moneylines for NBA. OddsPapi supports Asian Handicaps (AH 0 = <code>1072<\/code>, AH -0.5 = <code>1068<\/code>), Both Teams to Score (<code>104<\/code>), Over\/Under 2.5 (<code>1010<\/code>), and player props. Extend the <code>SPORT_CFG<\/code> dict to add new markets without touching the core logic.<\/p>\n<h2>Frequently Asked Questions<\/h2>\n<p><script type=\"application\/ld+json\">\n{\n  \"@context\": \"https:\/\/schema.org\",\n  \"@type\": \"FAQPage\",\n  \"mainEntity\": [\n    {\n      \"@type\": \"Question\",\n      \"name\": \"How many bookmakers does OddsPapi cover?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"OddsPapi aggregates odds from 300+ bookmakers, including sharp books (Pinnacle, Singbet, SBOBet), major softs (Bet365, DraftKings, FanDuel, bwin), crypto\/niche books (1xBet, GG.BET), and regional specialists.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"Is the OddsPapi API free to use?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Yes. The free tier includes 250 requests per day, access to 59 sports, and free historical odds data. No credit card required.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"What betting markets can I compare with this dashboard?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Moneylines, spreads, totals, Asian handicaps, Both Teams to Score, player props, and more. Each market has a unique ID. Soccer 1X2 is 101, Asian Handicap 0 is 1072, AH -0.5 is 1068.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"Can I detect arbitrage opportunities with this dashboard?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Yes. The detect_arb() function checks whether the combined implied probabilities of the best prices per outcome fall below 100%. When they do, a guaranteed profit exists regardless of the match outcome.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"What is the difference between sharp and soft bookmakers?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Sharp bookmakers (Pinnacle, Singbet) set efficient markets by accepting sharp money. Soft bookmakers (DraftKings, FanDuel) offer bonuses but run less efficient lines. Comparing both reveals value.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"Can I use this dashboard for live in-play markets?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Yes. The same \/odds endpoint works for live fixtures. Enable auto-refresh or upgrade to WebSocket streaming for sub-second updates.\"\n      }\n    }\n  ]\n}\n<\/script><\/p>\n<h3>How many bookmakers does OddsPapi cover?<\/h3>\n<p>300+ bookmakers, including sharp books (Pinnacle, Singbet, SBOBet), major softs (Bet365, DraftKings, FanDuel), crypto\/niche books (1xBet, GG.BET), and regional specialists across Asia, Europe, and Latin America. Competitors typically cover 15\u201340 books.<\/p>\n<h3>Is the OddsPapi API free to use?<\/h3>\n<p>Yes. The free tier includes 250 requests per day, access to 59 sports, and free historical odds data \u2014 no credit card required. Free historical data is something competitors charge extra for; OddsPapi includes it by default.<\/p>\n<h3>What betting markets can I compare?<\/h3>\n<p>Moneylines, spreads, totals, Asian handicaps, Both Teams to Score, player props, and more. Each market has a unique numeric ID \u2014 soccer 1X2 is <code>101<\/code>, Asian Handicap 0 is <code>1072<\/code>, AH -0.5 is <code>1068<\/code>. Extend the <code>SPORT_CFG<\/code> dict to add any market without touching the core dashboard logic.<\/p>\n<h3>Can I detect arbitrage with this dashboard?<\/h3>\n<p>Yes. The <code>detect_arb()<\/code> function checks whether the combined implied probabilities of the best prices per outcome \u2014 drawn from different bookmakers \u2014 fall below 100%. When they do, a guaranteed profit margin exists regardless of the result.<\/p>\n<h3>What's the difference between sharp and soft bookmakers?<\/h3>\n<p>Sharp books (Pinnacle, Singbet) set efficient lines by accepting large sharp wagers. Soft books (DraftKings, FanDuel) cater to recreational bettors with bonuses and lower bet limits. Comparing sharp prices against soft prices is the core methodology for value betting \u2014 OddsPapi gives you both in a single API call.<\/p>\n<h3>Can I use this for live in-play markets?<\/h3>\n<p>Yes. The same <code>\/odds<\/code> endpoint covers in-play fixtures. For live use, enable the auto-refresh toggle or upgrade to WebSocket streaming for sub-second updates. See the <a href=\"https:\/\/oddspapi.io\/blog\/websocket-odds-api-real-time-betting-data\/\">WebSocket guide<\/a> for details.<\/p>\n<h2>Stop Paying $99\/Month for Someone Else's Dashboard<\/h2>\n<p>You just built the same core functionality as OddsJam or OddsBoom \u2014 300+ bookmakers, real-time prices, automatic arb detection, and a clean interactive UI \u2014 for free. Now you own the logic, you control the markets you watch, and you can extend it any direction: Telegram alerts, historical tracking, Streamlit Cloud deployment, or a full WebSocket feed.<\/p>\n<p><strong>Get your free API key at <a href=\"https:\/\/oddspapi.io\">oddspapi.io<\/a><\/strong> \u2014 250 requests\/day, 59 sports, historical data included. No credit card. No trial period. Just the data.<\/p>\n<p><!--\nFocus Keyphrase: odds comparison dashboard\nSEO Title: Build a Live Odds Dashboard with Python & Streamlit (Free API)\nMeta Description: Build a live odds comparison dashboard with Python & Streamlit. Compare 300+ bookmakers, find best lines, spot arbs. Free API.\nSlug: odds-comparison-dashboard-python-streamlit\n--><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Build a live odds comparison dashboard with Python &#038; Streamlit. Compare 300+ bookmakers, find best lines, spot arbs. Free API.<\/p>\n","protected":false},"author":2,"featured_media":2531,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[8,9,11,10,19],"class_list":["post-2529","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-how-to-guides","tag-free-api","tag-odds-api","tag-python","tag-sports-betting-api","tag-streamlit"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.4 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Build a Live Odds Dashboard with Python &amp; Streamlit (Free API) | Odds API Development Blog<\/title>\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\/odds-comparison-dashboard-python-streamlit\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Build a Live Odds Dashboard with Python &amp; Streamlit (Free API) | Odds API Development Blog\" \/>\n<meta property=\"og:description\" content=\"Build a live odds comparison dashboard with Python &amp; Streamlit. Compare 300+ bookmakers, find best lines, spot arbs. Free API.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/\" \/>\n<meta property=\"og:site_name\" content=\"Odds API Development Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-03-24T10:00:00+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/03\/odds-comparison-dashboard-python-streamlit-scaled.webp\" \/>\n\t<meta property=\"og:image:width\" content=\"2560\" \/>\n\t<meta property=\"og:image:height\" content=\"1429\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/webp\" \/>\n<meta name=\"author\" content=\"Odds API Writer\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\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=\"10 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/\"},\"author\":{\"name\":\"Odds API Writer\",\"@id\":\"https:\/\/oddspapi.io\/blog\/#\/schema\/person\/b6f21e649c4f556f0a95c23a0f1efa13\"},\"headline\":\"Build a Live Odds Dashboard with Python &#038; Streamlit (Free API)\",\"datePublished\":\"2026-03-24T10:00:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/\"},\"wordCount\":1441,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/03\/odds-comparison-dashboard-python-streamlit-scaled.webp\",\"keywords\":[\"Free API\",\"Odds API\",\"Python\",\"Sports Betting API\",\"Streamlit\"],\"articleSection\":[\"How To Guides\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/\",\"url\":\"https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/\",\"name\":\"Build a Live Odds Dashboard with Python & Streamlit (Free API) | Odds API Development Blog\",\"isPartOf\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/03\/odds-comparison-dashboard-python-streamlit-scaled.webp\",\"datePublished\":\"2026-03-24T10:00:00+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/#primaryimage\",\"url\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/03\/odds-comparison-dashboard-python-streamlit-scaled.webp\",\"contentUrl\":\"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/03\/odds-comparison-dashboard-python-streamlit-scaled.webp\",\"width\":2560,\"height\":1429,\"caption\":\"Odds Dashboard - OddsPapi API Blog\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/oddspapi.io\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Build a Live Odds Dashboard with Python &#038; Streamlit (Free API)\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/oddspapi.io\/blog\/#website\",\"url\":\"https:\/\/oddspapi.io\/blog\/\",\"name\":\"OddsPapi\",\"description\":\"Sports Odds APIs 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":"Build a Live Odds Dashboard with Python & Streamlit (Free API) | Odds API Development Blog","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\/odds-comparison-dashboard-python-streamlit\/","og_locale":"en_US","og_type":"article","og_title":"Build a Live Odds Dashboard with Python & Streamlit (Free API) | Odds API Development Blog","og_description":"Build a live odds comparison dashboard with Python & Streamlit. Compare 300+ bookmakers, find best lines, spot arbs. Free API.","og_url":"https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/","og_site_name":"Odds API Development Blog","article_published_time":"2026-03-24T10:00:00+00:00","og_image":[{"width":2560,"height":1429,"url":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/03\/odds-comparison-dashboard-python-streamlit-scaled.webp","type":"image\/webp"}],"author":"Odds API Writer","twitter_card":"summary_large_image","twitter_creator":"@oddspapiapi","twitter_site":"@oddspapiapi","twitter_misc":{"Written by":"Odds API Writer","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/#article","isPartOf":{"@id":"https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/"},"author":{"name":"Odds API Writer","@id":"https:\/\/oddspapi.io\/blog\/#\/schema\/person\/b6f21e649c4f556f0a95c23a0f1efa13"},"headline":"Build a Live Odds Dashboard with Python &#038; Streamlit (Free API)","datePublished":"2026-03-24T10:00:00+00:00","mainEntityOfPage":{"@id":"https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/"},"wordCount":1441,"commentCount":0,"publisher":{"@id":"https:\/\/oddspapi.io\/blog\/#organization"},"image":{"@id":"https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/#primaryimage"},"thumbnailUrl":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/03\/odds-comparison-dashboard-python-streamlit-scaled.webp","keywords":["Free API","Odds API","Python","Sports Betting API","Streamlit"],"articleSection":["How To Guides"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/","url":"https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/","name":"Build a Live Odds Dashboard with Python & Streamlit (Free API) | Odds API Development Blog","isPartOf":{"@id":"https:\/\/oddspapi.io\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/#primaryimage"},"image":{"@id":"https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/#primaryimage"},"thumbnailUrl":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/03\/odds-comparison-dashboard-python-streamlit-scaled.webp","datePublished":"2026-03-24T10:00:00+00:00","breadcrumb":{"@id":"https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/#primaryimage","url":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/03\/odds-comparison-dashboard-python-streamlit-scaled.webp","contentUrl":"https:\/\/oddspapi.io\/blog\/wp-content\/uploads\/2026\/03\/odds-comparison-dashboard-python-streamlit-scaled.webp","width":2560,"height":1429,"caption":"Odds Dashboard - OddsPapi API Blog"},{"@type":"BreadcrumbList","@id":"https:\/\/oddspapi.io\/blog\/odds-comparison-dashboard-python-streamlit\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/oddspapi.io\/blog\/"},{"@type":"ListItem","position":2,"name":"Build a Live Odds Dashboard with Python &#038; Streamlit (Free API)"}]},{"@type":"WebSite","@id":"https:\/\/oddspapi.io\/blog\/#website","url":"https:\/\/oddspapi.io\/blog\/","name":"OddsPapi","description":"Sports Odds APIs 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\/2529","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=2529"}],"version-history":[{"count":1,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/posts\/2529\/revisions"}],"predecessor-version":[{"id":2530,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/posts\/2529\/revisions\/2530"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/media\/2531"}],"wp:attachment":[{"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/media?parent=2529"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/categories?post=2529"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/oddspapi.io\/blog\/wp-json\/wp\/v2\/tags?post=2529"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}