# Flashscore Betting Odds & Opening Lines Scraper (`incognito_mode/flashscore-odds-scraper`) Actor

Scrape pre-match betting odds from Flashscore: 1X2, over/under, Asian handicap, BTTS and more from 100+ bookmakers across countries, with opening prices and margins, plus outright winner odds for leagues and teams. 21 sports, one flat row per bookmaker line. No API key, no proxy.

- **URL**: https://apify.com/incognito\_mode/flashscore-odds-scraper.md
- **Developed by:** [Elena Vance](https://apify.com/incognito_mode) (community)
- **Categories:** Sports, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.43 / 1,000 bookmaker lines

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## 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

## Flashscore Betting Odds & Opening Lines Scraper

Scrape pre-match betting odds from [Flashscore](https://www.flashscore.com)'s
odds comparison — **1X2, moneyline, over/under, Asian and European handicap,
both teams to score** and more — from **100+ bookmakers across countries**,
with **every outcome's opening price next to its current one** and the
bookmaker's margin worked out. Football, tennis, basketball, hockey and 17 more
sports.

One flat row per bookmaker line, ready for a spreadsheet, pandas or SQL. No API
key, no login, no proxy, no browser. Export to **JSON, CSV, Excel, XML** or pull
it from the API.

***

### What you get

One row per **bookmaker × market × line × period**. Real, unedited output for
bet365's main over/under line on Girona v Albacete:

| Field | Value |
| --- | --- |
| `eventId` / `eventUrl` | `MNThRu6l` / `https://www.flashscore.com/match/MNThRu6l/` |
| `sport` / `tournament` | `football` / `SPAIN: LaLiga2 - Round 7` |
| `startTime` / `status` | `2026-09-25T18:30:00Z` / `finished` |
| `homeName` / `awayName` | `Girona` / `Albacete` |
| `homeScore` / `awayScore` | `2` / `0` |
| `bookmakerName` / `bookmakerId` | `bet365` / `16` |
| `bookmakerCountry` | `GB` |
| `market` / `period` / `line` | `OVER_UNDER` / `FULL_TIME` / `2.75` |
| `overOdds` / `overOpeningOdds` | `1.83` / `1.78` |
| `underOdds` / `underOpeningOdds` | `1.98` / `2.03` |
| `margin` / `openingMargin` | `0.0515` / `0.0544` |
| `outcomes` | `[{"name":"over","odds":1.83,"openingOdds":1.78,"change":0.05,"active":true}, …]` |
| `scrapedAt` | `2026-09-27T12:02:09Z` |

The prices most people want sit in fixed columns — `homeOdds`, `drawOdds`,
`awayOdds`, `overOdds`, `underOdds`, `yesOdds`, `noOdds`, each beside its
`…OpeningOdds` — so filtering to `market = "HOME_DRAW_AWAY"` and pivoting on
`bookmakerName` gives you an odds-comparison table without unpicking any JSON.
Markets with many outcomes (correct score, half-time/full-time) live in
`outcomes`, which every row also carries, with the price change per outcome.

#### Opening and closing prices

Every outcome has its **opening price** and its **current price**. For a
finished match the current price is the **last pre-match price** Flashscore
recorded — the closing line. That pair is what line-movement tracking,
closing-line-value analysis and model backtesting are built on, and Flashscore
keeps it for matches years old: pass an old match's id in `matchIds`.

#### Handicaps are always from the home side

`line: -1.5` on an Asian handicap row means **the home side gives 1.5**, with
`homeOdds` and `awayOdds` on the same row. Flashscore quotes each side from its
own point of view (home −1.5, away +1.5); this Actor pairs them, so a handicap
row is always a complete two-way book with a real margin.

In tennis, set and game lines are kept apart by `lineType` (`SETS` or
`GAMES`) — "+1.5" means very different things in each.

***

### More bookmakers: pick several countries

Flashscore shows the bookmakers licensed in one country at a time. List
several in `bookmakerCountries` and they are **merged into one dataset, each
bookmaker once**:

| Country | Bookmakers (one LaLiga2 match) |
| --- | --- |
| `GB` | 20 — bet365, Betfair, William Hill, Paddy Power, Ladbrokes, Coral, Sky Bet, Betfred, BetVictor, Betway, Unibet, … |
| `BR` | 20 |
| `IT` | 13 — SNAI, Sisal, Lottomatica, Eurobet, GoldBet, Planetwin365, bwin.it, … |
| `CA-ON` | 10 |
| `ES`, `FR`, `SE`, `DE`, `PL` | 6–9 each |
| `US-NJ` | 5 — **DraftKings, FanDuel**, BetMGM, Fanatics, bet365.us |

GB + DE + IT + ES + FR + BR returned **69 bookmakers on one match**. Each
country is one extra request per match. Where betting is licensed per state,
give the region (`US-PA`, `CA-ON`); a bare `US` means New Jersey and `CA`
means Ontario. A bookmaker licensed in two of your countries appears once,
from whichever you listed first.

***

### Outrights: who wins the league

Set `mode` to `outrights` and give league or team URLs in `outrightUrls` to
get **winner odds** — the title race, not single matches:

```json
{
  "mode": "outrights",
  "outrightUrls": [
    "https://www.flashscore.com/football/england/premier-league/",
    "https://www.flashscore.com/team/arsenal/hA1Zm19f/"
  ],
  "bookmakerCountries": ["GB", "DE"]
}
```

- **A league URL** gives every bookmaker's price on every team to win the
  current season — the Premier League from GB + DE was 100 rows: 20 teams ×
  bet365, Betfred, Betway, 7BetUK and Betano.de.
- **A team URL** gives that team's price in every competition it is quoted
  in — Arsenal: Premier League, Champions League, EFL Cup.

One row per **bookmaker × participant × competition**:

| Field | Value |
| --- | --- |
| `recordType` / `market` | `outright` / `WINNER` |
| `tournament` / `tournamentId` / `season` | `Premier League` / `SY30SsKF` / `2026/2027` |
| `participantName` / `participantId` | `Arsenal` / `hA1Zm19f` |
| `bookmakerName` / `bookmakerCountry` | `Betway` / `GB` |
| `odds` / `oddsChange` / `isBestOdds` | `1.57` / `down` / `true` |

`isBestOdds` marks the highest price across the bookmakers Flashscore shows
for that country. Outright rows are charged like match lines (one row, one
charge); `bookmakers`, `bookmakerCountries` and `maxItems` apply, the match
filters do not. A URL Flashscore does not have, or one no bookmaker in your
countries prices, is one unbilled `NO_OUTRIGHTS` row.

***

### Input

```json
{
  "sports": ["football", "tennis"],
  "days": ["0", "1"],
  "status": "scheduled",
  "leagues": ["https://www.flashscore.com/football/england/premier-league/"],
  "bookmakerCountries": ["GB", "DE", "US-NJ"],
  "bookmakers": [],
  "markets": ["HOME_DRAW_AWAY", "OVER_UNDER", "ASIAN_HANDICAP"],
  "periods": ["FULL_TIME"],
  "lines": "main",
  "maxMatches": 100,
  "maxItems": 1000
}
```

| Input | What it does |
| --- | --- |
| `sports` | Any of 21: football, tennis, basketball, hockey, american-football, baseball, handball, volleyball, rugby-union, rugby-league, cricket, futsal, floorball, aussie-rules, badminton, water-polo, field-hockey, table-tennis, boxing, mma, esports. |
| `days` | Offsets from today (`0`, `1`, `-1`) or dates (`2026-09-28`). UTC days, from 7 days back to 7 ahead. |
| `status` | `all`, `scheduled`, `live` or `finished`. |
| `leagues` | A Flashscore league URL (exact) or part of the name as Flashscore shows it (`ENGLAND: Premier League`). Empty = every competition. |
| `matchIds` | 8-character ids or match URLs, **of any age**. Replaces sports/days/status/leagues. |
| `bookmakerCountries` | Two-letter codes, with a region for per-state markets. Default `GB`. |
| `bookmakers` | Part of a name or a numeric id. `bet365` also matches `bet365.it` and `bet365.us`. |
| `markets` | Default: 1X2, moneyline, over/under, Asian handicap, BTTS. Also draw no bet, double chance, European handicap, correct score, HT/FT, odd/even. Empty = all. |
| `periods` | `FULL_TIME` by default; `FIRST_HALF`, `SECOND_HALF`, `FIRST_SET`, … Empty = all. |
| `lines` | `main` keeps each bookmaker's line nearest even money; `all` keeps every alternative total and handicap. |
| `maxMatches` | Matches with odds to price, soonest first, taking sports in turn. Default `100`. |
| `maxItems` | Hard cap on charged rows. Default `1000`. |
| `mode` | `matches` (default) or `outrights` — see above. |
| `outrightUrls` | League or team URLs for `outrights` mode, up to 100. |

#### ⚠️ How many rows a match is

With the defaults, **one match priced by the 20 GB bookmakers is 60–70 rows**:
about 20 each of 1X2 and BTTS, and one main over/under and handicap line per
bookmaker that offers them. Every extra country adds its new bookmakers;
`lines: "all"` multiplies over/under and handicap rows by 5–10; correct score
is one row per bookmaker holding ~25 outcomes. `maxItems` is the cost control.

**A line is one charge however many outcomes it has.** A 1X2 row is one row,
not three.

***

### What you are not charged for

- **Matches with no odds.** A day list is full of youth, amateur and ITF
  fixtures no bookmaker prices, and tennis is often unpriced until hours
  before play. They are skipped and counted in the run's status message.
- **Match ids Flashscore does not have** — one unbilled `MATCH_NOT_FOUND` row.
- **A requested match with no odds** — one unbilled `NO_ODDS` row saying which
  countries were asked, so you can try others.
- **Bad input** — an `INVALID_INPUT` row explaining the problem, not a failed
  run.
- **The same bookmaker twice** when two of your countries list it.
- **Failures.** A match whose odds could not be fetched from every requested
  country is reported by id in the status message and not charged; re-running
  usually succeeds.

***

### Why this is cheap to run

Flashscore's data comes from two keyless APIs that do not gate on IP, and the
bookmaker country is **a query parameter, not your IP address** — so several
countries cost one small request each rather than a residential proxy per
country. A run is one request per sport per day plus one per match per country.
Ten matches take about five seconds.

The Actor start fee is **$0.00005** and the default memory is **512 MB**, under
the 1 GB line, so Apify's per-gigabyte start fee is charged once.

Leave `proxyConfiguration` off. A proxy adds cost and no bookmakers; a
datacenter IP is tried automatically only if Flashscore ever refuses a request.

***

### What this Actor does NOT return

- **Live in-play odds.** Flashscore's comparison is pre-match. For a live or
  finished match you get the last pre-match price.
- **Price history between open and close.** Flashscore publishes the opening
  and the current price only. Schedule the Actor every N minutes to build a
  movement history — every row carries `scrapedAt`.
- **Player props, top-scorer or relegation markets, golf, horse racing or
  motorsport.** Outrights are the winner market only.
- **Pinnacle.** Flashscore does not list it in any country checked.

***

### What makes this different

- **Several countries, one dataset.** 100+ bookmakers are reachable; no other
  Store Actor merges countries.
- **Opening price on every outcome**, the change since open, and the margin
  on both — computed only over complete books, never over a partial one.
- **Flat rows.** One bookmaker line per row with fixed price columns, not one
  nested object per match.
- **Handicaps paired from the home side**, and tennis set and game lines kept
  apart, so every line is a real two- or three-way book.
- **21 sports**, any day from a week back to a week ahead, or any match by id.
- **Never billed for nothing** — no-odds matches, missing ids, duplicate
  bookmakers and failures are free.
- **A weekly canary** runs the deployed build against live Flashscore and opens
  a GitHub issue if field coverage drops.

***

### Example runs

**Today's football, GB bookmakers, 1X2 only:**

```json
{ "sports": ["football"], "days": ["0"], "markets": ["1X2"], "maxMatches": 50 }
```

**This weekend's Premier League across six countries:**

```json
{
  "days": ["2026-10-03", "2026-10-04"],
  "leagues": ["https://www.flashscore.com/football/england/premier-league/"],
  "bookmakerCountries": ["GB", "DE", "IT", "ES", "FR", "BR"]
}
```

**US sportsbooks on basketball and hockey:**

```json
{ "sports": ["basketball", "hockey"], "bookmakerCountries": ["US-NJ", "US-PA"], "status": "scheduled" }
```

**Closing lines for matches you already know, every market and line:**

```json
{ "matchIds": ["MNThRu6l"], "markets": [], "periods": [], "lines": "all" }
```

**NBA championship odds from US sportsbooks:**

```json
{ "mode": "outrights", "outrightUrls": ["https://www.flashscore.com/basketball/usa/nba/"], "bookmakerCountries": ["US-NJ"] }
```

**Only bet365 and Betfair, every alternative total:**

```json
{ "bookmakers": ["bet365", "betfair"], "markets": ["OVER_UNDER"], "lines": "all" }
```

***

### Ready-made examples

Open one, press **Try for free**, and change the input to your own:

- [Compare Premier League odds across UK bookmakers](https://apify.com/incognito_mode/flashscore-odds-scraper/examples/premier-league-odds-comparison)
- [Get Premier League title winner odds](https://apify.com/incognito_mode/flashscore-odds-scraper/examples/premier-league-title-winner-odds)
- [Get NBA championship odds from US sportsbooks](https://apify.com/incognito_mode/flashscore-odds-scraper/examples/nba-championship-odds)
- [Get closing odds for past football matches](https://apify.com/incognito_mode/flashscore-odds-scraper/examples/football-closing-lines-for-backtesting)

### More Flashscore Actors

Same data source, same flat rows and pay-per-result pricing:

- [Flashscore Odds Movement Tracker](https://apify.com/incognito_mode/flashscore-odds-tracker) — line movement and exact closing lines on a schedule
- [Flashscore Match Stats Scraper](https://apify.com/incognito_mode/flashscore-match-stats-scraper) — player stats with xG, lineups, box scores and team stats per match
- [Flashscore League Archive](https://apify.com/incognito_mode/flashscore-league-archive-scraper) — every season's results, tables and top scorers, back to 1901
- [Flashscore Teams & Players](https://apify.com/incognito_mode/flashscore-team-player-scraper) — squads, transfers with fees, market values and player careers
- [Flashscore Tennis Scraper](https://apify.com/incognito_mode/flashscore-tennis-scraper) — ATP/WTA results, serve stats, point-by-point, draws and rankings

# Actor input Schema

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

`matches`: odds for matches (the fields below). `outrights`: who-wins-the-competition odds for the league and team URLs in Outright URLs; Sports, Days, Status, Leagues, Match IDs, Markets, Periods and Lines are then ignored.

## `outrightUrls` (type: `array`):

Outrights mode only. A league page gives every participant's price to win that competition (e.g. https://www.flashscore.com/football/england/premier-league/). A team page gives the team's price in each competition it can win (e.g. https://www.flashscore.com/team/arsenal/hA1Zm19f/).

## `sports` (type: `array`):

Which sports to list matches for. Ignored when Match IDs are given.

## `days` (type: `array`):

Which days to list, as offsets from today (0 = today, 1 = tomorrow, -1 = yesterday) or dates like 2026-09-28. Days are UTC calendar days. Flashscore lists matches from 7 days ago to 7 days ahead; for an older match use Match IDs.

## `status` (type: `string`):

Upcoming matches carry the live pre-match price. Finished matches keep the last pre-match price Flashscore recorded next to the opening one — the data for backtesting and closing-line value.

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

Only matches from these competitions. Either part of the name as Flashscore shows it ("Premier League" also matches "Premier League 2", so prefer "ENGLAND: Premier League") or a Flashscore league URL, which matches exactly. Leave empty for every competition.

## `matchIds` (type: `array`):

Price specific matches instead of listing days: the 8-character id (MNThRu6l) or any Flashscore match URL. Works for matches of any age. When set, Sports, Days, Status and Leagues are ignored.

## `bookmakerCountries` (type: `array`):

Flashscore shows the bookmakers licensed in one country at a time. List several and their bookmakers are merged, each bookmaker once: GB alone is about 20 bookmakers, GB + DE + IT + ES + FR + BR + CA over 70. Two-letter codes; per-state markets take a region (US-NJ, US-PA, CA-ON). Each country is one extra request per match.

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

Only these bookmakers, by part of the name or by numeric id. "bet365" also matches bet365.it and bet365.us. Leave empty for every bookmaker in the chosen countries.

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

Which betting markets to return. Every selected market multiplies the rows per match: correct score alone is one row with 25 outcomes per bookmaker. An empty list returns every market Flashscore has.

## `periods` (type: `array`):

Which part of the match: FULL\_TIME, FIRST\_HALF, SECOND\_HALF, FULL\_TIME\_OVER\_TIME (including overtime), FIRST\_SET, SECOND\_SET. An empty list returns every period.

## `lines` (type: `string`):

Over/under and handicap markets come in many lines per bookmaker (2.5 goals, 3.5 goals, ...). "Main line only" keeps the one each bookmaker prices closest to even money; "All lines" keeps every one and is typically 5-10 times the rows.

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

Stop after this many matches with odds, soonest kick-off first. With several sports, matches are taken from each sport in turn so one sport cannot fill the cap. Matches with no odds do not count and are never charged.

## `maxItems` (type: `integer`):

Hard cap on charged rows for this run. One match priced by 20 bookmakers with the default markets is about 70 rows, so this is the main cost control.

## `proxyConfiguration` (type: `object`):

Leave this off. Flashscore's data is reachable directly, and the bookmaker country is chosen above, not by IP — a proxy adds cost and no bookmakers. A datacenter IP is tried automatically only if a request is actually refused.

## Actor input object example

```json
{
  "mode": "matches",
  "outrightUrls": [
    "https://www.flashscore.com/football/england/premier-league/",
    "https://www.flashscore.com/team/arsenal/hA1Zm19f/"
  ],
  "sports": [
    "football"
  ],
  "days": [
    "0",
    "1"
  ],
  "status": "all",
  "leagues": [
    "https://www.flashscore.com/football/england/premier-league/",
    "SPAIN: LaLiga"
  ],
  "matchIds": [
    "https://www.flashscore.com/match/MNThRu6l/"
  ],
  "bookmakerCountries": [
    "GB",
    "DE",
    "US-NJ"
  ],
  "bookmakers": [
    "bet365",
    "Betfair",
    "William Hill"
  ],
  "markets": [
    "HOME_DRAW_AWAY",
    "HOME_AWAY",
    "OVER_UNDER",
    "ASIAN_HANDICAP",
    "BOTH_TEAMS_TO_SCORE"
  ],
  "periods": [
    "FULL_TIME"
  ],
  "lines": "main",
  "maxMatches": 3,
  "maxItems": 1000,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

Dataset containing every scraped bookmaker line.

# 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 = {
    "sports": [
        "football"
    ],
    "days": [
        "0"
    ],
    "bookmakerCountries": [
        "GB"
    ],
    "maxMatches": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("incognito_mode/flashscore-odds-scraper").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 = {
    "sports": ["football"],
    "days": ["0"],
    "bookmakerCountries": ["GB"],
    "maxMatches": 3,
}

# Run the Actor and wait for it to finish
run = client.actor("incognito_mode/flashscore-odds-scraper").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 '{
  "sports": [
    "football"
  ],
  "days": [
    "0"
  ],
  "bookmakerCountries": [
    "GB"
  ],
  "maxMatches": 3
}' |
apify call incognito_mode/flashscore-odds-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,incognito_mode/flashscore-odds-scraper"
        }
    }
}
```

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/fAwdS8TpbT1ROHDbk/builds/6pgIyWpOLBFgZ75Nw/openapi.json
