# Pinnacle Odds Scraper (`mrdoe/pinnacle-odds-scraper`) Actor

Scrape Pinnacle betting odds, live and pre-match sports odds for soccer, tennis, basketball, football and more. Extract 1X2 moneyline, Asian handicaps, spreads, over/under totals and team totals with American and decimal prices for odds comparison, arbitrage and analysis.

- **URL**: https://apify.com/mrdoe/pinnacle-odds-scraper.md
- **Developed by:** [MrDoe](https://apify.com/mrdoe) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 1,000 results

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

![Pinnacle Odds Scraper hero](https://api.apify.com/v2/key-value-stores/kE36venAoVchGsE6b/records/pinnacle-odds-scraper--hero.png)

### What does Pinnacle Odds Scraper do?

Pinnacle Odds Scraper collects betting odds from Pinnacle and returns them as clean, ready-to-use data — one row per match. Pick a sport (or paste a league ID or a match URL), press **Start**, and download a dataset with the two teams, league, start time, live status, and **prices for every main market**: moneyline (1X2), spread / Asian handicap, totals (over/under) and team totals, for the full match and the first half. Every price comes in both **American and decimal** formats, and a full `markets` array keeps every alternate handicap and total line.

Use it for odds comparison, closing-line value analysis, model back-testing, arbitrage screening, and trading dashboards. Pinnacle's low margins and high limits make its price the reference line the rest of the market follows.

### Why use Pinnacle Odds Scraper?

- **Sharp reference odds** – Pinnacle is the market benchmark; its line is what serious models compare against.
- **Prices you can use immediately** – `odds.moneyline.match.home`, `odds.total.match.over`, `odds.spread.match.handicap`. No decoding.
- **American and decimal** – both formats on every price, so no conversion downstream.
- **Pre-match and live** – switch between the pre-match board and the in-play feed, or take both.
- **Whole leagues in one run** – point it at a league and it walks every fixture.
- **Every line kept** – alternate handicaps and totals are all in the `markets` array, not just the main line.
- **Clean, normalised output** – odds as numbers, ISO start times, deduplicated by match.

### What makes this different?

Most Pinnacle data online is either stale or the raw, deeply nested market feed. This Actor flattens it: the main markets come out as named fields (`moneyline`, `spread`, `total`, `teamTotal`) split by period, with the main line already picked and both odds formats attached — while the complete alternate-line detail stays available in `markets`. It also filters out the derived corner / card / booking prop markets that share the same shape, so a row is just the match itself.

### What data can you extract?

![Pinnacle Odds Scraper dataset output](https://api.apify.com/v2/key-value-stores/kE36venAoVchGsE6b/records/pinnacle-odds-scraper--output.png)

![Pinnacle Odds Scraper data fields](https://api.apify.com/v2/key-value-stores/kE36venAoVchGsE6b/records/pinnacle-odds-scraper--fields.png)

| Field                       | Description                                              |
| --------------------------- | ------------------------------------------------------- |
| `matchId`                   | Pinnacle matchup ID                                     |
| `url`                       | Match page URL on pinnacle.com                          |
| `bookmaker`                 | Always `Pinnacle`                                       |
| `sport` / `sportId`         | Sport                                                   |
| `league` / `leagueId`       | Competition                                             |
| `country`                   | Region / grouping the league sits under                 |
| `home` / `away`             | Team or player names                                    |
| `rotationNumber`            | Pinnacle rotation number for the match                  |
| `startTime`                 | Scheduled start (ISO 8601)                              |
| `status` / `isLive`         | `prematch` or `live`                                    |
| `periodsOffered`            | Which periods have markets (full match, first half...)  |
| `odds`                      | Tidy main markets — see below                           |
| `markets`                   | Every named line, including alternates                  |
| `totalMarkets`              | Number of market lines in the row                       |
| `scrapedAt`                 | ISO timestamp of collection                             |

**The `odds` object:**

```json
{
    "moneyline": {
        "match": { "home": 1.18, "away": 13.46, "draw": 8.25 },
        "firstHalf": { "home": 1.34, "away": 8.5, "draw": 4.9 }
    },
    "spread": {
        "match": { "handicap": -1.75, "home": 1.93, "away": 1.92 },
        "firstHalf": { "handicap": -0.75, "home": 1.9, "away": 1.9 }
    },
    "total": {
        "match": { "points": 3.25, "over": 1.96, "under": 1.9 },
        "firstHalf": { "points": 1.25, "over": 1.85, "under": 1.98 }
    },
    "teamTotal": {
        "match": { "points": 2.25, "home_over": 1.9, "home_under": 1.9, "away_over": 2.1, "away_under": 1.75 }
    }
}
```

### How to use Pinnacle Odds Scraper

![How Pinnacle Odds Scraper works](https://api.apify.com/v2/key-value-stores/kE36venAoVchGsE6b/records/pinnacle-odds-scraper--how-it-works.png)

1. Open the Actor.
2. Choose a **Feed** (pre-match / live / both) and one or more **Sports**. That is enough to run.
3. Optional — for specific competitions, add **League IDs** (for example `1980` for England - Premier League).
4. Optional — for one match, paste its **Match URL** from pinnacle.com.
5. Set **Max matches** and press **Start**.
6. Download the dataset as JSON, CSV, Excel, or XML, or use the API.

### Input Parameters

| Parameter            | Type    | Required | Default      | Description                                                                                              |
| -------------------- | ------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------ |
| `mode`               | String  | No       | `prematch`   | `prematch`, `live` (in-play), or `both`.                                                               |
| `sports`             | Array   | No       | `["Soccer"]` | Sport names (`Soccer`, `Tennis`, `Basketball`, `American Football`, `Baseball`, `Hockey`, `Esports`, `MMA`...) or numeric IDs. `["all"]` for every sport. Leave empty to use only league IDs / match URLs. |
| `leagueIds`          | Array   | No       | –            | Pinnacle numeric league IDs.                                                                          |
| `matchUrls`          | Array   | No       | –            | pinnacle.com match page URLs.                                                                         |
| `maxMatches`         | Integer | No       | 5            | Maximum matches per sport, league, or run. Raise for a full export (the form prefills 50).            |
| `includeMarkets`     | Boolean | No       | `true`       | Keep the full `markets` array (alternate lines included) next to the tidy `odds` object.              |
| `useCache`           | Boolean | No       | `false`      | Reuse match records from an earlier run.                                                              |
| `proxyConfiguration` | Object  | No       | No proxy     | Pinnacle odds are reachable without a proxy from most regions; enable one only if runs start failing. |

### Output Data

```json
{
    "matchId": 1634530078,
    "url": "https://www.pinnacle.com/en/soccer/england-premier-league/manchester-city-vs-coventry-city/1634530078/",
    "bookmaker": "Pinnacle",
    "sport": "Soccer",
    "sportId": 29,
    "league": "England - Premier League",
    "leagueId": 1980,
    "country": "England",
    "home": "Manchester City",
    "away": "Coventry City",
    "rotationNumber": 12345,
    "startTime": "2026-09-05T14:00:00.000Z",
    "status": "prematch",
    "isLive": false,
    "totalMarkets": 35,
    "odds": {
        "moneyline": { "match": { "home": 1.18, "away": 13.46, "draw": 8.25 } },
        "spread": { "match": { "handicap": -1.75, "home": 1.93, "away": 1.92 } },
        "total": { "match": { "points": 3.25, "over": 1.96, "under": 1.9 } }
    },
    "markets": [
        {
            "market": "moneyline",
            "period": "match",
            "isAlternate": false,
            "status": "open",
            "maxStake": 1200,
            "prices": [
                { "selection": "home", "points": null, "priceAmerican": -565, "priceDecimal": 1.18 },
                { "selection": "away", "points": null, "priceAmerican": 1146, "priceDecimal": 13.46 },
                { "selection": "draw", "points": null, "priceAmerican": 725, "priceDecimal": 8.25 }
            ]
        }
    ],
    "scrapedAt": "2026-09-05T09:00:00.000Z"
}
```

### Usage Examples

**Today's soccer prices**

- `mode`: `prematch`, `sports`: `["Soccer"]`, `maxMatches`: 200

**Live in-play odds across sports**

- `mode`: `live`, `sports`: `["Soccer", "Tennis", "Basketball"]`

**One competition**

- `sports`: `[]`, `leagueIds`: `["1980"]`

**One match, every line**

- `sports`: `[]`, `matchUrls`: a pinnacle.com match URL

### Tips for Best Results

- Pinnacle returns its full board per sport in one pass; raise `maxMatches` for a complete export.
- For closing-line value work, schedule the Actor near kick-off and again earlier, then diff the `odds`.
- American and decimal are both provided — pick whichever your model uses; convert to fractional downstream if needed.
- `leagueIds` are stable; find one by opening a competition on pinnacle.com and noting the number in its page URL.

### Known Limitations

- Odds move constantly; a row is a snapshot at collection time.
- Live markets are suspended around goals and other in-play events, so some prices can briefly be missing.
- Corner, card, booking and player-prop markets are out of scope — the row covers the match markets (moneyline, spread, total, team total) for the full match and first half.
- Pinnacle does not operate in every country; availability of the odds feed can vary by region.
- Some lower-tier competitions carry only moneyline, with no spread or total.

### Integrations

Connect results to Google Sheets, Make, Zapier, Slack, or your own systems via the Apify API and webhooks. Schedule runs to build an odds history.

### Export Formats

JSON, CSV, Excel, and XML from the Dataset tab or the Apify API.

### Frequently Asked Questions

#### How many matches can I collect?

`maxMatches` applies per sport and per league. Raise it for a full export; Pinnacle's whole board for a sport is usually a few hundred matches.

#### Can I target a specific competition or match?

Yes. Add Pinnacle **League IDs** for competitions, or paste **Match URLs** for individual matches. Set `sports` to empty if you want only those.

#### Why are some odds fields null or missing?

Not every match offers every market — lower-tier competitions may have moneyline only. Live markets can also be suspended momentarily around in-play events.

#### Does it cover live betting?

Yes. Set `mode` to `live` (or `both`). Live rows are marked `isLive: true`.

#### How are duplicates handled?

Matches are deduplicated by Pinnacle matchup ID within a run.

#### How fresh is the data?

Every run reads the live odds feed. Schedule the Actor to keep an odds history.

#### Does the cache affect repeated runs?

With **Use cache** on, a match already collected in an earlier run is reused instead of being requested again, which speeds up recurring runs but can return an older price. Leave it off when you need the current line.

### Support

Found a problem or want another market added? Open an issue on the Actor's **Issues** tab in the Apify Console.

### Legal / Responsible Use

You are responsible for how you use this Actor and the data it collects, including compliance with Pinnacle's Terms of Service and the gambling and data-protection laws that apply to you. This Actor collects publicly displayed betting odds for analysis; it does not place bets or access any account. Gambling is age-restricted and regulated, and Pinnacle is not licensed in every country — check your local rules before using odds data commercially. If you or someone you know has a gambling problem, seek support from a service such as BeGambleAware.

### Preview

![Pinnacle Odds Scraper input options](https://api.apify.com/v2/key-value-stores/kE36venAoVchGsE6b/records/pinnacle-odds-scraper--input.png)

# Actor input Schema

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

Which odds to collect: pre-match, live (in-play), or both.

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

Sports to collect, by name ("Soccer", "Tennis", "Basketball", "American Football", "Baseball", "Hockey", "Esports", "MMA", ...) or numeric Pinnacle sport ID. Use \["all"] for every sport that currently has fixtures. Leave empty when you only want the League IDs or Match URLs below.

## `leagueIds` (type: `array`):

Optional. Pinnacle numeric league IDs to target specific competitions (for example 1980 for England - Premier League). Collected in addition to the sports above.

## `matchUrls` (type: `array`):

Optional. Paste individual Pinnacle match page URLs, for example https://www.pinnacle.com/en/soccer/england-premier-league/manchester-city-vs-coventry-city/1634530078/ . Each returns every market for that match.

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

Maximum number of matches to collect per sport, league, or run. Raise this for a full export.

## `includeMarkets` (type: `boolean`):

Add a `markets` array with every line (including alternate handicaps and totals) on top of the tidy `odds` object. Turn off for smaller rows.

## `useCache` (type: `boolean`):

Reuse match records collected in an earlier run. Disabled by default so every run returns live odds.

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

Optional. Pinnacle odds are reachable without a proxy from most regions. Enable a proxy only if runs start failing in your location.

## Actor input object example

```json
{
  "mode": "prematch",
  "sports": [
    "Soccer"
  ],
  "leagueIds": [],
  "matchUrls": [],
  "maxMatches": 50,
  "includeMarkets": true,
  "useCache": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `matches` (type: `string`):

No description

# 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": [
        "Soccer"
    ],
    "maxMatches": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("mrdoe/pinnacle-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": ["Soccer"],
    "maxMatches": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("mrdoe/pinnacle-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": [
    "Soccer"
  ],
  "maxMatches": 50
}' |
apify call mrdoe/pinnacle-odds-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mrdoe/pinnacle-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/ZnLXyXP2UvNkB0idr/builds/lccOTWVXN8QCb9G00/openapi.json
