# Football Odds API France: Winamax, NetBet, PokerStars (`oddsfrance/french-sportsbooks-odds`) Actor

Compare French bookmakers odds in one run: Winamax, NetBet and PokerStars Sports with the same fields for every site. Find the best price or the bookmaker margin, or build an odds history for Ligue 1 and more. No subscription, pay per row. JSON, CSV, Excel.

- **URL**: https://apify.com/oddsfrance/french-sportsbooks-odds.md
- **Developed by:** [OddsFrance](https://apify.com/oddsfrance) (community)
- **Categories:** Sports, Developer tools, Automation
- **Features:** Limited permissions
- **Stats:** 1 total users, 0 monthly users, 0.0% 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

## French Bookmakers Odds API: Winamax, NetBet, PokerStars

**French bookmakers odds in one run**: Winamax, NetBet and PokerStars Sports with the same 19 fields for every site, ready to compare prices on the same match. No subscription, you pay only for the rows you keep.

Get the odds of three sportsbooks licensed in France (**Winamax**, **NetBet** and **PokerStars Sports**) in one dataset, with the same fields for every bookmaker. Useful to compare prices on the same match side by side, or to follow how the French market prices an event.

### Why this actor

- **One schema for three French sportsbooks**: compare odds side by side without writing three parsers.
- **Best price and margin in a few lines** of pandas (example below, tested on a real run).
- **Fault tolerant**: if one site blocks the run, the other two still deliver.
- **No subscription**: no monthly plan, no credits to manage. Each run is billed on the rows it saves, and `maxItems` caps the bill.
- **Flat rows, not nested JSON**: one row per outcome, the same 19 fields every time, decimal odds as numbers. Opens directly in Excel or Google Sheets.

### Quick start

1. Click **Try for free** (or **Start**). The default input reads football and finishes in 5 to 35 seconds.
2. Open the **Output** tab: one row per outcome, with competition, teams, start time and odds.
3. Download as CSV, Excel or JSON, or call the actor from your code (examples below).
4. Need fresh odds every hour? Save your input as a task and add a **Schedule**.

### Use cases

- **Betting tips and affiliate sites**: show today's French odds next to your predictions, refreshed by a schedule, without paying for a full odds feed.
- **Value betting and odds comparison**: compare the same match across bookmakers in one run, spot the best price and the bookmaker margin.
- **Prediction models and research**: schedule a run every hour or every day and keep each dataset to build your own odds history (opening price, closing price, line moves).
- **Telegram or Discord bots**: send the odds of tonight's matches to a channel with n8n or Make (Apify node, then Telegram or Discord node).
- **Dashboards in Google Sheets or Looker Studio**: one tab per run, refreshed automatically.

### What you get

- Websites: winamax.fr, netbet.fr, pokerstars.fr/sports (input `bookmakers` to choose).
- Sports: the union of the three actors below; each bookmaker returns the sports it lists.
- Markets: as in the single-bookmaker actors: the main market of each match on all three, plus totals and handicaps on PokerStars and outrights on Winamax.
- `maxItems` is shared equally between the chosen bookmakers, so one bookmaker cannot fill the whole dataset. Events are not merged: match them on teams and `startTime` (each site has its own ids and spelling).
- If one site blocks the run, the others still deliver and the run status names the blocked site.
- Pre-match by default; set `includeLive` to also keep events in play that the page lists.
- One row per outcome, the same 19 fields in every row (table below), decimal odds as numbers, times in UTC.

### How it works

The actor opens one or two pages per sport and bookmaker with a plain HTTP request, the way a browser loads it, and reads the odds that the page already contains. It reads `robots.txt` first and never opens a disallowed path, sends at most one request every 2 seconds to each website, uses no login, no cookies and no proxy. If a site answers with a block (HTTP 403 or 429, captcha page), the run stops at once with a clear message, without retrying. A default run takes 5 to 35 seconds.

### Input

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `bookmakers` | array | all three | `winamax`, `netbet`, `pokerstars`. |
| `sports` | array | `["football"]` | Sports to read (list in the input form). |
| `competitions` | array | empty | Optional text filters on the competition name, e.g. `Ligue 1`, `NBA`. Empty keeps all. |
| `markets` | array | `match_winner`, `1x2` | Market types to keep. |
| `includeLive` | boolean | `false` | Also keep events already in play. |
| `maxItems` | integer | `200` | Maximum number of rows saved (and paid). |

Example input:

```json
{
  "bookmakers": [
    "winamax",
    "netbet",
    "pokerstars"
  ],
  "sports": [
    "football"
  ],
  "markets": [
    "1x2",
    "match_winner"
  ],
  "includeLive": false,
  "maxItems": 300
}
```

### Output

Three rows copied from a real run of this actor (run on Apify, 2026-11-12):

```json
[
  {
    "bookmaker": "Winamax",
    "bookmakerCountry": "FR",
    "sport": "football",
    "competition": "Ligue des Nations",
    "country": "Europe",
    "eventId": "68931500",
    "eventName": "Italie - France",
    "homeTeam": "Italie",
    "awayTeam": "France",
    "startTime": "2026-11-12T19:45:00Z",
    "isLive": false,
    "marketType": "1x2",
    "marketName": "Résultat",
    "outcome": "home",
    "outcomeName": "Italie",
    "line": null,
    "oddsDecimal": 3.45,
    "scrapedAt": "2026-11-12T19:17:31Z",
    "sourceUrl": "https://www.winamax.fr/paris-sportifs/sports/1"
  },
  {
    "bookmaker": "NetBet",
    "bookmakerCountry": "FR",
    "sport": "football",
    "competition": "Ligue des Nations",
    "country": null,
    "eventId": "12174363",
    "eventName": "Italie - France",
    "homeTeam": "Italie",
    "awayTeam": "France",
    "startTime": "2026-11-12T19:45:00Z",
    "isLive": false,
    "marketType": "1x2",
    "marketName": "Qui va gagner le match ?",
    "outcome": "home",
    "outcomeName": "Italie",
    "line": null,
    "oddsDecimal": 3.75,
    "scrapedAt": "2026-11-12T19:17:34Z",
    "sourceUrl": "https://www.netbet.fr/football"
  },
  {
    "bookmaker": "PokerStars Sports",
    "bookmakerCountry": "FR",
    "sport": "football",
    "competition": "Ligue des nations UEFA - A",
    "country": null,
    "eventId": "36190241",
    "eventName": "Italie - France",
    "homeTeam": "Italie",
    "awayTeam": "France",
    "startTime": "2026-11-12T19:45:00Z",
    "isLive": false,
    "marketType": "1x2",
    "marketName": "Cotes du match",
    "outcome": "home",
    "outcomeName": "Italie",
    "line": null,
    "oddsDecimal": 3.9,
    "scrapedAt": "2026-11-12T19:17:47Z",
    "sourceUrl": "https://www.pokerstars.fr/sports/football/1/"
  }
]
```

| Field | Description |
| --- | --- |
| `bookmaker` | Bookmaker name (Winamax, NetBet, PokerStars Sports). |
| `bookmakerCountry` | Country of the bookmaker website that was read (FR, ES, DE). |
| `sport` | Normalized sport (football, tennis, basketball, ice_hockey, ...). |
| `competition` | Competition name as shown on the site (tennis circuit prefixed, e.g. ATP, WTA). |
| `country` | Country or region of the competition when the site gives one, else null. |
| `eventId` | Event id on the bookmaker site (not shared between bookmakers). |
| `eventName` | Event name as shown on the site. |
| `homeTeam` | Home team, or first participant for individual sports. |
| `awayTeam` | Away team, or second participant. |
| `startTime` | Start time, ISO 8601 UTC. |
| `isLive` | true if the event was in play when read. |
| `marketType` | Normalized market: match_winner, 1x2, over_under, handicap, both_teams_to_score, outright, other. |
| `marketName` | Market label on the site (original language). |
| `outcome` | Normalized outcome: home, draw, away, over, under, yes, no, or the participant name. |
| `outcomeName` | Outcome label on the site. |
| `line` | Handicap or total line, else null. |
| `oddsDecimal` | Decimal odds (number). |
| `scrapedAt` | Time the page was read, ISO 8601 UTC. |
| `sourceUrl` | Public page the row was read from. |

Labels (competition, market, outcome names) stay in the language of the website. You can download the dataset as JSON, CSV, Excel or XML from the run page or through the API.

### Price

Free to use during the launch: there is no actor fee, you only pay the Apify platform usage of your run (a default run of up to 200 rows uses well under $0.01, covered by the free Apify plan). The planned price is **$1.50 per 1,000 rows** saved to the dataset, platform usage included; Apify announces any price change to users 14 days before it applies.

### Use it from your code, Google Sheets, n8n or Make

Run the actor and get the rows in one HTTP call (replace `YOUR_API_TOKEN` with your Apify API token, from Console > Settings > API & Integrations):

```bash
curl -X POST "https://api.apify.com/v2/acts/oddsfrance~french-sportsbooks-odds/run-sync-get-dataset-items?token=YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"bookmakers": ["winamax", "netbet", "pokerstars"], "sports": ["football"], "markets": ["1x2", "match_winner"], "includeLive": false, "maxItems": 300}'
```

Python (`pip install apify-client`, version 3.x):

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_API_TOKEN")
run = client.actor("oddsfrance/french-sportsbooks-odds").call(run_input={"bookmakers": ["winamax", "netbet", "pokerstars"], "sports": ["football"], "markets": ["1x2", "match_winner"], "includeLive": False, "maxItems": 300})
for row in client.dataset(run.default_dataset_id).iterate_items():
    print(row["eventName"], row["outcome"], row["oddsDecimal"])
```

JavaScript or TypeScript (`npm install apify-client`):

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

const client = new ApifyClient({ token: 'YOUR_API_TOKEN' });
const run = await client.actor('oddsfrance/french-sportsbooks-odds').call({"bookmakers": ["winamax", "netbet", "pokerstars"], "sports": ["football"], "markets": ["1x2", "match_winner"], "includeLive": false, "maxItems": 300});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.slice(0, 5));
```

- **Google Sheets**: add the Apify integration for Google Sheets to a task (Integrations tab), or import the CSV export link of the dataset with `=IMPORTDATA("...")`.
- **n8n**: the official Apify node runs the actor and returns the rows; chain a Google Sheets, Telegram or Discord node.
- **Make (Integromat)** and **Zapier**: the Apify app has "Run an Actor" and "Get dataset items" modules.
- **Schedules and webhooks**: run it every few minutes or hours with an Apify schedule; a webhook can call your server when each run finishes.
- **AI agents**: the actor is available through the Apify MCP server, so Claude, ChatGPT or Cursor can call it as a tool.

### Recipe: best price and bookmaker margin

Tested on a real run (pandas and apify-client 3.x). Prints the best odds per outcome and the average 1X2 margin of each bookmaker:

```python
## (apify-client 3.x) Best price per outcome across Winamax, NetBet and PokerStars Sports, and the margin of each bookmaker.
import pandas as pd
from apify_client import ApifyClient

client = ApifyClient("YOUR_API_TOKEN")
run = client.actor("oddsfrance/french-sportsbooks-odds").call(
    run_input={"sports": ["football"], "markets": ["1x2"], "maxItems": 300}
)
df = pd.DataFrame(client.dataset(run.default_dataset_id).list_items().items)

## Same match on every site: first letters of both teams + start time (each site spells teams its own way).
key = lambda s: s.str.lower().str.replace(r"[^a-z]", "", regex=True).str[:5]
df["match"] = key(df["homeTeam"]) + "-" + key(df["awayTeam"]) + " " + df["startTime"]
best = df.loc[df.groupby(["match", "outcome"])["oddsDecimal"].idxmax()]
print(best[["eventName", "outcome", "bookmaker", "oddsDecimal"]].head(9).to_string(index=False))

## Bookmaker margin (overround) on complete 1X2 markets: sum of 1/odds minus 1, averaged per bookmaker.
full = df.groupby(["bookmaker", "match"]).filter(lambda m: len(m) == 3)
margin = full.groupby(["bookmaker", "match"])["oddsDecimal"].apply(lambda o: (1 / o).sum() - 1)
print(margin.groupby("bookmaker").mean().round(3))
```

### Recipe: build your own odds history

Save your input as a task, add a schedule (for example every hour), then rebuild the price moves from the last runs. Tested on real runs of this actor:

```python
## Odds history (apify-client 3.x): run the actor on an Apify schedule (e.g. every hour), then rebuild the price moves.
import pandas as pd
from apify_client import ApifyClient

client = ApifyClient("YOUR_API_TOKEN")
runs = client.actor("oddsfrance/french-sportsbooks-odds").runs().list(desc=True, limit=24, status="SUCCEEDED").items
history = pd.concat(
    [pd.DataFrame(client.dataset(r.default_dataset_id).list_items().items) for r in runs],
    ignore_index=True,
).sort_values("scrapedAt")

odds = history.groupby(["bookmakerCountry", "eventName", "marketType", "outcome"])["oddsDecimal"]
moves = pd.DataFrame({"first": odds.first(), "last": odds.last(), "snapshots": odds.size()})
moves["change"] = moves["last"] - moves["first"]
print(moves[moves["snapshots"] > 1].sort_values("change").head(10))
```

### Compared with The Odds API

The Odds API is a good product with a much wider bookmaker list. This actor is narrower and cheaper for the French market. Facts checked on their public pages on 12 November 2026:

| | This actor | The Odds API |
| --- | --- | --- |
| Pricing | Pay per row, no plan (free during launch) | Monthly plans: 500 free credits, then from $30 per month |
| Winamax France and Germany | Yes | Yes |
| Winamax Spain | Yes | Not in their bookmaker list |
| NetBet France | Yes | Yes |
| PokerStars Sports France | Yes | Not in their bookmaker list |
| Markets | Every market the public sport page lists | h2h, spreads, totals; more on selected sports |
| Odds history | Build your own with a schedule | Included in paid plans |
| Format | Flat rows (CSV, Excel ready) | Nested JSON per event |
| Worldwide coverage (US, UK, AU books) | No, French market only | Yes |

Pick The Odds API for many bookmakers worldwide or a ready-made history. Pick this actor for French bookmakers, no subscription, and rows you can open in a spreadsheet.

### Limits

- Odds move all the time: a row is a snapshot at `scrapedAt`, not a live feed. Live events are kept only when the page lists them, with no guarantee of latency or completeness.
- Only what the public page shows is returned. Fewer rows than `maxItems` means fewer open markets.
- The actor depends on the structure of the website. If the site changes or blocks Apify servers, runs can fail or return no rows until the actor is updated; the run log says why. The actor is tested every day on Apify.
- Event ids, team names and competition names are the bookmaker's own; there is no cross-site id.

### FAQ

**How fresh are the odds?** They are read when the run starts. A default run takes 5 to 35 seconds; schedule it as often as you need.

**Can I get only Ligue 1, the Premier League or the NBA?** Yes: put the name in `competitions` (case-insensitive text match).

**Can I get historical odds?** Not from the past, but every scheduled run is kept as a dataset, so your history starts with your first run (recipe above).

**Does it use proxies or bypass protections?** No. It reads public pages at a slow pace, follows `robots.txt` and stops when a site refuses access.

**Why not Betclic, Unibet or Parions Sport?** Their pages refuse requests from Apify servers or are protected by an anti-bot service, and this actor never bypasses a protection.

**Why did my run stop early?** The status message says it: a site answered with a block (403, 429 or captcha), or the spending limit of the run was reached. Rows saved before that are kept.

**Is a row the same as a match?** No. One row is one outcome: a 1X2 match gives three rows.

**Something is missing or broken?** Open an issue in the Issues tab of this actor.

### Other OddsFrance actors

- [Winamax France, Spain and Germany](https://apify.com/oddsfrance/winamax-odds-scraper)
- [NetBet France](https://apify.com/oddsfrance/netbet-odds-scraper)
- [PokerStars Sports France (totals and handicaps)](https://apify.com/oddsfrance/pokerstars-sports-odds-scraper)

### Legal and responsible use

- This actor is not affiliated with, endorsed by or sponsored by Winamax, NetBet, PokerStars or any bookmaker. Brand names belong to their owners and are used only to describe the source of the data.
- It collects public data only: odds that anyone can see on the website without an account. It collects no personal data.
- The data is provided for information purposes only, with no guarantee of accuracy or availability.
- You are responsible for complying with the terms of the websites and with the law of your country when you use this actor and its data.
- This actor does not encourage gambling and gives no betting advice. It promises no profit and no arbitrage. Gambling is for adults (18+) only and can be addictive; in France, help is available at joueurs-info-service.fr (09 74 75 13 13).

### En français

**Comparateur de cotes des bookmakers français** : Winamax, NetBet et PokerStars Sports dans un seul fichier, mêmes colonnes pour les trois. Trouvez la meilleure cote d'un match de Ligue 1, mesurez la marge de chaque opérateur, gardez l'historique avec un run planifié. Sans abonnement, vous payez à la ligne.

### En español

**Cuotas de las casas de apuestas francesas** (Winamax, NetBet, PokerStars Sports) en un solo conjunto de datos, mismas columnas para las tres. Sin suscripción.

# Actor input Schema

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

French sportsbooks to read, one after the other. A bookmaker that blocks the request is skipped with a clear message; the others still run.

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

Sports to read, one page per sport. Keep the list short for fast, cheap runs.

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

Optional. Keep only events whose competition or country contains one of these words (case-insensitive), e.g. "Ligue 1", "Premier League", "ATP". Empty = every competition on the page.

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

Normalised market types to keep. Only markets shown on the public sport pages can be returned (see the README).

## `includeLive` (type: `boolean`):

Also return events already in play, if the page shows them. Live odds move fast: treat them as a snapshot.

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

Maximum number of odds rows (one row = one outcome of one market). The run stops reading pages once it is reached.

## Actor input object example

```json
{
  "bookmakers": [
    "winamax",
    "netbet",
    "pokerstars"
  ],
  "sports": [
    "football"
  ],
  "competitions": [],
  "markets": [
    "match_winner",
    "1x2"
  ],
  "includeLive": false,
  "maxItems": 200
}
```

# Actor output Schema

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

Odds rows of the run, one per (event, market, outcome, bookmaker).

# 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 = {
    "bookmakers": [
        "winamax",
        "netbet",
        "pokerstars"
    ],
    "sports": [
        "football"
    ],
    "competitions": [],
    "markets": [
        "match_winner",
        "1x2"
    ],
    "maxItems": 200
};

// Run the Actor and wait for it to finish
const run = await client.actor("oddsfrance/french-sportsbooks-odds").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 = {
    "bookmakers": [
        "winamax",
        "netbet",
        "pokerstars",
    ],
    "sports": ["football"],
    "competitions": [],
    "markets": [
        "match_winner",
        "1x2",
    ],
    "maxItems": 200,
}

# Run the Actor and wait for it to finish
run = client.actor("oddsfrance/french-sportsbooks-odds").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 '{
  "bookmakers": [
    "winamax",
    "netbet",
    "pokerstars"
  ],
  "sports": [
    "football"
  ],
  "competitions": [],
  "markets": [
    "match_winner",
    "1x2"
  ],
  "maxItems": 200
}' |
apify call oddsfrance/french-sportsbooks-odds --silent --output-dataset

```

## MCP server setup

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

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/BQW4FkIHftHjca5bq/builds/aEGyXBXCNj6bx5hPe/openapi.json
