# Sports Odds Aggregator — Multi-Book Lines & Line-Shop (`brilliant_gum/sports-odds-aggregator`) Actor

Live sports betting odds aggregated across DraftKings, Bovada, Pinnacle & FanDuel (no login/key): moneyline, spread, totals & player props for NFL, NBA, WNBA, MLB, NHL, college & soccer. Best-line line-shopping, implied & no-vig fair probabilities, and arbitrage detection.

- **URL**: https://apify.com/brilliant\_gum/sports-odds-aggregator.md
- **Developed by:** [Yuliia Kulakova](https://apify.com/brilliant_gum) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.015 / game odds record

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 — 4-book lines with no-vig, best-line, arbitrage & line-movement

**4-book lines (DraftKings, FanDuel, Bovada, Pinnacle) with no-vig, best-line, arbitrage & line-movement — no login.**

![Sports Odds Aggregator](https://api.apify.com/v2/key-value-stores/IuFu0Cw6PpOkNkDV7/records/sports-odds-aggregator-banner.png)

> **Enjoying this Actor?** If it's useful, please leave a ⭐️ rating and a short review — it genuinely helps. **Something not working right?** Open an issue on the Actor's **Issues** tab — I actively maintain this Actor and respond fast to fix problems and help you get things working.

Pull **moneyline, spread and total** plus **player props** for the same game across four independent
sportsbooks, side by side, so you can **line-shop for the best price**, see **de-vigged fair
probabilities**, and catch **arbitrage** — then track how those lines move over time.

Built for **bettors** doing line-shopping and value scanning, **odds / analytics products** that need
a clean multi-book feed, and **model builders** who want fair (no-vig) probabilities and a sharp
reference price (Pinnacle) as inputs.

This is a **deep analytics aggregator across four real books** — not a "one row per book" dump and not
a claim to carry every book on the market. It carries the four books that are genuinely reachable
without a login or paid API, and does the math on top of them properly. Read the
[Honest limitations](#honest-limitations) section before you buy — book count and live odds are
called out plainly.

**No login. No paid third-party odds API.** DraftKings, Bovada and Pinnacle are **fully keyless and
work with no proxy**. FanDuel needs a **US Apify proxy** (a US datacenter proxy is enough) and is
emitted as `null` with a warning when one isn't configured.

***

### Key features

- **Four books, side by side per game** — **DraftKings** (opening + closing lines), **FanDuel** (US
  proxy required), **Bovada** (largest US-facing offshore book), and
  **Pinnacle** — the low-margin *sharp* reference book used for fair-value anchoring.
- **Every price in three formats** — **American**, **decimal** *and* **fractional** are emitted on
  every outcome (`american`, `decimal`, `fractional`), plus `impliedProbability`. `oddsFormat` sets
  your display preference; all three are always present.
- **No-vig fair probabilities** — de-vigged probabilities per book and a consensus, with the book
  margin (`overroundPct`). Pinnacle's low overround makes it a clean fair-value anchor.
- **Best line per outcome** — the highest-payout book and price for each side (`analytics.*.best`),
  with `isBestPrice` flags in flat/rows output. This is the core of line-shopping.
- **Arbitrage detection** — risk-free arb across books, **2-way** (moneyline / total) and **3-way**
  (soccer home / draw / away), with `roiPct`, `invSum` and the `stake` split per outcome.
- **Alternate-line ladders** — full alternate spread/total ladders from Pinnacle under each book's
  `alternateLines` when `includeAlternateLines` is on (other books expose only the main line).
- **Line-movement monitoring** — with `monitoringMode`, the actor persists last-seen prices in a named
  key-value store and attaches `lineMovement[]` (`previousPrice`, `priceChange`, `moveDirection`,
  `firstSeenPrice`) across scheduled runs — plus DraftKings **opening vs closing** lines.
- **Player props** (Bovada) — over/under lines (passing / rushing / receiving / pitcher / batter, etc.),
  yes/no markets, and **outright list markets** such as **Anytime Touchdown Scorer** and First TD Scorer.
- **Edge-threshold filters** — `minArbPct` and `minBestEdgePct` emit only games with a real arbitrage
  ROI or best-line edge vs the no-vig fair price.
- **15+ leagues** — NFL, NCAAF, NBA, WNBA, NCAAB, NCAAW, MLB, NHL, MMA, and top soccer (EPL, UEFA
  Champions League, UEFA Europa League, MLS, La Liga, Serie A, Bundesliga, Ligue 1).

***

### Modes

| Mode | What you get |
|------|--------------|
| `games` (default) | One record per upcoming game with every book's moneyline / spread / total side by side + best-line, no-vig and arbitrage analytics. |
| `event` | The same rich record for a single game by `eventId` (from `games` output; optionally with `playerProps` attached). |
| `player-props` | One record per player per stat market (Bovada): line, over/under (or yes/no, or outright) prices, and book margin. |
| `best-line` | Line-shopping view: per market/outcome, the best book & price, the full field, edge vs consensus, and the no-vig fair probability. |

`outputMode: "events"` (default) keeps the nested per-game shape. `outputMode: "rows"` flattens
games / event / best-line into one row per book × market × outcome (spreadsheet-friendly).

***

### Supported sports / leagues

Keys follow the-odds-api convention; short aliases (`nba`, `nfl`, `mlb`, `nhl`, `epl`, `ncaaw`, `uel`, …)
also work.

`americanfootball_nfl`, `americanfootball_ncaaf`, `basketball_nba`, `basketball_wnba`,
`basketball_ncaab`, `basketball_ncaaw`, `baseball_mlb`, `icehockey_nhl`, `mma_mixed_martial_arts`,
`soccer_epl`, `soccer_uefa_champs_league`, `soccer_uefa_europa_league`, `soccer_usa_mls`,
`soccer_spain_la_liga`, `soccer_italy_serie_a`, `soccer_germany_bundesliga`, `soccer_france_ligue_one`.

Soccer moneyline is **3-way** (home / draw / away) and arbitrage requires all three legs. Books are
matched to the game schedule by normalized team names + date; a book that hasn't posted a game or
market is returned as `null` — never faked.

***

### Input

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `mode` | string enum | `games` | `games` / `event` / `player-props` / `best-line`. |
| `sport` | string | `""` | Convenience single sport key (merged into `sports`). |
| `sports` | array | `[]` | One or more sport/league keys. Empty + empty `sport` → `baseball_mlb`. |
| `eventId` | string | `""` | Event id (the `eventId` from `games` output) for `event` mode. |
| `eventIds` | array | `[]` | Multiple event ids for `event` mode. |
| `books` | array | `[]` | Restrict to `draftkings` / `bovada` / `pinnacle` / `fanduel`. Empty = all available. |
| `markets` | array | `[]` | Restrict to `moneyline` / `spread` / `total`. Empty = all. |
| `oddsFormat` | string enum | `american` | Display preference: `american` / `decimal` / `fractional`. All three are always emitted. |
| `outputMode` | string enum | `events` | `events` (nested per game) or `rows` (flat, one row per outcome). |
| `lookAheadDays` | integer | `3` | Days ahead to include games (1–60). Auto-extends up to 60 days if the near window is empty (e.g. off-season). |
| `includeStartedEvents` | boolean | `false` | Include in-progress / finished games (with live/final scores). |
| `dateFrom` | string | `""` | `YYYY-MM-DD` lower bound (UTC). Overrides look-ahead when set. |
| `dateTo` | string | `""` | `YYYY-MM-DD` upper bound (UTC). |
| `players` | array | `[]` | `player-props`: keep only players whose name contains any of these. |
| `statContains` | array | `[]` | `player-props`: keep only stats containing any of these (e.g. `passing`, `rushing`). |
| `maxPropsPerEvent` | integer | `200` | Cap props read per game. |
| `includePlayerProps` | boolean | `false` | `event` mode: also attach the game's Bovada props under `playerProps[]`. |
| `computeBestLine` | boolean | `true` | Best (highest-payout) price per outcome + `isBestPrice` flags. |
| `computeNoVig` | boolean | `true` | De-vigged fair probabilities and the book margin per market. |
| `computeArbitrage` | boolean | `true` | Detect risk-free arbitrage across books (ROI% + stake split). |
| `includeOpeningLines` | boolean | `true` | Include DraftKings opening lines alongside closing. |
| `includeAlternateLines` | boolean | `false` | Attach full alternate spread/total ladders (Pinnacle) under each book's `alternateLines`. |
| `monitoringMode` | boolean | `false` | Track line movement across scheduled runs (named KV store) and attach `lineMovement[]`. |
| `minArbPct` | integer | `0` | Only emit games whose best cross-book arbitrage ROI ≥ this % (0 = off). |
| `minBestEdgePct` | integer | `0` | Only emit games with a best-line edge vs the no-vig fair price ≥ this % (0 = off). |
| `maxItems` | integer | `50` | Max output records for the whole run. |
| `maxEventsPerSport` | integer | `0` | Cap games per sport (0 = no cap). |
| `pinnacleApiKey` | string | `""` | Optional override of Pinnacle's built-in data key. |
| `fanduelApiKey` | string | `""` | Optional override of FanDuel's built-in data key. |
| `proxyConfiguration` | object | `{ "useApifyProxy": false }` | Optional for DK/Bovada/Pinnacle; **required (US) for FanDuel**. A US datacenter proxy suffices. |

#### Example inputs

Games for one sport (keyless — 3 books; FanDuel needs a US proxy):

```json
{ "mode": "games", "sport": "baseball_mlb", "maxItems": 10 }
```

Single event with player props attached:

```json
{ "mode": "event", "sport": "americanfootball_nfl", "eventId": "401816790", "includePlayerProps": true }
```

Line-shopping / best-line across two sports:

```json
{ "mode": "best-line", "sports": ["americanfootball_nfl", "basketball_nba"], "markets": ["moneyline", "total"], "maxItems": 100 }
```

All four books (FanDuel via US proxy) with alternate ladders and line-movement monitoring:

```json
{
  "mode": "games",
  "sport": "americanfootball_nfl",
  "includeAlternateLines": true,
  "monitoringMode": true,
  "proxyConfiguration": { "useApifyProxy": true, "apifyProxyCountry": "US" }
}
```

***

### Output

#### Game record (`games` mode, `events` output — trimmed, real values)

```json
{
  "recordType": "game",
  "sport": "baseball_mlb",
  "league": "MLB",
  "eventId": "401816790",
  "eventName": "Toronto Blue Jays at Cleveland Guardians",
  "startTime": "2026-09-03T17:10:00Z",
  "startDate": "2026-09-03",
  "homeTeam": { "displayName": "Cleveland Guardians", "abbreviation": "CLE" },
  "awayTeam": { "displayName": "Toronto Blue Jays", "abbreviation": "TOR" },
  "bookmakers": ["draftkings", "bovada", "fanduel", "pinnacle"],
  "bookCount": 4,
  "books": {
    "draftkings": {
      "moneyline": {
        "home": { "american": -107, "decimal": 1.9346, "fractional": "100/107", "impliedProbability": 0.5169 },
        "away": { "american": -112, "decimal": 1.8929, "fractional": "25/28", "impliedProbability": 0.5283 }
      },
      "total": { "line": 8, "over": { "american": -110 }, "under": { "american": -110 } },
      "lastUpdated": "2026-09-03T09:19:24Z"
    },
    "bovada":   { "moneyline": { "home": { "american": -110 }, "away": { "american": -110 } } },
    "fanduel":  { "moneyline": { "home": { "american": -108 }, "away": { "american": -108 } } },
    "pinnacle": { "moneyline": { "home": { "american": -105 }, "away": { "american": -105 } } }
  },
  "analytics": {
    "moneyline": {
      "best": {
        "home": { "book": "pinnacle", "american": -105, "decimal": 1.9524 },
        "away": { "book": "pinnacle", "american": -105, "decimal": 1.9524 }
      },
      "consensusImplied": { "home": 0.5169, "away": 0.5283 },
      "noVig": {
        "draftkings": { "home": 0.4945, "away": 0.5055, "overroundPct": 4.52 },
        "pinnacle":   { "home": 0.5,    "away": 0.5,    "overroundPct": 2.44 }
      },
      "arbitrage": { "exists": false, "invSum": 1.0244 }
    }
  },
  "scrapedAt": "2026-09-03T09:19:25Z"
}
```

When `includeAlternateLines` is on, Pinnacle's entry also carries
`"alternateLines": { "spread": [{ "line": 1, "isAlternate": true, "home": {…}, "away": {…} }], "total": [{ "line": 6.5, "isAlternate": true, "over": {…}, "under": {…} }] }`.
When `monitoringMode` is on, each game record also carries a `lineMovement[]` array (see fields below).

#### Player-prop record (`player-props` mode — field shape)

Props come from Bovada. `propType` is one of `over-under`, `yes-no` or `outright`; the price legs vary
by type (`over`/`under`, `yes`/`no`, or a single `price`). Field names below are exact; the examples
illustrate the record shape.

```json
{
  "recordType": "player-prop",
  "sport": "americanfootball_nfl",
  "league": "NFL",
  "eventId": "401816790",
  "eventName": "…",
  "startTime": "…",
  "book": "bovada",
  "player": "Drake Maye",
  "team": "NE",
  "statGroup": "Passing Props",
  "statType": "Passing Yards",
  "propType": "over-under",
  "line": 235.5,
  "over":  { "american": -115, "decimal": 1.8696, "fractional": "20/23", "impliedProbability": 0.5349 },
  "under": { "american": -105, "decimal": 1.9524, "fractional": "20/21", "impliedProbability": 0.5122 },
  "overroundPct": 4.71,
  "scrapedAt": "…"
}
```

Outright list markets (e.g. **Anytime Touchdown Scorer**) emit one record per player with
`"propType": "outright"` and a single `"price": { "american": …, "decimal": …, "fractional": …, "impliedProbability": … }`
instead of over/under legs.

#### Field reference

**Game / event record:** `recordType` (`"game"`), `sport`, `league`, `leagueName`, `eventId`,
`eventName`, `shortName`, `startTime`, `startDate`, `status`, `state`, `completed`, `venue`,
`homeTeam` / `awayTeam` (`displayName`, `shortName`, `abbreviation`, `nickname`, `id`, `logo`),
`homeScore` / `awayScore`, `bookmakers` (books that priced the game), `bookCount`, `books`,
`analytics`, `scrapedAt`.

**Per book** (`books.<book>`): `moneyline` / `spread` / `total` (each price = `american`, `decimal`,
`fractional`, `impliedProbability`; spread/total carry `line` and `point`), `openingMoneyline` /
`openingSpread` / `openingTotal` (DraftKings only, with `includeOpeningLines`), `alternateLines`
(Pinnacle, with `includeAlternateLines`), `lastUpdated` (real odds-fetch timestamp, never the start time).

**Analytics:** `moneyline` / `spread` / `total`, each with `best` (`{ book, american, decimal }` per
outcome), `line` (spread/total), `consensusImplied` / `consensusNoVig`, `noVig` (per book, incl.
`overroundPct`), and `arbitrage` (`exists`, `roiPct`, `invSum`, `stake`, `books`, `ways`).

**`lineMovement[]`** (monitoringMode): `book`, `market`, `outcome`, `currentPrice`, `previousPrice`,
`priceChange`, `moveDirection` (`lengthened` / `shortened` / `unchanged`), `firstSeenPrice`,
`firstSeenAt`, `previousSeenAt`.

**Flat `rows`:** `recordType` (`"odds-row"`), `sport`, `league`, `eventId`, `eventName`, `homeTeam`,
`awayTeam`, `startTime`, `book`, `market`, `outcome`, `team`, `point`, `priceAmerican`, `priceDecimal`,
`priceFractional`, `impliedProbability`, `noVigProbability`, `isBestPrice`, `scrapedAt`.

**`best-line`:** `recordType` (`"best-line"`), `sport`, `league`, `eventId`, `eventName`, `startTime`,
`homeTeam`, `awayTeam`, `market`, `line`, `outcomes[]` (`outcome`, `team`, `point`, `best`, `field[]`,
`consensusImplied`, `noVigProbability`, `edgePct`), `bookCount`, `arbitrage`, `scrapedAt`.

**`player-prop`:** `recordType` (`"player-prop"`), `sport`, `league`, `eventId`, `eventName`,
`startTime`, `book`, `player`, `team`, `statGroup`, `statType`, `propType`, `line`, `overroundPct`,
`scrapedAt`, plus `over`/`under` (over-under), `yes`/`no` (yes-no), or `price` (outright).

***

### Pricing

This actor uses **pay-per-event**: each output record type is priced separately. A full **game-odds**
record (all books side by side + best-line, no-vig and arbitrage analytics) is the primary event;
**best-line** records, **player props** and flat **odds rows** are each priced per item, plus a small
actor-start fee. You pay only for what a run produces — cap a run with `maxItems`, `maxEventsPerSport`
and `lookAheadDays`, and narrow it with `books`, `markets` and the `minArbPct` / `minBestEdgePct` edge
filters to keep record counts (and cost) tight. Exact per-event prices are shown on this Actor's Apify
Store page. Platform usage (compute & proxy) is billed to the account running the Actor at Apify's
standard rates.

***

### Use cases

- **Line-shopping** — for each game and market, get the best available price and the book offering it,
  so you always bet the number instead of leaving value on the table.
- **Arbitrage & value scanning** — surface risk-free arbitrage (2-way and 3-way soccer) and best-line
  edges vs the no-vig fair price; use `minArbPct` / `minBestEdgePct` to see only games worth acting on.
- **Odds feeds for models** — a clean, normalized multi-book feed with implied and no-vig
  probabilities and a sharp reference (Pinnacle) as model inputs, in a stable JSON schema.
- **Line-movement tracking** — run on a schedule with `monitoringMode` to record how each book's price
  moves over time (previous price, change, direction, first-seen price), plus DK opening vs closing.

***

### Honest limitations

Please read this before you buy — this actor deliberately under-promises on book count and live odds.

- **Four books, not five-plus.** The actor carries **DraftKings, FanDuel, Bovada and Pinnacle** — the
  four books that are genuinely reachable without a login or paid API. It does not claim to carry every
  book on the market.
- **BetMGM and Caesars are not offered — genuinely unreachable without a login or paid access, not
  omitted by choice.** Both are documented, not faked.
- **FanDuel requires a US Apify proxy.** FanDuel is geo-restricted and needs a **US IP** — enable
  `proxyConfiguration` (a US datacenter proxy is sufficient; residential also works). The other three
  books work with **no proxy at all**. Without a US proxy, FanDuel is emitted as `null` and the run logs
  a warning — honest, never faked.
- **Live / in-play odds are not included.** This is a **pre-match** feed plus DraftKings **opening and
  closing** snapshots. `includeStartedEvents` surfaces in-progress/final games with the last posted line
  and scores, but true changing in-play *prices* are **not** shipped — they were not verifiably reachable
  keyless at build time, so they were deferred rather than shipped unvalidated. **Live is on the roadmap.**
- **Tennis and golf are not supported.** Their schedules are tournament-level (outright/futures)
  events with **no per-match moneyline/spread/total game lines**, so there is nothing to line-shop in this
  actor's schema. They would need a different (outrights) backbone.
- **Coverage depends on the slate.** Off-season leagues may have DraftKings lines weeks out
  while other books haven't posted yet — those books are honestly `null` until they open. Pinnacle prices
  most but not all games (e.g. 7 of 9 in a sample MLB slate). Soccer carries DK + Pinnacle; FanDuel
  exposes no soccer lobby and Bovada's soccer coupons are inconsistent.

**Positioning:** the deepest **analytics** aggregator across four real, keyless-reachable books — not
"every book, every market." If you need BetMGM/Caesars, live in-play prices, or tennis/golf, this actor
is not the right fit today.

***

### FAQ

**Do I need an API key or account?** No. There is no login and no paid odds API — everything works with
built-in defaults. You can optionally override Pinnacle's and FanDuel's keys via `pinnacleApiKey` /
`fanduelApiKey`.

**Do I need a proxy?** Not for DraftKings, Bovada or Pinnacle — they work with no proxy. **FanDuel does**
need a **US** proxy; without one it comes back as `null`.

**Which books will I actually get?** In-season and with a US proxy, up to **four** (DK, FanDuel, Bovada,
Pinnacle). Keyless with no proxy, three (DK, Bovada, Pinnacle). Any book that hasn't priced a given game
is `null`.

**What is "no-vig"?** The sportsbook's margin (the *vig* / overround) removed, leaving fair implied
probabilities that sum to 100%. It's the standard way to estimate a true price. `overroundPct` reports
the margin the book was charging.

**How do I track line movement?** Turn on `monitoringMode` and run the actor on a schedule. It persists
last-seen prices in a named key-value store and attaches a `lineMovement[]` array (previous price, change,
direction, first-seen price) on each subsequent run.

**Can I get a flat, spreadsheet-friendly export?** Yes — set `outputMode: "rows"` for one row per book ×
market × outcome (with `isBestPrice` and `noVigProbability` on each row).

**Is this legal?** The actor reads public, keyless market data and performs no login or paywall
circumvention. Odds are factual market data. It is for informational/analytical use only — you are
responsible for complying with betting laws in your jurisdiction and each site's terms. Not affiliated
with any of the sportsbooks or data sources referenced.

# Actor input Schema

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

What to scrape. games = one record per upcoming game for the chosen sport(s) with every book's moneyline/spread/total side-by-side plus best-line, no-vig and arbitrage analytics. event = the same rich record for one game by ESPN eventId. player-props = one record per player per stat market (Bovada). best-line = a line-shopping view: per market/outcome the best book and price, its edge vs the field and the no-vig fair price.

## `sport` (type: `string`):

Convenience single sport key, merged into Sports. Accepts the-odds-api style keys (basketball\_nba, americanfootball\_nfl, baseball\_mlb, icehockey\_nhl, soccer\_epl, ...) or short aliases (nba, nfl, mlb, nhl, epl). Leave blank to use Sports.

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

One or more sport/league keys. Supported: americanfootball\_nfl, americanfootball\_ncaaf, basketball\_nba, basketball\_wnba, basketball\_ncaab, basketball\_ncaaw, baseball\_mlb, icehockey\_nhl, soccer\_epl, soccer\_uefa\_champs\_league, soccer\_uefa\_europa\_league, soccer\_usa\_mls, soccer\_spain\_la\_liga, soccer\_italy\_serie\_a, soccer\_germany\_bundesliga, soccer\_france\_ligue\_one, mma\_mixed\_martial\_arts. Short aliases (nba, nfl, mlb, ncaaw, uel, ...) accepted. Empty + empty Sport = baseball\_mlb.

## `eventId` (type: `string`):

ESPN event id for mode=event (the eventId field returned by games mode). Requires a matching Sport so the league is known.

## `eventIds` (type: `array`):

Multiple ESPN event ids for mode=event.

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

Restrict output to these sportsbooks. Empty = all available. FanDuel may require a US proxy in some regions (enable proxyConfiguration); a book with no line for a given game is emitted as null.

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

Restrict to these game-line markets. Empty = all. (Player props are their own mode.)

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

Display preference. American, decimal and fractional prices are always emitted on every outcome regardless of this setting.

## `outputMode` (type: `string`):

events = one nested record per game (books side-by-side + analytics). rows = flat, one row per book x market x outcome (spreadsheet-friendly). Applies to games/event/best-line.

## `lookAheadDays` (type: `integer`):

How many days ahead to include upcoming games (games/best-line/player-props). If the near window is empty (e.g. an off-season league), the actor automatically extends up to 60 days to find the next scheduled slate.

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

Include in-progress and completed games (with live/final scores). Off = upcoming (pre-game) only.

## `dateFrom` (type: `string`):

Lower bound on game date (UTC). Overrides Look-ahead days when set.

## `dateTo` (type: `string`):

Upper bound on game date (UTC).

## `players` (type: `array`):

player-props mode: keep only players whose name contains any of these (case-insensitive), e.g. Mahomes.

## `statContains` (type: `array`):

player-props mode: keep only markets whose stat contains any of these, e.g. passing, rushing, receiving, strikeouts, points, rebounds, assists.

## `maxPropsPerEvent` (type: `integer`):

Cap the number of player-prop markets read per game.

## `includePlayerProps` (type: `boolean`):

event mode: also attach the game's Bovada player props under playerProps\[].

## `computeBestLine` (type: `boolean`):

Add the best (highest-payout) price per outcome across books and isBestPrice flags.

## `computeNoVig` (type: `boolean`):

Add de-vigged fair probabilities and the book margin (overround) per market.

## `computeArbitrage` (type: `boolean`):

Flag risk-free arbitrage across books (moneyline/total) with ROI% and stake split.

## `includeOpeningLines` (type: `boolean`):

Include DraftKings opening lines alongside closing (line-movement reference).

## `includeAlternateLines` (type: `boolean`):

Attach the full alternate spread/total ladders (multiple points per market) under each book's alternateLines. Pinnacle carries complete ladders; other books expose only the main line.

## `monitoringMode` (type: `boolean`):

Track line movement across scheduled runs. Persists last-seen prices in a named key-value store and attaches lineMovement\[] (previousPrice, priceChange, moveDirection, firstSeenPrice) to each game record (events output). Run the actor on a schedule to build history.

## `minArbPct` (type: `integer`):

Only emit games whose best cross-book arbitrage ROI is at least this percent (0 = no filter). Applies to games/best-line.

## `minBestEdgePct` (type: `integer`):

Only emit games with a best-line edge vs the no-vig fair price of at least this percent (0 = no filter). Applies to games/best-line.

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

Maximum number of output records for the whole run.

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

Cap games processed per sport (0 = no cap, limited only by Max items).

## `pinnacleApiKey` (type: `string`):

Optional override for Pinnacle's data key. Leave blank to use the built-in default.

## `fanduelApiKey` (type: `string`):

Optional override for FanDuel's data key. Leave blank to use the built-in default. FanDuel also requires a US IP — enable proxyConfiguration.

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

Optional proxy. The feeds are public and work without a proxy; enable a US datacenter or residential proxy only if you hit collective rate-limits or geo-blocks.

## Actor input object example

```json
{
  "mode": "games",
  "sport": "basketball_nba",
  "sports": [
    "baseball_mlb"
  ],
  "eventId": "",
  "eventIds": [],
  "books": [],
  "markets": [],
  "oddsFormat": "american",
  "outputMode": "events",
  "lookAheadDays": 3,
  "includeStartedEvents": false,
  "dateFrom": "",
  "dateTo": "",
  "players": [],
  "statContains": [],
  "maxPropsPerEvent": 200,
  "includePlayerProps": false,
  "computeBestLine": true,
  "computeNoVig": true,
  "computeArbitrage": true,
  "includeOpeningLines": true,
  "includeAlternateLines": false,
  "monitoringMode": false,
  "minArbPct": 0,
  "minBestEdgePct": 0,
  "maxItems": 50,
  "maxEventsPerSport": 0,
  "pinnacleApiKey": "",
  "fanduelApiKey": "",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

All game, best-line, player-prop and odds-row records produced by the run.

# 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 = {
    "mode": "games",
    "sport": "basketball_nba",
    "sports": [
        "baseball_mlb"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("brilliant_gum/sports-odds-aggregator").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 = {
    "mode": "games",
    "sport": "basketball_nba",
    "sports": ["baseball_mlb"],
}

# Run the Actor and wait for it to finish
run = client.actor("brilliant_gum/sports-odds-aggregator").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 '{
  "mode": "games",
  "sport": "basketball_nba",
  "sports": [
    "baseball_mlb"
  ]
}' |
apify call brilliant_gum/sports-odds-aggregator --silent --output-dataset

```

## MCP server setup

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

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/BxDxeqS4F6lxauy5d/builds/4XcON1368OUIM6n2c/openapi.json
