# Sportsbook Odds Tracker: Moneyline, Spread and Totals (`scrapemint/sportsbook-odds-tracker`) Actor

Live and pregame odds from a sportsbook's own public feed: moneyline, spread and totals for football, basketball, baseball, hockey, soccer, tennis and more. Every row carries the price, the line, the implied probability and the book's hold. No API key, no account. Lists every league it prices.

- **URL**: https://apify.com/scrapemint/sportsbook-odds-tracker.md
- **Developed by:** [Ken M](https://apify.com/scrapemint) (community)
- **Categories:** Business, News
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$4.00 / 1,000 odds rows

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#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

## Sportsbook Odds Tracker: Moneyline, Spread and Totals

Live prices straight from a sportsbook's own feed, for every league it prices: American football, soccer, basketball, tennis, baseball, hockey, esports and more. No API key, no account, no proxy.

Every row carries the price, the line it is attached to, what that price implies as a probability, and what the book is charging for it.

### Three modes

**Odds.** One row per market per event. Moneyline, spread and total, priced in American, decimal and fractional odds, with the handicap or total attached to each outcome, the start time, and whether the event is already in play.

**Movement.** The reason to schedule this. The first run records a baseline and charges nothing. Every run after that returns only the markets whose price or line actually changed, with the direction, the size of the move in probability points, and both the old and new numbers side by side.

**Leagues.** The directory of competitions with a live event count on each, which is where the league paths for the other two modes come from. Soccer alone runs to over a thousand priced events across Europe, South America, Asia, Africa and North America.

### What is derived rather than copied

A book publishes a price. It does not publish what the price implies or what it is charging for it. Each market row adds:

- `impliedProbabilityPercent` per outcome, from decimal odds
- `holdPercent`, the overround, which is the book's margin on that market
- `fairProbabilityPercent` and `fairDecimalOdds`, the same price with the margin taken back out, which is the number worth comparing against your own estimate

The hold is computed across the outcomes actually priced, so a three way soccer market with a draw goes through the same arithmetic as a two way moneyline. If a leg is missing or suspended, `holdMeasured` is false and no hold is published, because a market with a missing leg would otherwise report a margin well below what the book is really charging.

### Things worth knowing about the source

- **Suspended markets keep their last price.** The feed marks them, and this actor drops them by default. Left in, a frozen price reads as a live quote. Set `includeSuspended` if you want them, and read `marketStatus`.
- **An even money price arrives as the word `EVEN`, not `+100`.** Roughly one price in twenty in a soccer pull. Prices here are read from the decimal odds, which are always present, so `EVEN` becomes 100 rather than nothing.
- **Split lines are common.** A soccer handicap is often quoted as two numbers, such as -0.5 and -1.0, and the stake is settled against both. Both ship, as `handicap` and `handicapSecondary`, with `isSplitLine` set.
- **The same bet is named differently per sport.** "Moneyline Game" in the NFL is "3-Way Moneyline" in soccer, "Point Spread Game" is "Goal Spread". Every row carries the raw `marketName` and a stable `marketType` you can filter on.
- **Half and period lines are different bets** from the full game line and are excluded by default.
- **This is the headline game lines, not props.** Each row reports `marketsPricedByBook` next to `marketsInThisFeed` so the gap is visible rather than looking like missing data. A typical soccer event prices over a hundred markets; this feed carries three.
- **A league out of season answers with an empty feed**, not an error. That comes back as a free note, not a silent zero.

### One book, not a consensus

This reads one sportsbook. It is a real book's real prices, which is what a consensus number is built out of, but it is not a market average and it is not line shopping across several books on its own. Prices, availability and the leagues on offer depend on the book.

### Billing

Pay per event. `odds_row` at $0.004 covers one market, one moved market, or one league in the directory. The first 2 rows of every run are free. Notes and diagnostics are never charged. A baseline run in movement mode charges nothing.

A default run of 50 rows costs $0.20 and takes a few seconds.

# Actor input Schema

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

odds = current prices for the leagues you choose. movement = only the markets whose price or line changed since your last run, which is what makes this worth scheduling. leagues = the directory of sports and competitions with live event counts, which is where the league paths come from.

## `leaguePaths` (type: `array`):

Used by odds and movement mode. Examples: football/nfl, basketball/nba, soccer/europe, tennis. A sport on its own returns every competition inside it, which can run to a thousand events, so keep the row cap in mind.

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

Which sportsbooks to price. Pinnacle is the sharp book: it runs on low margin and high limits, so its vig-free price is the closest thing to a market consensus. Bovada is the recreational book. Reading both is what makes a price difference meaningful. Pinnacle covers the major leagues only; an unmapped league path is skipped on that book rather than guessed at.

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

Leagues mode only. Which sports to list competitions for. Examples: soccer, football, basketball, tennis, baseball, hockey, esports.

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

Empty returns every headline market. Values: moneyline, moneyline\_3way (soccer and other sports that price the draw), spread, total, other.

## `liveOnly` (type: `boolean`):

Return only events that have already started and are being priced live.

## `includeSuspended` (type: `boolean`):

Off by default. A suspended market keeps its last price in the feed even though no bet can be taken on it, so leaving these in makes a frozen price look like a live quote.

## `includeNonMainPeriods` (type: `boolean`):

Off by default. First half, second half and similar lines are different bets from the full game line and are easy to mistake for it.

## `minMovePoints` (type: `integer`):

Movement mode. Only return markets where implied probability moved at least this many percentage points. 0 returns every change.

## `maxRows` (type: `integer`):

One row is one market on one event. The first 2 rows of every run are free.

## Actor input object example

```json
{
  "mode": "odds",
  "leaguePaths": [
    "football/nfl"
  ],
  "books": [
    "bovada",
    "pinnacle"
  ],
  "sports": [
    "soccer"
  ],
  "marketTypes": [],
  "liveOnly": false,
  "includeSuspended": false,
  "includeNonMainPeriods": false,
  "minMovePoints": 0,
  "maxRows": 50
}
```

# 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 = {
    "leaguePaths": [
        "football/nfl"
    ],
    "books": [
        "bovada",
        "pinnacle"
    ],
    "sports": [
        "soccer"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapemint/sportsbook-odds-tracker").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 = {
    "leaguePaths": ["football/nfl"],
    "books": [
        "bovada",
        "pinnacle",
    ],
    "sports": ["soccer"],
}

# Run the Actor and wait for it to finish
run = client.actor("scrapemint/sportsbook-odds-tracker").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 '{
  "leaguePaths": [
    "football/nfl"
  ],
  "books": [
    "bovada",
    "pinnacle"
  ],
  "sports": [
    "soccer"
  ]
}' |
apify call scrapemint/sportsbook-odds-tracker --silent --output-dataset

```

## MCP server setup

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

```

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/mfdweh6kPOLCyaJ6B/builds/90oSxDDLSMHHWPMwk/openapi.json
