Skip to main content

GET /api/events

Returns distinct events (matches) that currently have bookmaker odds. Each event has a slug you pass to GET /api/events/{id} to retrieve all market odds.

Query Parameters

ParameterTypeDefaultDescription
sportstringFilter by sport name (e.g. Football, Basketball, Tennis)
competitionstringFilter by competition code (e.g. EPL, NBA, LIG1)
bookmakerstringOnly events where this bookmaker has odds
is_livebooleantrue = in-play only, false = pre-match only
stale_minutesinteger10Max age of odds to include (1–60 min)
limitinteger200Max events returned, up to 1000

Response

{
  "data": [
    {
      "slug": "Crystal%20Palace%20-%20West%20Ham%20United",
      "match_name": "Crystal Palace - West Ham United",
      "competition": "EPL",
      "sport": "Football",
      "match_date": "2026-04-20T20:00:00Z",
      "is_live": false,
      "bookmaker_count": 7
    },
    {
      "slug": "PSG%20-%20Lyon",
      "match_name": "PSG - Lyon",
      "competition": "LIG1",
      "sport": "Football",
      "match_date": "2026-04-20T21:00:00Z",
      "is_live": false,
      "bookmaker_count": 5
    }
  ],
  "meta": {
    "count": 1616,
    "rate_limit_remaining": 297
  }
}

Response Fields

FieldTypeDescription
slugstringURL-encoded match_name — pass to GET /api/events/{id}
match_namestringCanonical match name as stored by the scraper (e.g. "PSG - Lyon")
competitionstringCompetition code (e.g. EPL, LIG1)
sportstringSport derived from competition code
match_dateISO-8601 string | nullScheduled kick-off time (UTC), null if unknown
is_livebooleantrue if any bookmaker has this event live right now
bookmaker_countintegerNumber of distinct bookmakers covering this event
The same fixture can appear multiple times with different match_name values if different bookmakers use different team name formats. Use bookmaker_count to identify the canonical listing (highest count = most coverage = most canonical name).

GET /api/events/

Returns all bookmaker odds and Pinnacle reference lines for a single event, grouped by (bookmaker × market_type × period). This is the most granular endpoint — every odds line for every market from every bookmaker for one match.

Path Parameter

ParameterDescription
idURL-encoded match name from the slug field in GET /api/events

Query Parameters

ParameterTypeDefaultDescription
bookmakerstringFilter to a single bookmaker
market_typestringFilter to a single market type (e.g. moneyline, total, spread)
periodintegerFilter to a single period (0=full-time, 1=first-half)
stale_minutesinteger20Max age of odds to include (1–60 min)

Response

{
  "data": {
    "match_name": "Crystal Palace - West Ham United",
    "competition": "EPL",
    "sport": "Football",
    "match_date": null,
    "slug": "Crystal%20Palace%20-%20West%20Ham%20United",
    "markets": [
      {
        "bookmaker": "Betsson",
        "market_type": "moneyline",
        "period": 0,
        "selections": [
          { "selection": "Crystal Palace", "odds": 3.40 },
          { "selection": "Draw", "odds": 3.25 },
          { "selection": "West Ham United", "odds": 2.10 }
        ],
        "scraped_at": "2026-04-20T14:11:09Z",
        "url": "https://www.betsson.fr/paris-sportifs/event/1234.1"
      },
      {
        "bookmaker": "Betsson",
        "market_type": "total",
        "period": 0,
        "selections": [
          { "selection": "Over 2.5", "odds": 1.87 },
          { "selection": "Under 2.5", "odds": 1.98 }
        ],
        "scraped_at": "2026-04-20T14:11:09Z",
        "url": "https://www.betsson.fr/paris-sportifs/event/1234.1"
      },
      {
        "bookmaker": "Betsson",
        "market_type": "total",
        "period": 1,
        "selections": [
          { "selection": "Over 0.5", "odds": 1.22 },
          { "selection": "Under 0.5", "odds": 3.90 }
        ],
        "scraped_at": "2026-04-20T14:11:09Z",
        "url": "https://www.betsson.fr/paris-sportifs/event/1234.1"
      },
      {
        "bookmaker": "Winamax",
        "market_type": "moneyline",
        "period": 0,
        "selections": [
          { "selection": "Crystal Palace", "odds": 3.35 },
          { "selection": "Draw", "odds": 3.20 },
          { "selection": "West Ham United", "odds": 2.15 }
        ],
        "scraped_at": "2026-04-20T14:07:00Z",
        "url": "https://www.winamax.fr/paris-sportifs/sports/..."
      }
    ],
    "pinnacle": [
      {
        "market_type": "moneyline",
        "period": 0,
        "selections": [
          { "selection": "Crystal Palace", "odds": 3.20 },
          { "selection": "Draw", "odds": 3.15 },
          { "selection": "West Ham United", "odds": 2.00 }
        ],
        "scraped_at": "2026-04-20T13:47:16Z"
      }
    ],
    "summary": {
      "bookmakers": ["1xbet", "Bet365", "Betify", "Betsson", "Cloudbet", "Duel", "Mystake", "Polymarket", "Winamax"],
      "market_types": ["3way_handicap", "btts", "moneyline", "player_prop", "spread", "team_total", "total"],
      "total_markets": 57
    }
  },
  "meta": {
    "rate_limit_remaining": 296
  }
}

