# NFL Playoff Odds API - Monte Carlo Simulator (`commodus67/nfl-playoff-odds-api-monte-carlo-simulator`) Actor

Monte Carlo simulator for NFL playoff odds. Projects division, wild card, playoff and No. 1 seed probabilities for all 32 teams, plus market edge and Kelly stake sizing. JSON output for apps, models and AI agents.

- **URL**: https://apify.com/commodus67/nfl-playoff-odds-api-monte-carlo-simulator.md
- **Developed by:** [ELIO LIBERATORE](https://apify.com/commodus67) (community)
- **Categories:** Developer tools, Automation, News
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.10 / 1,000 team projections

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

## NFL Playoff Odds API — Monte Carlo Simulator for Football Postseason Probabilities

Get **NFL playoff odds as structured data**, not as a web page. This Actor replays every remaining game of the NFL regular season thousands of times on the real schedule and returns, for all 32 teams, the probability of winning the division, taking a wild card, reaching the playoffs, and landing the No. 1 seed — plus projected records, market edges and Kelly-style bet sizing.

Playoff odds tables are published all over the web, but almost never as an API you can pull into a spreadsheet, a model or an AI agent. This Actor computes them from scratch and hands you clean JSON, CSV, Excel or an API endpoint.

**No API key. No account. No configuration.** Standings and the remaining schedule come from the public ESPN feed. Run it with an empty input and it projects the current season.

***

### What you get

- **Playoff probabilities** for all 32 NFL teams — division, wild card, overall playoff berth, and No. 1 seed
- **Projected final records** — mean wins, losses and ties across every simulated season
- **The strength estimates behind the numbers** — actual win %, Pythagorean win % from points scored and allowed, and the regressed true-talent estimate the simulation actually uses
- **Market edge** — supply bookmaker implied probabilities and get the gap between model and market, per team
- **Bet sizing** — quarter-Kelly stake suggestions with per-position and total-portfolio caps
- **Three ready-made dataset views** — playoff overview, market edge table, and the records / point differential the model was built from

Export to **JSON, CSV, Excel or XML**, call it from the **Apify API**, schedule it weekly, or plug it into n8n, Make, Zapier or an MCP-enabled AI agent.

***

### Who uses this

| Use case | What you do with it |
|---|---|
| **Sports betting models** | Compare fair probabilities against sportsbook futures prices and size positions with the built-in Kelly caps |
| **Fantasy football** | Know which teams are still playing meaningful games in December before you plan your playoff roster |
| **Sports media and blogs** | A defensible playoff-odds table you generated yourself, refreshed every Tuesday morning |
| **Data science and backtesting** | Set a past `season` and project it from where it stood, then score the model against what happened |
| **Dashboards** | A weekly scheduled run feeding Google Sheets, a database, or a BI tool |
| **AI agents** | A tool that returns real computed probabilities instead of hallucinated ones |

***

### Quick start

Run it with an empty input:

```json
{}
```

That projects the current season with 20,000 simulated seasons. A full 32-team run finishes in a few seconds.

To compare against the market, add bookmaker implied probabilities:

```json
{
  "iterations": 20000,
  "marketProbabilities": [
    { "team": "Kansas City Chiefs", "market": "playoff",  "impliedProbability": 0.82 },
    { "team": "Detroit Lions",      "market": "division", "impliedProbability": 0.55 }
  ]
}
```

Team names are matched loosely — `"chiefs"` and `"Kansas City Chiefs"` both work. `market` accepts `playoff` or `division`.

***

### Why simulate instead of scraping a playoff odds page

Most football tools copy numbers out of a website. This one computes something that is not published as data anywhere: a full distribution of season outcomes built from the actual games each team still has to play. Four design choices make the difference.

**It simulates the real schedule, not an average one.** NFL schedules are unbalanced by design — place-based scheduling means a first-place team draws other first-place teams. A 6-4 club with three divisional road games left has a harder path than its record suggests. Every remaining fixture is loaded individually, with the correct home and away side, and played out.

**It leans on point differential, not just the record.** Points scored and allowed predict future wins better than the win-loss column does, and over a 17-game season that gap is enormous. A 7-3 team outscored by 20 points is not a 7-3 team. The default weight on Pythagorean expectation is 0.65 and you can move it.

**It treats a 17-game sample as the small sample it is.** Every simulated season redraws each team's true talent from a distribution rather than assuming this year's record is the truth. Without that, a 17-game season produces absurdly confident probabilities. This is why the regression setting here (6 games) is deliberately much smaller than you would use in baseball, while the strength uncertainty is larger.

**It reproduces the real playoff format.** Four division winners per conference, seeded 1 through 4; the three best non-winners take the wild cards; the top division winner in each conference gets the first-round bye. Ties count as half a win in the standings, exactly as the league treats them.

***

### How it works

1. **Load the standings** — wins, losses, ties, points for and points against for all 32 teams, from the public ESPN feed.
2. **Estimate true talent** — blend the actual winning percentage with the Pythagorean expectation from point differential (exponent 2.37), then regress the result toward .500 by a configurable number of games.
3. **Load the remaining schedule** — every unplayed game for every team, deduplicated, with home and away sides.
4. **Simulate** — for each iteration, redraw every team's strength from a normal distribution around its estimate, then play out every remaining game as a logistic function of the log-odds difference plus home-field advantage, with a small chance of a tie.
5. **Seed the field** — sort each division, take the four winners, then the three best remaining teams per conference as wild cards, and record the top seed.
6. **Aggregate** — count how often each team wins its division, takes a wild card, makes the field and lands the No. 1 seed, and average the projected record.

***

### Value bets and position sizing

If you supply `marketProbabilities`, the Actor does more than show you the gap. It sizes the bet.

- **`playoffEdge` / `divisionEdge`** — model probability minus market probability, in percentage points.
- **`hasValue`** — true when the best edge for that team clears `edgeThreshold` (default 0.05, i.e. five points).
- **`kellyFractionUncapped`** — quarter-Kelly stake as a fraction of bankroll, before any limits.
- **`suggestedFractionOfBankroll`** — the same number after clipping to `maxPerPositionPct` (default 2% of bankroll).
- **`suggestedStake`** — that fraction times your `bankroll`, in dollars.
- **`cappedByPortfolioLimit`** — true when the bet qualified but the total exposure ceiling (`maxTotalExposurePct`, default 6%) was already used up by better edges. Those rows come back with a zero stake so you can see what was dropped and why.

Positions are filled best-edge-first. At the defaults, 6% total over a 2% per-position cap means roughly three bets fit.

Quarter-Kelly is a deliberate choice: full Kelly is optimal only if your probabilities are exactly right, and nobody's are.

***

### Input

Every field is optional. An empty input gives you the current season with sensible defaults.

| Field | Type | Default | What it does |
|---|---|---|---|
| `iterations` | integer | 20000 | Simulated seasons. More iterations, smoother probabilities. |
| `season` | integer | current | Set a past year to project that season from where it stood — useful for backtesting. |
| `archiveToNamedDataset` | string | empty | Name of a dataset in your account to also append this run's rows to. Named datasets are kept indefinitely, so scheduled runs accumulate a permanent history. See “Build your own history” below. |
| `regressionGames` | integer | 6 | How hard a partial season is pulled toward .500. Small on purpose: an NFL season is only 17 games. |
| `pythagoreanWeight` | number | 0.65 | How much of the strength estimate comes from point differential rather than the raw record. |
| `homeFieldAdvantage` | number | 0.2 | Added to the home team's strength in log-odds. 0.2 ≈ a 55% home win rate between equal teams. |
| `strengthUncertainty` | number | 0.16 | How unsure the model is about each team's true talent. Setting it to zero makes the output far too confident. |
| `tieProbability` | number | 0.004 | Chance any single game ends in a tie — roughly one per season across 272 games. |
| `edgeThreshold` | number | 0.05 | Minimum model-minus-market gap to flag a team as value. |
| `bankroll` | integer | 1000 | Your capital, in dollars. Scales `suggestedStake` only. |
| `maxPerPositionPct` | number | 0.02 | Hard ceiling on any single bet, as a fraction of bankroll. |
| `maxTotalExposurePct` | number | 0.06 | Ceiling on the sum of all open positions. |
| `marketProbabilities` | array | `[]` | Bookmaker implied probabilities to compare against. Each entry needs `team`, `market` (`playoff` or `division`) and `impliedProbability`. |

***

### Output

One row per team, sorted by playoff probability. Three prebuilt views let you jump straight to what you need.

**Playoff overview** — division, team, projected wins, and the four probabilities.

| Field | Meaning |
|---|---|
| `fairDivisionProbability` | Chance of winning the division |
| `fairWildCardProbability` | Chance of making it as a wild card |
| `fairPlayoffProbability` | Chance of reaching the playoffs by either route |
| `fairTopSeedProbability` | Chance of the No. 1 seed and the first-round bye |
| `projectedWins` / `projectedLosses` / `projectedTies` | Mean final record across all simulations |

**Market edge** — model vs market, the gap, and the stake sizing described above.

**Records and point differential** — `wins`, `losses`, `ties`, `gamesRemaining`, `pointsFor`, `pointsAgainst`, `pointDifferential`, `actualWinPct`, `pythagoreanWinPct` and `trueTalentWinPct`, so you can see exactly what the model thought each team was before it started simulating.

Every row also carries `simulations`, `season` and `retrievedAt`, so a stored dataset stays interpretable months later.

***

### Run it every week

NFL games are mostly weekly, so the numbers move in one big step. Schedule a run for Tuesday morning and the odds are fresh for the whole week:

1. Open the **Schedules** tab in Apify Console
2. Add this Actor
3. Set the cron to `0 13 * * 2` (Tuesdays, 13:00 UTC)

Point the dataset at Google Sheets, a webhook or a database and you have a self-updating playoff odds table.

***

### Build your own history

A run's own dataset is temporary — Apify deletes it 31 days after the run. A **named** dataset is kept indefinitely.

Set `archiveToNamedDataset` to a name such as `nfl-playoff-odds-history` and every run appends its rows to that dataset as well as writing its normal one. Point a daily or weekly schedule at it and you accumulate your own record of how each team's playoff probability moved across the season — a time series you cannot reconstruct afterwards, because the standings and remaining schedule that produced each snapshot no longer exist.

Every row already carries `retrievedAt`, `season` and `simulations`, so stacked snapshots are never ambiguous: you always know when a projection was taken, for which season, and how many simulated seasons it came from.

The named dataset is created the first time it is used and lives in your account under **Storage → Datasets**. Leave the field empty and nothing changes: you get the normal run dataset only.

***

### Pricing

Pay per result. You are charged for each team projection returned — a full league run is 32 of them. No platform usage fees on top, no subscription, no minimum.

A full 32-team league run costs about **$0.10**. Running it once a week for a whole NFL season is roughly $2 in total.

***

### FAQ

**Where does the data come from?**
The public ESPN standings and schedule feeds. No API key and no account are required.

**Are these the same numbers as FiveThirtyEight or ESPN's FPI?**
No. Those are different models with different assumptions, and several of them are no longer published as data at all. This one is transparent about its inputs and every knob is exposed, so you can tune it and see exactly what changed.

**How many iterations do I need?**
20,000 is plenty. Probabilities are typically stable to well under a percentage point at that level. Going to 100,000 costs more time for a difference you will not act on.

**Can I project a past season?**
Yes. Set `season` to a past year and the Actor projects it from the point the standings stood at, which is how you backtest the model.

**Does it handle ties?**
Yes. Ties count as half a win in the standings, exactly as the NFL does it, and the simulation produces them at a realistic rate.

**Does it model the exact NFL tiebreakers?**
No, and this is worth being clear about. Head-to-head, common games and strength of victory are not reproduced; ties in simulated win totals are broken randomly. Over 20,000 seasons this washes out to a small effect on team probabilities, but it means a specific late-season tiebreaker scenario is not what this tool answers.

**Can an AI agent call this?**
Yes. It works through the Apify MCP server like any other Actor, and it returns computed numbers instead of guessed ones.

**Can I get the output in a spreadsheet?**
Yes. Every run's dataset exports to CSV, Excel, JSON or XML, or you can pull it from the API.

**What does `hasValue` actually mean?**
That the model's probability beats the market probability you supplied by at least `edgeThreshold`. It is a flag on your own inputs, not a recommendation.

**Is there a version for other sports?**
There is an MLB version — [MLB Playoff Odds API](https://apify.com/commodus67/mlb-playoff-odds-monte-carlo) — built on the same engine, and a soccer model for 17 leagues, [Football (Soccer) Monte Carlo Season Predictor](https://apify.com/commodus67/football-soccer-monte-carlo-predictor).

***

### Limitations and honest caveats

- **Injuries, weather, benched starters and quarterback changes are not modeled.** The strength estimate is based only on results and points. A team that just lost its starting quarterback will look better here than it should.
- **Tiebreakers are simplified**, as described above.
- **Early in the season the numbers are soft.** With three games played there is very little signal; the regression toward .500 is doing most of the work, and it should be.
- **This is a model, not a prediction.** A 78% playoff probability means the team misses roughly one season in five. That is the point of a distribution.
- **Nothing here is betting advice.** The Kelly outputs are arithmetic on numbers you supplied, and the edge is only as good as your market prices.

***

*Not affiliated with, endorsed by or sponsored by the National Football League or ESPN. Team names and league marks belong to their respective owners. Data comes from publicly available feeds.*

# Actor input Schema

## `iterations` (type: `integer`):

How many full seasons to simulate. More iterations give smoother probabilities and cost a little more time. 20000 is plenty for stable numbers; the run takes a few seconds either way.

## `season` (type: `integer`):

Leave empty to use the current season. Set a year to project a past season from where it stood, which is useful for backtesting the model.

## `archiveToNamedDataset` (type: `string`):

Leave empty for a normal run. If you put a name here, every run also appends its results to a named dataset in your account, which Apify keeps indefinitely instead of deleting after a few days. Run it on a schedule and you build your own history of how the probabilities moved across the season, which is something you cannot reconstruct later. Use the same name every time, for example nfl-playoff-odds-history.

## `regressionGames` (type: `integer`):

How strongly a partial season is pulled towards .500. Think of it as adding this many imaginary games at a .500 record. An NFL season is only 17 games, so the default of 6 is deliberately much smaller than you would use in baseball. Lower it to trust the current record more, raise it to trust it less.

## `pythagoreanWeight` (type: `number`):

How much of the strength estimate comes from points scored and allowed rather than the raw win-loss record. Point differential predicts future wins better than the record itself, especially over a short season, so the default leans on it.

## `homeFieldAdvantage` (type: `number`):

Added to the home team's strength in every simulated game. The default of 0.2 corresponds to roughly a 55 percent win rate for the home side between evenly matched teams, in line with recent NFL seasons. Set to 0 to ignore venue.

## `strengthUncertainty` (type: `number`):

How unsure the model is about each team's true talent. Every simulated season draws a slightly different version of each team. A 17-game sample is very noisy, so this matters more in the NFL than in a long season. Setting it to zero makes the output far too confident.

## `tieProbability` (type: `number`):

Chance that any single simulated game ends in a tie. NFL ties are rare but real, roughly one per season across 272 games, which is where the default of 0.004 comes from. A tie counts as half a win in the standings, exactly as the league treats it.

## `edgeThreshold` (type: `number`):

A team is flagged in the hasValue column when the model probability beats the market probability you supplied by at least this much. 0.05 means five percentage points.

## `bankroll` (type: `integer`):

Your total betting capital, in dollars. It only scales the suggestedStake column. The suggestedFractionOfBankroll column is independent of this number, so it stays meaningful whatever capital you run.

## `maxPerPositionPct` (type: `number`):

Hard ceiling on any single bet, as a fraction of bankroll. 0.02 means no position ever exceeds 2% of your capital. Quarter-Kelly sizing is clipped to this ceiling, and the kellyFractionUncapped column shows what the model wanted before the clip.

## `maxTotalExposurePct` (type: `number`):

Ceiling on the sum of all open positions, as a fraction of bankroll. This divided by the per-position ceiling is how many bets actually fit: at the defaults, 6% over 2% means three. Qualifying bets beyond that are still marked as value but come back with a zero stake and cappedByPortfolioLimit set to true.

## `marketProbabilities` (type: `array`):

Bookmaker implied probabilities to compare against, so the output shows where the model disagrees with the market. Each entry needs a team name, a market of either playoff or division, and a probability between 0 and 1. Example: \[{ "team": "Kansas City Chiefs", "market": "playoff", "impliedProbability": 0.82 }]

## Actor input object example

```json
{
  "iterations": 20000,
  "regressionGames": 6,
  "pythagoreanWeight": 0.65,
  "homeFieldAdvantage": 0.2,
  "strengthUncertainty": 0.16,
  "tieProbability": 0.004,
  "edgeThreshold": 0.05,
  "bankroll": 1000,
  "maxPerPositionPct": 0.02,
  "maxTotalExposurePct": 0.06,
  "marketProbabilities": []
}
```

# Actor output Schema

## `teamProjections` (type: `string`):

All 32 team rows: division, wild card, playoff and top seed probabilities, projected record, and market edge when market probabilities were supplied.

# 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 = {
    "iterations": 20000,
    "regressionGames": 6,
    "pythagoreanWeight": 0.65,
    "homeFieldAdvantage": 0.2,
    "strengthUncertainty": 0.16,
    "tieProbability": 0.004,
    "edgeThreshold": 0.05,
    "bankroll": 1000,
    "maxPerPositionPct": 0.02,
    "maxTotalExposurePct": 0.06,
    "marketProbabilities": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("commodus67/nfl-playoff-odds-api-monte-carlo-simulator").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 = {
    "iterations": 20000,
    "regressionGames": 6,
    "pythagoreanWeight": 0.65,
    "homeFieldAdvantage": 0.2,
    "strengthUncertainty": 0.16,
    "tieProbability": 0.004,
    "edgeThreshold": 0.05,
    "bankroll": 1000,
    "maxPerPositionPct": 0.02,
    "maxTotalExposurePct": 0.06,
    "marketProbabilities": [],
}

# Run the Actor and wait for it to finish
run = client.actor("commodus67/nfl-playoff-odds-api-monte-carlo-simulator").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 '{
  "iterations": 20000,
  "regressionGames": 6,
  "pythagoreanWeight": 0.65,
  "homeFieldAdvantage": 0.2,
  "strengthUncertainty": 0.16,
  "tieProbability": 0.004,
  "edgeThreshold": 0.05,
  "bankroll": 1000,
  "maxPerPositionPct": 0.02,
  "maxTotalExposurePct": 0.06,
  "marketProbabilities": []
}' |
apify call commodus67/nfl-playoff-odds-api-monte-carlo-simulator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,commodus67/nfl-playoff-odds-api-monte-carlo-simulator"
        }
    }
}

```

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/V403EhJwem1V6Pa1Z/builds/1bsSrQe5ycYakxwlp/openapi.json
