# Football Odds Scraper: Historical & Closing Lines, 40+ Books (`sports-odds-lab/football-odds-results`) Actor

Football (soccer) odds from up to 42 bookmakers — 1X2, over/under, BTTS, Asian handicap — with opening and closing lines, no-vig consensus and line movement. Recent matches from 1,000+ competitions or full seasons of any league, odds history back to the mid-2000s. Scores, xG, goal events.

- **URL**: https://apify.com/sports-odds-lab/football-odds-results.md
- **Developed by:** [Sports Odds Lab](https://apify.com/sports-odds-lab) (community)
- **Categories:** Developer tools, Automation
- **Stats:** 3 total users, 2 monthly users, 89.1% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-usage

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Football Odds Scraper: Historical & Closing Lines, 40+ Books

> 🎁 **Free until 8 October 2026.** Press **Start** — the prefilled input returns 30 matches with odds in under a minute.

![Sample output: Premier League 2024/25 with fair 1X2 odds, line movement, over/under and Asian handicap consensus](https://api.apify.com/v2/key-value-stores/CEtWcytR8Vm5hiMr1/records/preview-football.png?signature=8TY0WA2vaOQGvFSm2zt9)

Get **every football (soccer) match from 1,000+ competitions** — top leagues, lower divisions, cups, women's and youth football — with **opening and closing odds from up to 42 bookmakers** for **1X2, over/under, both teams to score and Asian handicap**, a **no-vig market consensus**, **line movement**, full **scores** (half-time, extra time, penalties), **xG statistics** and **goal events**. One clean row per match, ready for a spreadsheet, a database or a model.

**Two modes:**

- **Recent matches** — every competition, from 7 days back to 7 days ahead (upcoming matches with current prices)
- **Full seasons** — complete seasons of any league you choose, with **odds history back to the mid-2000s**

Most football scrapers give you scores. Most odds scrapers give you today's prices from a handful of books. This Actor gives you both, with the **opening price and the closing line** of every bookmaker — the data you need to build and backtest betting models.

### What you get for each match

- **Match**: kick-off time (UTC), country, competition and stage, home and away team, Flashscore IDs and link, women's / youth flags
- **Result**: status (finished, after extra time, after penalties, awarded, live, scheduled, postponed…), final score, half-time and second-half score, score after 90 minutes, after extra time and the penalty shoot-out, **1X2 result (H/D/A, settled on 90 minutes)**, winner, red cards
- **Odds per bookmaker** — opening price and latest price for every outcome; once a match has started the latest price is the **closing line**:
  - **1X2** (home / draw / away) with the bookmaker margin
  - **Over/Under** goals — the lines you choose (e.g. 1.5, 2.5, 3.5) or every line offered, quarter lines included
  - **Both teams to score** (yes / no)
  - **Asian handicap** — the main line or all lines
  - **Double chance** *(optional)*
- **Market consensus** (median across bookmakers, margin removed) for every market:
  - fair odds and win / over / BTTS probabilities at the open and at the close
  - **line movement** in percentage points
  - best available odds
  - the Asian handicap **main line at the open and at the close**
  - median 1X2 margin
- **Statistics** *(finished and live matches)*: expected goals (xG), xG on target, expected assists, possession, shots, shots on target, big chances, corners, passes, crosses, fouls, cards, saves and more
- **Events**: every goal with minute, scorer, assist and the score after the goal; penalties scored and missed, own goals, yellow cards, second yellows and red cards

### Historical odds: full seasons back to 2005

Pick leagues and seasons and get every match of the season — all stages, including play-offs and championship/relegation groups — with the opening and closing odds each bookmaker published at the time.

| Season | Bookmakers per match (examples) |
|---|---|
| 2005/06 | 6–8 (Premier League) |
| 2009/10 – 2012/13 | 5–11 (Premier League, Ekstraklasa, Brazil Serie A, League Two, Eliteserien) |
| 2015/16 – 2019/20 | 9–11 (Premier League, Ekstraklasa, J1 League, Argentina) |
| 2022/23 | 14 |
| 2024/25 and later | 30+ listings (about 18–35 brands) |

Over/under and Asian handicap odds go back as far as 1X2; both-teams-to-score odds from about 2015. Scores and goal events cover every season; expected goals (xG) the recent seasons of major leagues.

A full season of a top league (380 matches, four markets, statistics and events) takes about 6 minutes. For many leagues and seasons, raise the run timeout (the default is 6 hours).

### Coverage

Measured on a full day of 313 matches (all levels, including amateur and youth football):

| Segment | Matches with odds | Bookmaker brands per priced match (median) |
|---|---|---|
| Men's senior football | 78% | 25 |
| Women's football | 94% | 25 |
| Youth and reserve teams | 70% | 11 |
| **Top divisions and national cups** (in the sample: MLS, Brazil Serie B, Colombia Primera A, Copa Chile, Emperor's Cup) | 100% | 35 (40–42 listings) |

Over/under 2.5 and both-teams-to-score prices come with 99% of priced matches. A busy weekend day has 1,500+ matches.

Bookmakers include bet365, 1xBet, Betano, Betfred, Betway, bwin, BetMGM, Betclic, DraftKings, Fanatics, Interwetten, Ladbrokes, Midnite, Skybet, Sportingbet, Stake, STS, Superbet, Unibet, Winamax and more — up to 42 listings, depending on the match. Brands that appear under several licences (Betano .br / .ca / .de / .dk, BetMGM .uk / .us…) are counted **once** in the consensus, so they cannot outvote the rest.

### Use cases

- **Build and backtest football models** — train on closing-line probabilities, the most efficient price the market produces, for 1X2, totals and handicaps
- **Closing Line Value (CLV)** — compare the price you bet with the consensus close to measure whether you beat the market
- **Line movement and steam** — find matches where the market moved sharply between open and close
- **Lower leagues and women's football** — the markets where historical odds are hardest to find
- **Your own odds history** — schedule the Actor daily and keep appending to a dataset; after a few weeks you own a multi-bookmaker history nobody sells for these competitions

### How to use it

1. Pick the days (up to 7 back and 7 ahead) and statuses. Optionally narrow it down by country or competition.
2. Choose the markets and over/under lines.
3. Click **Start**. 30 matches take under a minute; a full midweek day takes about 4 minutes, a busy weekend day about 20.
4. Download the results as JSON, CSV or Excel, or read them through the Apify API.

**Tip — build a history automatically:** create a [Schedule](https://docs.apify.com/platform/schedules) that runs every morning with `daysBack: 1`, `daysAhead: 0`, `statuses: ["finished"]`. Each run adds yesterday's matches with their closing lines.

### Ready-made examples

One click, preconfigured — open, press **Start**, adjust if you like:

- [Premier League odds history](https://apify.com/sports-odds-lab/football-odds-results/examples/premier-league-odds-history)
- [Closing odds for the top 5 European leagues](https://apify.com/sports-odds-lab/football-odds-results/examples/top-5-leagues-closing-odds)
- [Champions League odds history](https://apify.com/sports-odds-lab/football-odds-results/examples/champions-league-odds-history)
- [Yesterday's football results with closing odds](https://apify.com/sports-odds-lab/football-odds-results/examples/yesterday-football-closing-odds)
- [Today's matches with odds from 40+ bookmakers](https://apify.com/sports-odds-lab/football-odds-results/examples/todays-football-odds-40-bookmakers)
- [Over/under 2.5 goals odds dataset](https://apify.com/sports-odds-lab/football-odds-results/examples/over-under-2-5-goals-odds-dataset)
- [MLS odds history](https://apify.com/sports-odds-lab/football-odds-results/examples/mls-odds-history)
- [Brasileirão Serie A odds history](https://apify.com/sports-odds-lab/football-odds-results/examples/brazil-serie-a-odds-history)

#### Input example — recent matches

```json
{
  "daysBack": 1,
  "daysAhead": 0,
  "statuses": ["finished"],
  "competitions": ["England: Premier League", "Spain: LaLiga", "Europe: Champions League"],
  "markets": ["1x2", "overUnder", "btts", "asianHandicap"],
  "overUnderLines": ["1.5", "2.5", "3.5"],
  "includeStats": true,
  "includeEvents": true
}
```

#### Input example — full seasons (history)

```json
{
  "mode": "seasons",
  "leagues": ["England: Premier League", "Spain: LaLiga", "Germany: Bundesliga", "Italy: Serie A", "France: Ligue 1"],
  "seasons": ["2024-2025", "2023-2024", "2022-2023"],
  "markets": ["1x2", "overUnder", "btts", "asianHandicap"]
}
```

Seasons follow the league's own format: `2024-2025` for autumn–spring leagues, `2024` for calendar-year leagues (MLS, Brazil, Scandinavia). Leave `seasons` empty and set `lastSeasons` to get the current season plus the ones before it. A league can also be given as its Flashscore URL.

Country and competition names follow Flashscore: `England`, `Spain`, `Europe` (UEFA competitions), `World` (friendlies); `England: Premier League`, `Germany: Bundesliga`, `Brazil: Serie A Betano`. Stages are matched automatically — `Argentina: Liga Profesional` also returns its Apertura and Clausura.

#### Output example (shortened)

```json
{
  "matchId": "KlfMo4PO",
  "startTime": "2026-09-22T22:30:00Z",
  "country": "Brazil",
  "competition": "Serie B",
  "homeTeam": "Criciuma",
  "awayTeam": "Operario-PR",
  "status": "finished",
  "score": "0-2",
  "halfTimeScore": "0-1",
  "result": "A",
  "consensus": {
    "bookmakerCount": 41,
    "books1x2": 35,
    "probHomeOpening": 0.5242,
    "probHomeLast": 0.4715,
    "fairHomeOdds": 2.121,
    "fairDrawOdds": 3.385,
    "fairAwayOdds": 4.289,
    "moveHomePts": -5.27,
    "probOver25Last": 0.4079,
    "probBttsYesLast": 0.4613,
    "ahLine": -0.5,
    "ahLineOpening": -0.75,
    "margin1x2Pct": 7.76
  },
  "odds": {
    "1x2": [{"bookmaker": "bet365", "opening1": 1.68, "openingX": 3.3, "opening2": 4.75, "last1": 1.96, "lastX": 3.2, "last2": 4.0, "marginPct": 7.27}],
    "overUnder": [{"bookmaker": "bet365", "line": 2.5, "openingOver": 2.3, "openingUnder": 1.6, "lastOver": 2.3, "lastUnder": 1.6}],
    "btts": [{"bookmaker": "bet365", "openingYes": 2.2, "openingNo": 1.62, "lastYes": 2.0, "lastNo": 1.73}],
    "asianHandicap": [{"bookmaker": "bet365", "line": -0.5, "openingHome": 1.75, "openingAway": 2.05, "lastHome": 2.0, "lastAway": 1.8}]
  },
  "stats": {
    "expectedGoals": {"home": 1.34, "away": 0.33},
    "ballPossession": {"home": 67, "away": 33},
    "totalShots": {"home": 26, "away": 5},
    "passes": {"home": {"pct": 84, "successful": 413, "total": 489}, "away": {"pct": 67, "successful": 176, "total": 262}}
  },
  "events": [
    {"period": "1st Half", "minute": 38, "side": "away", "type": "goal", "player": "Caio Dantas", "assist": "Mikael Doka", "scoreAfter": "0-1"},
    {"period": "2nd Half", "minute": 90, "addedTime": 6, "side": "away", "type": "goal", "player": "Maxwell", "assist": "Boschilia", "scoreAfter": "0-2"}
  ]
}
```

### Pricing

⚽ **Free until 8 October 2026** — during the launch period you only pay Apify's platform usage, typically well under $0.01 per 100 matches.

**From 9 October 2026 you pay per match saved — platform usage included**, nothing else:

| Apify plan | Price per 1,000 matches |
|---|---|
| Free | $5.00 |
| Starter | $4.50 |
| Scale | $4.00 |
| Business and higher | $3.50 |

💡 **On Apify's free plan the monthly $5 platform credit covers about 1,000 matches** — enough to try the data properly before paying anything.

Every match comes with the odds of all bookmakers and markets you selected, the consensus, the statistics and the events, at no extra cost. Nothing is charged for matches you filter out: **Skip matches without odds** is on by default, so you only pay for priced matches. Set a spending limit on the run if you want a hard cap — the Actor stops as soon as the limit is reached.

### FAQ

**What is the "closing line"?** The last price before kick-off. For finished and live matches the `last…` fields are the closing odds; for scheduled matches they are the current prices.

**Which result do the 1X2 odds settle on?** 90 minutes plus stoppage time — that is the `result` field (H/D/A). Extra time and penalties are reported separately (`extraTimeScore`, `penaltiesScore`, `winner`).

**How is the consensus calculated?** For each bookmaker the margin is removed proportionally, then the median is taken across bookmaker brands. The median keeps one stale or mispriced book from skewing the result.

**What is the Asian handicap main line?** The line whose consensus is closest to 50/50. `ahLine` is the main line at the close, `ahLineOpening` at the open — a moved line is a strong market signal.

**How far back can I go?** In *Recent matches* mode 7 days back and 7 ahead. In *Full seasons* mode any season Flashscore lists — odds go back to the mid-2000s for major leagues (fewer bookmakers in older seasons, see the table above).

**Which time zone?** All timestamps are UTC.

**Is Pinnacle or Betfair Exchange included?** No — the source does not list them. The consensus of 20–35 bookmaker brands is a solid reference, and the Actor reports the best available price as well.

**Where does the data come from?** From publicly available match and odds information shown on Flashscore. This Actor is not affiliated with Flashscore or any bookmaker.

### More from Sports Odds Lab

- 🎾 [Tennis Scraper: Live Scores, Results & Odds](https://apify.com/sports-odds-lab/tennis-odds-results) — every ATP, WTA, Challenger and ITF match: live scores, rankings, odds from 40+ bookmakers back to 2009
- 🏀 [NBA, NFL, MLB & NHL Odds](https://apify.com/sports-odds-lab/nba-nfl-mlb-nhl-odds) — moneyline, spread and total history back to 2009, DraftKings, FanDuel, BetMGM
- 📊 [Polymarket & Kalshi Scraper](https://apify.com/sports-odds-lab/polymarket-kalshi-odds) — prediction market odds, price history and results in one table, full Kalshi archive since 2021

⭐ **Did this Actor save you time?** A short review on this page helps other bettors and analysts find it — and tells us what to build next.

### Changelog

- **0.2** — **Full seasons mode**: complete seasons of any league with opening/closing odds back to the mid-2000s; `season` and `round` fields; Asian handicap main line chosen by bookmaker vote (robust when only a few books quote a match).
- **0.1** — first release (free launch period): 1,000+ competitions; opening/closing odds from up to 42 bookmakers for 1X2, over/under, both teams to score, Asian handicap and double chance; no-vig consensus with line movement; scores incl. extra time and penalties; xG statistics; goal and card events.

# Actor input Schema

## `mode` (type: `string`):

Recent matches: every competition from up to 7 days back to 7 days ahead (upcoming matches with current odds). Full seasons: complete seasons of the leagues you list below, history back to about 2008 — add the leagues in the 'Full seasons' section.

## `statuses` (type: `array`):

Finished includes matches decided after extra time or penalties and awarded results. In 'Full seasons' mode only finished (and postponed/cancelled) matches exist.

## `daysBack` (type: `integer`):

How many past days to include (0 = today only). Up to 7.

## `daysAhead` (type: `integer`):

How many upcoming days to include. Up to 7. Upcoming matches carry the current prices instead of closing lines.

## `countries` (type: `array`):

Optional. Country or region names as shown on Flashscore, e.g. England, Spain, Brazil, Europe (UEFA club and national-team competitions), World (friendlies, FIFA). Leave empty for every country.

## `competitions` (type: `array`):

Optional. 'Country: Competition' as shown on Flashscore, e.g. 'England: Premier League', 'Spain: LaLiga', 'Europe: Champions League'. Stages are included automatically ('Argentina: Liga Profesional' also matches its Apertura and Clausura). A name without a country (e.g. 'Premier League') matches that name in every country.

## `includeWomen` (type: `boolean`):

Women's leagues, cups and national-team matches.

## `includeYouth` (type: `boolean`):

U17–U23, reserve, B-team and academy competitions.

## `leagues` (type: `array`):

'Country: League' as shown on Flashscore — e.g. 'England: Premier League', 'Spain: LaLiga', 'Germany: Bundesliga', 'Brazil: Serie A Betano', 'Europe: Champions League' — or the league's Flashscore URL. Every stage of a season is included (play-offs, championship and relegation groups).

## `seasons` (type: `array`):

Optional. Seasons as the league names them: 2024-2025 for autumn–spring leagues, 2024 for calendar-year leagues (MLS, Brazil, Scandinavia). Leave empty to use 'Last seasons'.

## `lastSeasons` (type: `integer`):

Used when Seasons is empty: the current season (matches played so far) plus this many minus one previous seasons. 5 = the current season and the four before it.

## `markets` (type: `array`):

Full-time markets to collect. 1X2 is always included — it shows which bookmakers priced the match.

## `overUnderLines` (type: `array`):

Goal lines to return, e.g. 1.5, 2.5, 3.5. Enter 'all' for every line each bookmaker offers (0.5 to 6.5, quarter lines included).

## `asianHandicapLines` (type: `string`):

The main line is the one whose consensus is closest to 50/50 (reported as ahLine; the opening main line as ahLineOpening).

## `bookmakers` (type: `array`):

Optional. Names or parts of names, e.g. bet365, Betano, Unibet. Leave empty for all available (up to 42).

## `includeOdds` (type: `boolean`):

Opening and latest (closing) odds per bookmaker plus the no-vig consensus.

## `onlyWithOdds` (type: `boolean`):

Do not save (and do not pay for) matches that no bookmaker priced. About 1 in 5 matches, mostly amateur and youth football.

## `includeStats` (type: `boolean`):

xG, xGOT, xA, possession, shots, big chances, corners, passes, fouls, cards and more for finished and live matches (full detail for major competitions).

## `includeEvents` (type: `boolean`):

Every goal with minute, scorer, assist and score after the goal; penalties (scored and missed), own goals, yellow and red cards.

## `maxMatches` (type: `integer`):

Stop after saving this many matches (0 = no limit). A busy weekend day has 1,500+ matches; a full season of a top league 300–400.

## `concurrency` (type: `integer`):

Advanced. Higher is faster; the default is safe.

## Actor input object example

```json
{
  "mode": "recent",
  "statuses": [
    "finished"
  ],
  "daysBack": 1,
  "daysAhead": 0,
  "includeWomen": true,
  "includeYouth": true,
  "leagues": [
    "England: Premier League"
  ],
  "seasons": [
    "2024-2025"
  ],
  "lastSeasons": 1,
  "markets": [
    "1x2",
    "overUnder",
    "btts",
    "asianHandicap"
  ],
  "overUnderLines": [
    "1.5",
    "2.5",
    "3.5"
  ],
  "asianHandicapLines": "main",
  "includeOdds": true,
  "onlyWithOdds": true,
  "includeStats": true,
  "includeEvents": true,
  "maxMatches": 30,
  "concurrency": 16
}
```

# Actor output Schema

## `overview` (type: `string`):

One row per match: teams, competition, score and the no-vig consensus.

## `results` (type: `string`):

Full records including the odds of every bookmaker, statistics and goal events.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "mode": "recent",
    "statuses": [
        "finished"
    ],
    "daysBack": 1,
    "daysAhead": 0,
    "leagues": [
        "England: Premier League"
    ],
    "seasons": [
        "2024-2025"
    ],
    "markets": [
        "1x2",
        "overUnder",
        "btts",
        "asianHandicap"
    ],
    "overUnderLines": [
        "1.5",
        "2.5",
        "3.5"
    ],
    "maxMatches": 30
};

// Run the Actor and wait for it to finish
const run = await client.actor("sports-odds-lab/football-odds-results").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "mode": "recent",
    "statuses": ["finished"],
    "daysBack": 1,
    "daysAhead": 0,
    "leagues": ["England: Premier League"],
    "seasons": ["2024-2025"],
    "markets": [
        "1x2",
        "overUnder",
        "btts",
        "asianHandicap",
    ],
    "overUnderLines": [
        "1.5",
        "2.5",
        "3.5",
    ],
    "maxMatches": 30,
}

# Run the Actor and wait for it to finish
run = client.actor("sports-odds-lab/football-odds-results").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "mode": "recent",
  "statuses": [
    "finished"
  ],
  "daysBack": 1,
  "daysAhead": 0,
  "leagues": [
    "England: Premier League"
  ],
  "seasons": [
    "2024-2025"
  ],
  "markets": [
    "1x2",
    "overUnder",
    "btts",
    "asianHandicap"
  ],
  "overUnderLines": [
    "1.5",
    "2.5",
    "3.5"
  ],
  "maxMatches": 30
}' |
apify call sports-odds-lab/football-odds-results --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,sports-odds-lab/football-odds-results"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/jj5etLhZJm1LXG7Gw/builds/jSsTSfAbiuJbmdCOX/openapi.json