markets Array Fields

Each object in markets represents all the odds a single bookmaker has for one market at one period:
FieldTypeDescription
bookmakerstringBookmaker name
market_typestringMarket type — see Market Types
periodintegerTime period — see Period Values
selectionsarrayAll outcomes for this market. Each has selection (string) and odds (decimal float)
scraped_atISO-8601 stringWhen this bookmaker’s odds were last scraped
urlstring | nullDirect deep-link to the bookmaker’s page for this event

pinnacle Array Fields

Same structure as markets but without bookmaker or url. Pinnacle is the sharp reference line — it represents the sharpest market consensus. Compare any bookmaker’s odds against Pinnacle to estimate edge.

summary Fields

FieldTypeDescription
bookmakersstring[]All bookmakers covering this event (sorted alphabetically)
market_typesstring[]All market types available across all bookmakers (sorted)
total_marketsintegerTotal (bookmaker × market_type × period) groups in this response
Finding value: Compare markets[n].selections[k].odds against the corresponding selection in pinnacle. When a bookmaker’s odds are higher than Pinnacle’s fair price for the same outcome, that’s a potential +EV opportunity. See GET /api/value-bets for pre-calculated EV detections.

Drilldown Examples

# All markets for one event
curl "https://api.oddsstream.io/api/events/Crystal%20Palace%20-%20West%20Ham%20United" \
  -H "X-Api-Key: os_live_YOUR_KEY"

# Moneyline only
curl "https://api.oddsstream.io/api/events/Crystal%20Palace%20-%20West%20Ham%20United?market_type=moneyline" \
  -H "X-Api-Key: os_live_YOUR_KEY"

# Betsson only
curl "https://api.oddsstream.io/api/events/Crystal%20Palace%20-%20West%20Ham%20United?bookmaker=Betsson" \
  -H "X-Api-Key: os_live_YOUR_KEY"

# Betsson's 1st-half totals
curl "https://api.oddsstream.io/api/events/Crystal%20Palace%20-%20West%20Ham%20United?bookmaker=Betsson&market_type=total&period=1" \
  -H "X-Api-Key: os_live_YOUR_KEY"

Full Python Example

import requests, urllib.parse

API_KEY = "os_live_YOUR_KEY"
BASE = "https://api.oddsstream.io"

# Step 1: find EPL events
resp = requests.get(
    f"{BASE}/api/events",
    params={"competition": "EPL", "limit": 10},
    headers={"X-Api-Key": API_KEY}
)
events = resp.json()["data"]
print(f"Found {len(events)} EPL events")

# Step 2: pick an event and get all its markets
event = events[0]
slug = event["slug"]  # already URL-encoded

resp2 = requests.get(
    f"{BASE}/api/events/{slug}",
    headers={"X-Api-Key": API_KEY}
)
detail = resp2.json()["data"]
print(f"Match: {detail['match_name']}")
print(f"Bookmakers: {detail['summary']['bookmakers']}")
print(f"Markets available: {detail['summary']['market_types']}")

# Step 3: find all moneyline odds across bookmakers
moneyline_markets = [m for m in detail["markets"] if m["market_type"] == "moneyline" and m["period"] == 0]
for market in moneyline_markets:
    odds_str = ", ".join(f"{s['selection']} {s['odds']}" for s in market["selections"])
    print(f"  {market['bookmaker']}: {odds_str}")

JavaScript Example

const API_KEY = "os_live_YOUR_KEY";
const BASE = "https://api.oddsstream.io";

// Get all Football events with 5+ bookmakers
const resp = await fetch(`${BASE}/api/events?sport=Football&limit=100`, {
  headers: { "X-Api-Key": API_KEY }
});
const { data: events } = await resp.json();
const deepCoverage = events.filter(e => e.bookmaker_count >= 5);

// Get one event's full detail
const event = deepCoverage[0];
const detailResp = await fetch(`${BASE}/api/events/${event.slug}`, {
  headers: { "X-Api-Key": API_KEY }
});
const { data } = await detailResp.json();

// Compare Betsson moneyline against Pinnacle
const betssonML = data.markets.find(m => m.bookmaker === "Betsson" && m.market_type === "moneyline" && m.period === 0);
const pinnacleML = data.pinnacle.find(p => p.market_type === "moneyline" && p.period === 0);

if (betssonML && pinnacleML) {
  betssonML.selections.forEach(sel => {
    const pin = pinnacleML.selections.find(p => p.selection === sel.selection);
    if (pin && sel.odds > pin.odds) {
      const edge = ((sel.odds / pin.odds) - 1) * 100;
      console.log(`${sel.selection}: Betsson ${sel.odds} > Pinnacle ${pin.odds} (+${edge.toFixed(1)}%)`);
    }
  });
}