Skip to main content

GET /api/odds

Returns current bookmaker odds — one row per (event × bookmaker × market_type × period) combination. The odds field is an object keyed by selection text, making it easy to compare prices across bookmakers for the same market. For the full market breakdown of a single event (all bookmakers, all markets, grouped), use GET /api/events/{id} instead.

Query Parameters

ParameterTypeDefaultDescription
sportstringFilter by sport name (e.g. Football, Tennis)
competitionstringFilter by competition code (e.g. EPL, NBA)
bookmakerstringFilter by bookmaker name (e.g. Winamax, Betclic)
market_typestringFilter by market type (e.g. moneyline, spread, total)
is_livebooleantrue = in-play only, false = pre-match only
limitinteger100Max rows returned, up to 500

Response

{
  "data": [
    {
      "bookmaker": "Betsson",
      "match_name": "Crystal Palace - West Ham United",
      "competition": "EPL",
      "sport": "Football",
      "market_type": "moneyline",
      "period": 0,
      "is_live": false,
      "odds": {
        "Crystal Palace": 3.40,
        "Draw": 3.25,
        "West Ham United": 2.10
      },
      "match_date": "2026-04-20T20:00:00Z",
      "scraped_at": "2026-04-20T14:11:09Z",
      "url": "https://www.betsson.fr/paris-sportifs/event/4237119.1"
    },
    {
      "bookmaker": "Winamax",
      "match_name": "Crystal Palace - West Ham United",
      "competition": "EPL",
      "sport": "Football",
      "market_type": "moneyline",
      "period": 0,
      "is_live": false,
      "odds": {
        "Crystal Palace": 3.35,
        "Draw": 3.20,
        "West Ham United": 2.15
      },
      "match_date": "2026-04-20T20:00:00Z",
      "scraped_at": "2026-04-20T14:07:11Z",
      "url": "https://www.winamax.fr/paris-sportifs/sports/..."
    },
    {
      "bookmaker": "Unibet.fr",
      "match_name": "Cleveland Cavaliers / Toronto Raptors",
      "competition": "NBA",
      "sport": "Basketball",
      "market_type": "player_prop",
      "period": 0,
      "is_live": false,
      "odds": {
        "RJ Barrett Rebounds Over 4.5": 2.12,
        "RJ Barrett Rebounds Under 4.5": 1.72
      },
      "match_date": "2026-04-20T23:00:00Z",
      "scraped_at": "2026-04-20T13:51:28Z",
      "url": null
    }
  ],
  "meta": {
    "count": 12847,
    "rate_limit_remaining": 295
  }
}

Response Fields

FieldTypeDescription
bookmakerstringBookmaker name
match_namestringCanonical event name from the scraper
competitionstringCompetition code
sportstringSport derived from competition code
market_typestringMarket type — see table below
periodintegerTime segment (0=full time, 1=1st half, etc.)
is_livebooleantrue if this is an in-play market
oddsobjectKeyed by selection text → decimal odds value
match_dateISO-8601 string | nullScheduled kick-off (UTC)
scraped_atISO-8601 stringWhen these odds were last scraped
urlstring | nullDirect deep-link to this market on the bookmaker site
liquidityfloat | nullPinnacle max bet size (only populated for Pinnacle rows)

odds Object Format

The odds field groups all selections for this (bookmaker, event, market, period) combination into a single object. Selection texts vary by market type:
Market TypeTypical odds keys
moneyline (football)"Home Team", "Draw", "Away Team"
moneyline (other)"Home Team", "Away Team"
spread"Home Team +1.5", "Away Team -1.5"
total"Over 2.5", "Under 2.5"
btts"Yes", "No"
double_chance"1X", "X2", "12"
player_prop"Player Name Stat Over X.X", "Player Name Stat Under X.X"
team_total"Home Over 1.5", "Home Under 1.5"

GET /api/odds/live

Same as GET /api/odds but returns only in-play events. Requires Pro+ Live plan.

Query Parameters

Identical to GET /api/odds (same filters, same limit).

Notes

  • Live odds update every 5–10 seconds. Poll accordingly.
  • Bookmakers with live coverage: Betclic, Winamax (selected events), Unibet.fr (selected events).

GET /api/odds/

Returns all current odds for a specific bookmaker. Equivalent to GET /api/odds?bookmaker={bookmaker} but slightly more efficient.

Path Parameter

ParameterDescription
bookmakerExact bookmaker name (e.g. Winamax, Betclic) — use GET /api/bookmakers to get valid names

Query Parameters

ParameterTypeDefaultDescription
sportstringFilter by sport
competitionstringFilter by competition code
market_typestringFilter by market type
is_livebooleanIn-play filter
limitinteger100Max rows, up to 500

Response

Same format as GET /api/odds.

Code Examples

# All EPL moneyline odds
curl "https://api.oddsstream.io/api/odds?competition=EPL&market_type=moneyline" \
  -H "X-Api-Key: os_live_YOUR_KEY"

# All Betclic odds
curl "https://api.oddsstream.io/api/odds/Betclic" \
  -H "X-Api-Key: os_live_YOUR_KEY"

# All Winamax football spreads
curl "https://api.oddsstream.io/api/odds/Winamax?sport=Football&market_type=spread" \
  -H "X-Api-Key: os_live_YOUR_KEY"

# Live basketball odds (Pro+ Live)
curl "https://api.oddsstream.io/api/odds/live?sport=Basketball" \
  -H "X-Api-Key: os_live_YOUR_KEY"
import requests

API_KEY = "os_live_YOUR_KEY"

# Get all LIG1 moneyline rows
resp = requests.get(
    "https://api.oddsstream.io/api/odds",
    params={"competition": "LIG1", "market_type": "moneyline", "limit": 200},
    headers={"X-Api-Key": API_KEY}
)
rows = resp.json()["data"]

# Group by match name to compare bookmakers
from collections import defaultdict
by_match = defaultdict(list)
for row in rows:
    by_match[row["match_name"]].append(row)

# Find best odds per selection
for match, bookmaker_rows in by_match.items():
    print(f"\n{match}")
    # Collect all selections from all bookmakers
    all_selections = {}
    for row in bookmaker_rows:
        for sel, odds in row["odds"].items():
            if sel not in all_selections or odds > all_selections[sel]["odds"]:
                all_selections[sel] = {"odds": odds, "bookmaker": row["bookmaker"]}
    for sel, best in all_selections.items():
        print(f"  {sel}: {best['odds']} @ {best['bookmaker']}")
// Poll all EPL odds every 30 seconds
async function pollOdds() {
  const resp = await fetch(
    "https://api.oddsstream.io/api/odds?competition=EPL&market_type=moneyline&limit=500",
    { headers: { "X-Api-Key": "os_live_YOUR_KEY" } }
  );
  const { data, meta } = await resp.json();
  
  console.log(`${data.length} odds rows, ${meta.rate_limit_remaining} requests remaining`);
  
  // Build lookup: match → bookmaker → odds
  const lookup = {};
  for (const row of data) {
    if (!lookup[row.match_name]) lookup[row.match_name] = {};
    lookup[row.match_name][row.bookmaker] = row.odds;
  }
  return lookup;
}

setInterval(pollOdds, 30_000);