# Sports Odds Aggregator: Pinnacle + Bovada Odds Feed (`mrdoe/multi-book-sports-odds-feed`) Actor

One odds feed from Pinnacle and Bovada: moneyline, spreads and totals for soccer, NFL, NBA, MLB, NHL, tennis and MMA with no-vig fair odds, best-price flags and line movement. No API key.

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

## Pricing

from $9.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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

![Sports Odds Aggregator: Pinnacle + Bovada Odds Feed - scrape data with Apify](https://api.apify.com/v2/key-value-stores/kE36venAoVchGsE6b/records/multi-book-sports-odds-feed--hero.png)

### What does Sports Odds Aggregator: Pinnacle + Bovada Odds Feed do?

Sports Odds Aggregator pulls **pre-match and live betting odds from Pinnacle (the sharp reference book) and Bovada (the largest US offshore book)** into one clean, normalized table. Every row is one outcome of one market - moneyline, spread or total - for soccer, NFL and college football, NBA, MLB, NHL, tennis and MMA. The same match is **matched across both books automatically**, so you can compare prices, see which book is best, and get **no-vig fair odds** and **line movement** without any extra work. No API key and no account needed.

### Quick start (3 steps)

1. **Open the Actor** on Apify and click **Try for free**.
2. **Pick your sports and books** (the defaults - NFL/college football and basketball on both books - already work). Press **Start**.
3. **Download the odds** as JSON, CSV or Excel, connect them to Google Sheets, or call the Actor as a live API (see below).

### Where and how can you use it?

| Use case | Who it is for | How to do it |
|---|---|---|
| **Line shopping** | Bettors | Filter rows where `isBestPriceAcrossBooks` is true to always see the best price. |
| **Fair-value and +EV models** | Sharp bettors, quants | Use `noVigFairPrice` from Pinnacle as the benchmark and compare it to Bovada's price. |
| **Closing-line value (CLV) tracking** | Serious bettors | Schedule a run before kickoff and compare with the price you took. |
| **Line-movement alerts** | Tipsters, Discord/Telegram bots | Schedule runs every 5-15 minutes and read `moveDirection`, `priceChange`, `firstSeenPrice`. |
| **Arbitrage research** | Arb hunters | Join both books on `fixtureId` and check the implied probabilities. |
| **Odds widgets and betting content** | Publishers, app builders | Call the live API and render the best prices per match. |
| **Datasets and backtesting** | Analysts, students | Schedule daily runs and build your own odds history. |
| **AI agents and chatbots** | Developers using LLMs | Let an assistant answer "who has the best price on tonight's game?" with fresh data. |

**Works with:** Google Sheets, Excel, Make, Zapier, n8n, Airtable, Slack, Discord, Telegram, webhooks, Python, JavaScript, cURL and any AI agent that can call an HTTP API.

### What data do you get?

![Sports Odds Aggregator: Pinnacle + Bovada Odds Feed data fields](https://api.apify.com/v2/key-value-stores/kE36venAoVchGsE6b/records/multi-book-sports-odds-feed--fields.png)

![Sports Odds Aggregator: Pinnacle + Bovada Odds Feed output data](https://api.apify.com/v2/key-value-stores/kE36venAoVchGsE6b/records/multi-book-sports-odds-feed--output.png)

Each row has:

| Field | Meaning |
|---|---|
| `book`, `sport`, `league` | Bookmaker, sport and competition |
| `homeTeam`, `awayTeam`, `commenceTime`, `isLive` | The match |
| `fixtureId`, `booksOnFixture` | The same match across books - join on `fixtureId` |
| `marketType`, `marketLine`, `outcomeKey`, `outcomeLabel` | h2h / spreads / totals, the line, and the outcome (home, away, draw, over, under) |
| `price`, `priceAmerican` | Decimal and American odds |
| `impliedProbability`, `overround` | Raw probability and the book's margin on that market |
| `noVigProb`, `noVigFairPrice` | Margin-free probability and fair odds |
| `isBestPriceAcrossBooks` | True for the highest price when at least two books quote the same outcome and line |
| `previousPrice`, `priceChange`, `priceChangePct`, `moveDirection`, `firstSeenPrice`, `moveSinceFirstSeen` | Line movement versus your last run (from the second run onward) |
| `scrapedAt` | When the row was captured |

### Example result

```json
{
  "book": "pinnacle",
  "sport": "basketball",
  "league": "Philippines - NCAA",
  "homeTeam": "Arellano Chiefs",
  "awayTeam": "UPHSD Altas",
  "commenceTime": "2026-09-25T07:00:00Z",
  "isLive": false,
  "marketType": "spreads",
  "marketLine": -2.5,
  "outcomeKey": "home",
  "outcomeLabel": "Arellano Chiefs -2.5",
  "price": 1.833,
  "priceAmerican": -120,
  "impliedProbability": 0.545554,
  "overround": 0.091107,
  "noVigProb": 0.5,
  "noVigFairPrice": 2,
  "isBestPriceAcrossBooks": false,
  "fixtureId": "basketball-pinnacle-1636906808"
}
```

### Input parameters

![Sports Odds Aggregator: Pinnacle + Bovada Odds Feed input options](https://api.apify.com/v2/key-value-stores/kE36venAoVchGsE6b/records/multi-book-sports-odds-feed--input.png)

| Parameter | Type | Default | What it does |
|---|---|---|---|
| `sports` | array | `["american_football","basketball"]` | soccer, basketball, american\_football, baseball, hockey, tennis, mma |
| `books` | array | `["pinnacle","bovada"]` | Which bookmakers to include (Bovada has no MMA) |
| `marketTypes` | array | `["h2h","spreads","totals"]` | Moneyline, handicap, over/under |
| `mode` | string | `pre_match_only` | `pre_match_only`, `live_only` or `pre_match_and_live` |
| `leagueFilter` | array | - | Keep leagues whose name contains a word, e.g. `NFL`, `NCAA`, `Premier League` |
| `maxEventsPerSport` | integer | 5 | Matches per sport. Matches on both books come first, then the soonest. 0 = all |
| `includeFairOdds` | boolean | true | Adds implied probability, margin and no-vig fair odds |
| `includeBestPriceFlag` | boolean | true | Flags the best price across books |
| `includeLineMovement` | boolean | true | Compares with the previous run |

### Use it as a live API (Actor Standby)

Need an answer right now instead of starting a run? This Actor also works as an **always-on HTTP endpoint** (Apify Actor Standby). Send a request, get JSON back in seconds.

```bash
curl -H "Authorization: Bearer $APIFY_TOKEN" \
  "https://mrdoe--multi-book-sports-odds-feed.apify.actor/?sports=basketball,american_football&books=pinnacle,bovada&maxEventsPerSport=5"
```

```python
import requests
r = requests.get(
    "https://mrdoe--multi-book-sports-odds-feed.apify.actor/",
    params={"sports": "soccer", "leagueFilter": "Premier League", "maxEventsPerSport": 5},
    headers={"Authorization": "Bearer YOUR_APIFY_TOKEN"},
    timeout=90,
)
print(r.json()["items"])
```

```javascript
const res = await fetch("https://mrdoe--multi-book-sports-odds-feed.apify.actor/?sports=tennis&mode=pre_match_and_live", {
  headers: { Authorization: `Bearer ${process.env.APIFY_TOKEN}` },
});
const { count, items } = await res.json();
```

You can also `POST` a JSON body with the same fields as the Actor input. The response is `{ "count": 12, "items": [ ... ] }`. Line movement needs stored history, so movement fields are added on normal (scheduled) runs, not on live API calls.

| | Standby API | Normal run |
|---|---|---|
| Best for | Apps, bots, chat assistants, quick checks | Scheduled polling, exports, history |
| Output | JSON in the response | Dataset (JSON, CSV, Excel) |
| Line movement | Not included | Included from the second run |

### Schedule it

Save your settings as a **Task**, then add a **Schedule** in Apify Console: every 5-15 minutes for line movement, hourly for dashboards, daily for a history dataset. Add a webhook to send fresh rows to Discord, Slack, Sheets or your own system.

### Tips for best results

- Use **both books** and keep `includeBestPriceFlag` on - matches covered by both books are returned first.
- Use `leagueFilter` to focus, for example `["NFL"]`, `["NCAA"]` or `["Premier League"]`.
- For soccer, set `maxEventsPerSport` to control how many matches you pull.
- For line movement, run on a schedule with the same settings each time.

### Known limitations

- Pre-match and live **moneyline, spread and total** markets only; player props and specials are not included.
- Only two books (Pinnacle and Bovada) for now. Bovada does not list MMA.
- Books can pause or change prices at any moment; each row is a snapshot at `scrapedAt`.
- Team names are matched across books automatically; very unusual name differences may not match, in which case each book's rows simply appear separately.

### Frequently Asked Questions

#### Do I need an API key or a bookmaker account?

No. There is nothing to sign up for or configure beyond the input.

#### Which sports and markets are covered?

Soccer, NFL and college football, NBA and other basketball, MLB, NHL, tennis and MMA - moneyline (1X2 in soccer), spreads and totals.

#### What are no-vig fair odds?

The bookmaker's margin removed from the market so the probabilities add up to 100%. Pinnacle's no-vig price is widely used as a fair-value benchmark.

#### How do I find the best price?

![How the Sports Odds Aggregator: Pinnacle + Bovada Odds Feed works](https://api.apify.com/v2/key-value-stores/kE36venAoVchGsE6b/records/multi-book-sports-odds-feed--how-it-works.png)
Filter rows where `isBestPriceAcrossBooks` is true. It is set only when at least two books quote the same outcome and line.

#### Can I use it as an API?

Yes - see **Use it as a live API** above. It works with Python, JavaScript, cURL, Make, Zapier, n8n and AI agents.

#### Can I run it on a schedule?

Yes. Create a Task with your input and add a schedule in Apify Console.

#### Am I charged for failed runs or empty results?

You are charged only for rows actually returned.

### Support

Questions or a missing field? Open an issue from the **Issues** tab on this Actor's page.

### Legal / Responsible Use

Odds data is provided for personal analysis and research. Check the rules for sports betting and data use in your jurisdiction, and do not redistribute the data commercially without the right licences. Gamble responsibly.

# Actor input Schema

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

Which sports to fetch. Options: soccer, basketball, american\_football, baseball, hockey, tennis, mma.

## `books` (type: `array`):

Pinnacle is the sharp reference book; Bovada is the largest US offshore book. Use both to compare prices and flag the best one.

## `marketTypes` (type: `array`):

h2h = moneyline (1X2 in soccer), spreads = handicap, totals = over/under.

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

Pre-match only, live only, or both.

## `leagueFilter` (type: `array`):

Keep only leagues whose name contains one of these words, e.g. NFL, NCAA, Premier League, ATP.

## `maxEventsPerSport` (type: `integer`):

Matches on both books come first, then the soonest to start. 0 = all available.

## `includeFairOdds` (type: `boolean`):

Adds implied probability, the market margin and margin-free fair odds to every row.

## `includeBestPriceFlag` (type: `boolean`):

Marks the highest price when at least two books quote the same outcome and line.

## `includeLineMovement` (type: `boolean`):

Compares each price with the last run's price for the same outcome. Movement fields appear from the second run.

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

Optional. Datacenter proxy is enough; leave off unless you see blocked responses.

## Actor input object example

```json
{
  "sports": [
    "american_football",
    "basketball"
  ],
  "books": [
    "pinnacle",
    "bovada"
  ],
  "marketTypes": [
    "h2h",
    "spreads",
    "totals"
  ],
  "mode": "pre_match_only",
  "leagueFilter": [],
  "maxEventsPerSport": 5,
  "includeFairOdds": true,
  "includeBestPriceFlag": true,
  "includeLineMovement": true,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (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": [
        "american_football",
        "basketball"
    ],
    "books": [
        "pinnacle",
        "bovada"
    ],
    "marketTypes": [
        "h2h",
        "spreads",
        "totals"
    ],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("mrdoe/multi-book-sports-odds-feed").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": [
        "american_football",
        "basketball",
    ],
    "books": [
        "pinnacle",
        "bovada",
    ],
    "marketTypes": [
        "h2h",
        "spreads",
        "totals",
    ],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("mrdoe/multi-book-sports-odds-feed").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": [
    "american_football",
    "basketball"
  ],
  "books": [
    "pinnacle",
    "bovada"
  ],
  "marketTypes": [
    "h2h",
    "spreads",
    "totals"
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call mrdoe/multi-book-sports-odds-feed --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mrdoe/multi-book-sports-odds-feed"
        }
    }
}
```

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/XkfNjziK6C0UwQyga/builds/xwficb5YOPL7wFYum/openapi.json
