<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: SharpAPI</title>
    <description>The latest articles on DEV Community by SharpAPI (@sharpapi).</description>
    <link>https://dev.to/sharpapi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4034478%2F2252acda-ce31-433b-8788-3f1db1be8821.png</url>
      <title>DEV Community: SharpAPI</title>
      <link>https://dev.to/sharpapi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sharpapi"/>
    <language>en</language>
    <item>
      <title>How to Get Real-Time Sports Betting Odds from an API</title>
      <dc:creator>SharpAPI</dc:creator>
      <pubDate>Fri, 17 Jul 2026 22:28:50 +0000</pubDate>
      <link>https://dev.to/sharpapi/how-to-get-real-time-sports-betting-odds-from-an-api-45bf</link>
      <guid>https://dev.to/sharpapi/how-to-get-real-time-sports-betting-odds-from-an-api-45bf</guid>
      <description>&lt;p&gt;If you have ever tried to build a betting tool, a line-shopping app, or a model that needs live odds, you have probably hit the same wall: the sportsbooks themselves do not give you an API. DraftKings, FanDuel, BetMGM, Caesars, Pinnacle, and the rest have no public developer program. Their odds move constantly, they differ book to book, and there is no supported way to pull them.&lt;/p&gt;

&lt;p&gt;This guide walks through how developers actually solve that, and the concepts you need to turn raw odds into something useful: fair prices, expected value, and arbitrage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the books have no public API
&lt;/h2&gt;

&lt;p&gt;Sportsbooks make money on the margin baked into their prices (the "vig"). Exposing a clean, real-time feed would help competitors and sharp bettors more than it helps the book, so none of the major US operators publish one. The odds you see in their apps come from private internal feeds.&lt;/p&gt;

&lt;p&gt;That leaves two options: scrape each book yourself (fragile, rate-limited, and a maintenance treadmill across dozens of sites), or use an aggregator that has already normalized every book into one schema. This guide uses &lt;a href="https://sharpapi.io" rel="noopener noreferrer"&gt;SharpAPI&lt;/a&gt;, which aggregates 45+ sportsbooks and betting exchanges into a single format, but the concepts apply no matter how you source the data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Odds formats, and why you convert everything to probability
&lt;/h2&gt;

&lt;p&gt;The same price shows up three ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;American&lt;/strong&gt;: &lt;code&gt;-150&lt;/code&gt; (bet 150 to win 100) or &lt;code&gt;+130&lt;/code&gt; (bet 100 to win 130)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decimal&lt;/strong&gt;: &lt;code&gt;1.67&lt;/code&gt; or &lt;code&gt;2.30&lt;/code&gt; (total return per 1 unit staked)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implied probability&lt;/strong&gt;: the decimal odds inverted, &lt;code&gt;1 / 2.30 = 0.435&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Always convert to implied probability before you compare anything. Two books quoting &lt;code&gt;-150&lt;/code&gt; and &lt;code&gt;1.67&lt;/code&gt; are quoting the same price; only probability makes that obvious.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;american_to_decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;decimal_to_prob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;decimal_to_prob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;american_to_decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;  &lt;span class="c1"&gt;# 0.6 (before vig removal)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Removing the vig to get a fair price
&lt;/h2&gt;

&lt;p&gt;Add up the implied probabilities on both sides of a market and you will get more than 100 percent. That overround is the vig. To estimate the "fair" (no-vig) probability, normalize the two sides so they sum to 1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;no_vig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prob_a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prob_b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prob_a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;prob_b&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;prob_a&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prob_b&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sharp books like Pinnacle run thin margins and move on real money, so their no-vig line is the closest thing to a market consensus. That consensus is the reference you measure every other book against. Pinnacle has no public API either, so pulling Pinnacle odds is one of the most common reasons developers reach for an aggregator in the first place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding +EV bets
&lt;/h2&gt;

&lt;p&gt;A bet has positive expected value when a book is offering better odds than the fair price implies. If the no-vig fair probability of an outcome is 0.50 (fair decimal 2.00), and some book is offering 2.15, that book is paying you more than the risk warrants:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;expected_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;offered_decimal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fair_prob&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;offered_decimal&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;fair_prob&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="c1"&gt;# fair prob 0.50, a book offering 2.15
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;expected_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;2.15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.50&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# 0.075  -&amp;gt; +7.5% edge
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do this across every book and every market and you have a +EV scanner. The work is not the math, it is getting clean, synchronized odds from every book at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Arbitrage and middles
&lt;/h2&gt;

&lt;p&gt;When two books disagree enough, you can back both sides and lock a profit regardless of outcome. If the best price on each side sums to an implied probability under 100 percent, it is an arb:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_arb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;best_decimal_a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;best_decimal_b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;best_decimal_a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;best_decimal_b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These windows are small and close fast, which is why latency matters. Polling every few seconds misses most of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pulling it together with a live feed
&lt;/h2&gt;

&lt;p&gt;Here is the whole loop against a normalized aggregator: get the odds, remove the vig against a sharp reference, and surface anything +EV.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;odds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.sharpapi.io/api/v1/odds&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-API-Key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sport&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nfl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;market&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;moneyline&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# group by event + selection, compare each book to the no-vig reference,
# flag offered prices whose EV clears your threshold
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For anything real-time, polling is the wrong tool. Odds change continuously, and the value windows you care about open and close in seconds. A push-based stream (Server-Sent Events or WebSocket) hands you each change as it happens instead of you asking repeatedly. SharpAPI pushes updates over SSE with sub-89ms latency, which is the difference between catching a middle and reading about it afterward.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to start
&lt;/h2&gt;

&lt;p&gt;You do not need all 45 books or a streaming pipeline on day one. Start with one sport and one market, convert everything to probability, remove the vig against a sharp reference, and print anything with positive EV. Once that works, add books, add markets, then move from polling to streaming.&lt;/p&gt;

&lt;p&gt;If you want a normalized feed to build against, SharpAPI has a free tier (12 requests/minute, no credit card): &lt;a href="https://sharpapi.io" rel="noopener noreferrer"&gt;sharpapi.io&lt;/a&gt;. The concepts here are the same whichever source you use, the hard part was never the math, it was getting clean odds from every book at the same instant.&lt;/p&gt;

</description>
      <category>api</category>
      <category>python</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
