# Sports Odds & Line Movement (`nortonw3/sports-odds-lines`) Actor

Pregame moneyline, spread and total across MLB, NFL, NCAAF, NBA, WNBA and NHL in one normalized schema — with opening lines, so you can see how the market moved.

- **URL**: https://apify.com/nortonw3/sports-odds-lines.md
- **Developed by:** [Closing Line](https://apify.com/nortonw3) (community)
- **Categories:** Automation, Agents, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.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

## Sports Odds & Line Movement

Pregame **moneyline, spread and total** for MLB, NFL, NCAA Football, NBA, WNBA and NHL —
one normalized schema across every sport, with **opening lines included** so you can see
how the market moved.

No API key. No proxy. No browser. One row per game.

### Why this one

**Most odds scrapers give you the current price. This one gives you the current price and
the opening price**, on every market, with a `lineMoved` flag. Line movement is the part
that tells you something; a closing number on its own does not.

| | This actor | Typical alternatives |
|---|---|---|
| Opening lines + movement flag | ✅ every market | ❌ closing price only |
| Sports in one schema | 6 leagues, identical fields | often one sport per actor |
| Moneyline, spread and total | ✅ all three | usually all three |
| American **and** decimal odds | ✅ | usually American only |
| Out-of-season league | free explanatory row | silent empty result |
| Billing | **per row delivered** | some bill per run attempted |
| Spend cap | ✅ `maxRows` | rare |
| API key required | ❌ none | varies |

### Output

One row per game:

```json
{
  "league": "MLB",
  "eventName": "Baltimore Orioles at Tampa Bay Rays",
  "startTime": "2026-08-17T22:05Z",
  "book": "DraftKings",
  "homeTeam": "Tampa Bay Rays",  "awayTeam": "Baltimore Orioles",
  "moneylineHome": "-163",       "moneylineAway": "+135",
  "spread": -1.5,
  "spreadHomeLine": "-1.5",      "spreadHomeOdds": "+135",
  "overUnder": 8.0,
  "overOdds": "-102",            "underOdds": "-118",
  "openMoneylineHome": "-162",   "openMoneylineAway": "+134",
  "openOverOdds": "+100",        "openUnderOdds": "-120",
  "lineMoved": true,
  "oddsFormat": "american",
  "scrapedAt": "2026-08-17T02:38:47+00:00"
}
```

### Input

| Field | Default | Notes |
|---|---|---|
| `leagues` | all six | `mlb`, `nfl`, `ncaaf`, `nba`, `wnba`, `ncaab`, `nhl` |
| `daysAhead` | 3 | Days forward to scan, on top of the current slate. Max 14. |
| `oddsFormat` | `american` | or `decimal` (`-163` → `1.6135`) |
| `includeStartedEvents` | `false` | Keeps in-progress games; prices are pregame closes |
| `maxRows` | 0 | Hard cap on billable rows. 0 = uncapped |

### Pricing

Pay per **odds row delivered**. Error and explanatory rows are free. If a run returns
nothing, you are charged nothing — billing is never per attempted run.

`maxRows` caps spend on a large slate: a full NCAA Football Saturday is ~100 games.

### Frequently asked questions

**Where do the odds come from?**
ESPN's public scoreboard feed, which carries one sportsbook's pregame lines per game —
currently DraftKings. Keyless, no proxy, no browser.

**Why not read the sportsbooks directly?**
They block automated access to their own endpoints. Public feeds are reachable and
stable; scraping books is neither.

**How far ahead are lines available?**
Books post roughly 24 hours out for daily sports. Weekly sports (NFL, NCAAF) are posted
for the whole upcoming slate, often two weeks ahead — the actor picks these up
automatically via the current slate, regardless of `daysAhead`.

**I ran it and got no rows.**
Most likely nothing is posted yet, or the leagues you picked are out of season. You get
a free `NO_ODDS_AVAILABLE` row explaining which, rather than an empty result you have to
guess at. You are not charged.

**Do you have live/in-play odds or player props?**
No. Pregame moneyline, spread and total only.

**Multiple books per game?**
Not yet — currently one book per event, whichever ESPN carries.

**Why do some games have no opening line?**
ESPN does not always publish an open for every market. Those fields come back `null` and
`lineMoved` reflects only the markets it could compare.

### Error codes

Every failure is a dataset row you can filter on, never a crashed run. A bad league or a
brief upstream outage never takes down the rest of the batch.

| Code | Meaning |
|---|---|
| `NO_ODDS_AVAILABLE` | Nothing posted for the requested leagues and window |
| `LEAGUE_NOT_SUPPORTED` | Unrecognised league key; supported list included in the row |
| `UPSTREAM_UNAVAILABLE` | ESPN unreachable or returned an error for one league/date |

### Notes on the data

Odds are published by sportsbooks and surfaced through a public feed; they are facts
about prices, not personal data. This actor collects no personal information.

Prices change constantly — a row is a snapshot at `scrapedAt`, not a quote. Nothing here
is betting advice.

# Actor input Schema

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

Which leagues to pull lines for. Out-of-season leagues simply return no rows — they are never an error.

## `daysAhead` (type: `integer`):

How many days forward to scan, in addition to the current slate. Sportsbooks post lines roughly 24h out, so 2-3 is usually enough. Max 14.

## `oddsFormat` (type: `string`):

American (-163) or decimal (1.6135).

## `includeStartedEvents` (type: `boolean`):

Off by default. When on, keeps games that have started; their prices are the pregame closes.

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

Hard cap on billable rows for this run. 0 means no cap. Use it to bound spend on a large slate.

## Actor input object example

```json
{
  "leagues": [
    "mlb",
    "nfl",
    "ncaaf",
    "nba",
    "wnba",
    "nhl"
  ],
  "daysAhead": 3,
  "oddsFormat": "american",
  "includeStartedEvents": false,
  "maxRows": 0
}
```

# Actor output Schema

## `odds` (type: `string`):

One row per game: moneyline, spread and total, plus the opening lines and a lineMoved flag. Error rows carry an errorCode and are never billed.

## `oddsCsv` (type: `string`):

The same rows as CSV, for spreadsheets and model training pipelines.

## `runLog` (type: `string`):

Which leagues and dates were scanned, and any upstream failures.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("nortonw3/sports-odds-lines").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("nortonw3/sports-odds-lines").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 '{}' |
apify call nortonw3/sports-odds-lines --silent --output-dataset

```

## MCP server setup

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

```

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/Qeu2oRfP52h2zj23B/builds/pf8rPuizif516Ys2G/openapi.json
