ParseForge Scrapers

NHL Advanced Stats Scraper: MoneyPuck xG, Corsi, Odds

parseforge/nhl-advanced-stats-scraper

News & mediaOther

Scrape NHL expected goals, Corsi, on-ice splits, line combos, playoff odds and game win probabilities back to the 2008-09 season.

Run this scraper See the API call
Total runs
13
Bookmarked
0
Last modified
7 days ago

This scraper was last updated on .

What does the NHL Advanced Stats Scraper: MoneyPuck xG, Corsi, Odds return?

ParseForge

MoneyPuck NHL Advanced Stats Scraper: xG, Corsi, Odds

Scrape every MoneyPuck NHL table back to the 2008-09 season: 4,700 skater rows, 490 goalie rows, 3,000 line combinations and 160 team rows per season. Every row carries expected goals, Corsi, Fenwick, on-ice and off-ice splits, danger buckets and per-60 rates. No login or API key. Export to CSV, JSON, Excel, or XML.

MoneyPuck publishes its model output as flat files and the NHL publishes a public schedule and standings API, but neither gives you a filtered, joined, one-row-per-entity table. This Actor reads both, applies your filters on the raw upstream row, and returns exactly the seasons, situations, teams and columns you asked for.

Who uses it What they scrape NHL advanced stats for
Hockey bettors Model win probability against the market price, game by game
Fantasy players Expected goals and per-60 rates to find undervalued skaters
Sports analysts On-ice and off-ice splits for player impact models
Media and content teams Line combination usage and team danger profiles for previews
Data scientists Eighteen seasons of shot-quality data as a training set

What it does

This Actor collects NHL advanced statistics from MoneyPuck and the NHL public API, and returns each entity as a flat row. Every row carries:

  • ๐Ÿงพ Core fields: player or team, position, situation, season, games played, ice time, points, goals, assists, shots and shot attempts.
  • ๐ŸŽฏ Expected goals: raw xG, flurry and score adjusted xG, goals above expected, and xG per 60.
  • ๐ŸงŠ On-ice context: on-ice and off-ice xG, Corsi and Fenwick percentages, ice time rank.
  • ๐Ÿฅ… Goalie model output: goals saved above expected, expected save percentage, rebound control.
  • ๐ŸŽฒ Odds and predictions: playoff and Stanley Cup probabilities, and the model versus market win probability for each game.
  • ๐Ÿ“Š Computed signals: points per 60, goals per 60, shooting percentage, faceoff win percentage, goal and expected goal differential.

Results export to CSV, JSON, Excel, or XML, or stream from the API.

What you can do with NHL advanced stats

๐Ÿ“ˆ Find the games where the model disagrees with the book.

The game prediction rows carry the MoneyPuck win probability and the implied market probability side by side, plus the difference between them, so a scan of one date range surfaces the disagreements without any odds maths of your own.

๐Ÿ” Rank skaters by what they created, not what went in.

Goals above expected, xG per 60 and on-ice xG percentage separate a shooter on a hot streak from one who is generating chances, over any season back to 2008-09.

๐Ÿง  Build a player impact model.

Turn on the on-ice offence and defence blocks and every skater row carries roughly sixty extra columns of what happened while that player was on the ice, in the situation you selected.

๐ŸงŠ Study line combinations and deployment.

The line dataset returns 3,000 forward lines and defence pairings per season with ice time rank, expected goals share and danger buckets, which is where usage decisions actually show up.

Why choose this scraper

What you get
Ten tables in one Actor Skaters, goalies, teams, lines, playoff odds, game predictions, schedule, standings and two game-log tables
Eighteen seasons Regular season and playoffs, every season from 2008-09 to the latest published one
Five situation splits All situations, 5 on 5, 5 on 4, 4 on 5 and other strengths
Filters run before billing Team, position, name, games played, ice time, points and expected goals are applied to the full upstream row
Optional columns are optional Six column blocks are off by default and billed only on the rows where they actually returned data
No key, no login Both upstreams are public, so there is nothing to register and no rate limit to negotiate

How it compares

There are several NHL scrapers on Apify and most of them read scoreboards: ESPN scores, live odds, or the box score tables from Hockey Reference. None of them return the MoneyPuck model output, which is the file hockey analysts actually work from. The closest neighbours are our own hockey-reference-scraper, which gives you the traditional counting stats, and the various nhl-stats-scraper listings that wrap the NHL public API. This Actor is the one that carries expected goals, danger buckets, on-ice splits and win probabilities.

This Actor Hockey Reference scrapers ESPN and scoreboard scrapers
Expected goals and danger buckets Yes No No
On-ice and off-ice splits Yes No No
Line and pairing combinations Yes No No
Playoff odds and win probabilities Yes No Odds only, no model
Seasons available 2008-09 onward Full history, counting stats Current season
Situation splits Five None None

What an NHL skater row looks like

{
  "playerId": "8478043",
  "playerName": "Sam Lafferty",
  "team": "CHI",
  "position": "C",
  "situation": "all",
  "season": "2025",
  "seasonLabel": "2025-26",
  "gameType": "regular",
  "gamesPlayed": 29,
  "icetimeMinutes": 249.32,
  "timeOnIcePerGameMinutes": 8.6,
  "shifts": 354,
  "iceTimeRank": 327,
  "gameScore": 0.83,
  "points": 2,
  "goals": 1,
  "primaryAssists": 0,
  "secondaryAssists": 1,
  "shotsOnGoal": 9,
  "missedShots": 5,
  "blockedShotAttempts": 6,
  "shotAttempts": 20,
  "xGoals": 0.85,
  "xGoalsAdjusted": 0.78,
  "goalsAboveExpected": 0.15,
  "shootingPct": 11.11,
  "pointsPer60": 0.481,
  "goalsPer60": 0.241,
  "xGoalsPer60": 0.205,
  "shotAttemptsPer60": 4.813,
  "rebounds": 2,
  "reboundGoals": 0,
  "hits": 35,
  "takeaways": 2,
  "giveaways": 15,
  "dZoneGiveaways": 3,
  "faceoffsWon": 26,
  "faceoffsLost": 26,
  "faceoffWinPct": 50,
  "penalties": 2,
  "penaltyMinutes": 4,
  "penaltiesDrawn": 1,
  "shotsBlockedByPlayer": 5,
  "onIceXGoalsPct": 39,
  "onIceCorsiPct": 47,
  "onIceFenwickPct": 45,
  "offIceXGoalsPct": 43,
  "sourceUrl": "https://moneypuck.com/moneypuck/playerData/seasonSummary/2025/regular/skaters.csv",
  "rowType": "skater",
  "scrapedAt": "2026-08-28T19:19:25.896Z"
}

Configure the run

Pick the tables you want in Data to scrape, choose a Season and a Game type, and select which Game situations you want split out. Everything else is a filter: team, position, player name, minimum games, minimum ice time, minimum points and minimum expected goals are all applied to the raw upstream row, so you can filter on a column without buying the block that contains it. When you select several tables, the row cap is split evenly between them and anything one table leaves unused rolls to the next.

Every skater in the latest published season, no extras:

{
  "datasets": ["skaters"],
  "season": "latest",
  "gameType": "regular",
  "situations": ["all"],
  "maxItems": 5000
}

Colorado's 5 on 5 skaters with the on-ice and danger columns:

{
  "datasets": ["skaters"],
  "season": "2025",
  "situations": ["5on5"],
  "teams": ["COL"],
  "minIcetimeMinutes": 200,
  "includeOnIceFor": true,
  "includeOnIceAgainst": true,
  "includeDangerBreakdown": true,
  "maxItems": 100
}

Model versus market win probability for a week of games:

{
  "datasets": ["gamePredictions", "schedule"],
  "dateFrom": "2026-04-01",
  "dateTo": "2026-04-07",
  "maxItems": 200
}

Free users

Free Apify accounts get a 10 row preview per run, which is enough to check the schema and the field names. Upgrade to a paid plan to lift the cap to a million rows per run.

Run it

  1. Create a free Apify account. New accounts get $5 in free credit.
  2. Open the Actor, leave the default input, and press Start to see ten skater rows.
  3. Pick the tables, the season and the filters you want, then raise Max Items.
  4. Export the dataset to CSV, JSON, Excel or XML, or read it from the API.

Use with AI agents (MCP)

claude mcp add apify --transport sse https://mcp.apify.com/sse --header "Authorization: Bearer YOUR_APIFY_TOKEN"

Then ask in plain language:

  • "Get me every Colorado skater at 5 on 5 last season with more than 200 minutes of ice time."
  • "Which games this week does the model like more than the sportsbook does?"
  • "Pull the top 50 skaters by goals above expected for the 2024-25 season."

Troubleshooting

No results came back. The most common cause is a situation filter that does not exist in the table you picked. Line and pairing rows exist for 5 on 5 and other strengths, not for every split. Start with all and narrow from there.

Fewer rows than I asked for. When you select several tables the cap is split between them, and a table that runs out of matching rows hands the remainder to the next one. Raise Max Items, or select fewer tables.

A column I expected is missing. The six column blocks are off by default. Tick the block you need, for example on-ice offence, and the columns appear on the rows that have them.

Playoff odds all read 0 or 100. The simulation file is a live file. Between seasons it holds the finished season, where every probability has collapsed to a certainty. During the season it holds live odds.

The run is slow. Game logs are one file download per player or per team. Lower Max players or teams for game logs, or select the season summary tables instead.

FAQ

Question Answer
Where does the data come from? MoneyPuck's published model files and the NHL public API. Both are public and neither needs a key.
How far back does it go? The 2008-09 season, for both regular season and playoffs.
What does a season number mean? The season's starting year, MoneyPuck style. 2025 is the 2025-26 season.
What are the situations? All situations, 5 on 5, 5 on 4 power play, 4 on 5 penalty kill, and other strengths.
Are old team codes handled? Yes. Los Angeles is L.A before 2017 upstream, and a filter on LAK matches those rows too.
Can I filter without paying for the column? Yes. Filters run on the full upstream row before projection, so filtering by expected goals does not require the expected goals detail block.
Is the market win probability included? Yes, on game prediction rows, together with the model probability and the difference between them.
How fast is it? Measured at 9,756 rows in 15.4 seconds on a 512 MB run, with a 267 MB peak.
Does it need a proxy? No. Both upstreams answer plain HTTP requests.

Need help? Email parseforge@protonmail.com with your run ID, your input, and what you expected.

Disclaimer: this is an unofficial tool, not affiliated with MoneyPuck or the National Hockey League. It reads only public data that both sources publish without a login. Statistics about public sporting events are not personal data under GDPR, CCPA or PIPL; player names appear because they are part of the public record of a professional competition.

What input does the NHL Advanced Stats Scraper: MoneyPuck xG, Corsi, Odds accept?

FieldTypeWhat it doesDefault
datasets array Which NHL tables to pull. Skaters, goalies, teams and lines are MoneyPuck season summaries; playoff odds are MoneyPuck simulations; schedule and standings come from the NHL public API; game logs are one row per game per entity. ["skaters"]
season string (19 options) Season start year, MoneyPuck style: 2025 is the 2025-26 season. Leave on Latest to take the most recent published season automatically. latest
gameType string (2 options) Regular season or playoffs. Both are published for every season back to 2008-09. regular
situations array MoneyPuck splits every table by situation. 'all' is the full-strength-plus-special-teams total; the others are the strength states. ["all"]
maxItems integer Free users: limited to 10 items (preview). Paid users: up to 1,000,000. 10
teams array Filter by team code. Historical codes are matched automatically, so LAK also matches the L.A rows used before 2017. not set
positions array Skater positions as MoneyPuck labels them. Goalies live in their own dataset, not here. not set
playerName string Case-insensitive substring match on the player name, for example 'McDavid'. not set
playerIds array NHL player IDs to pull game logs for, for example 8478402. When empty, the game-log datasets take the highest-ice-time players that pass the filters. not set
minGamesPlayed integer Drop rows below this many games. Useful to cut the long tail of one-game call-ups. not set
minIcetimeMinutes number Drop rows below this much total ice time in the season, in minutes. not set
minPoints number Drop skater rows below this many points. not set
minXGoals number Drop rows below this many individual expected goals. Decimals are honoured, so 12.5 works. not set
dateFrom string First date for the schedule, game predictions and standings datasets, as YYYY-MM-DD. Defaults to seven days ago. not set
dateTo string Last date for the schedule, game predictions and standings datasets, as YYYY-MM-DD. Defaults to today. not set
maxGameLogEntities integer How many players or teams the game-log datasets walk. Each one is a separate file download. 5
includeDangerBreakdown boolean Adds low, medium and high danger shots, expected goals and goals to every row. false
includeOnIceFor boolean Adds the ~30 on-ice-for columns: shots, expected goals, rebounds and danger buckets while the player was on the ice. false
includeOnIceAgainst boolean Adds the ~30 on-ice-against columns, which is what a defensive-impact model needs. false
includeZoneStarts boolean Adds offensive, defensive, neutral and on-the-fly shift starts and ends, plus time on bench. false
includeExpectedGoalsDetail boolean Adds the expected rebound, freeze, play-stopped and score-adjusted components behind the xG number. false
includeOffIceSplits boolean Adds the team's expected goals and shot attempts while the player was on the bench, plus the after-shift carryover columns. false
includePlayerBio boolean Looks each skater or goalie up in the NHL public API and adds height, weight, birth date and place, shoots or catches, draft position and headshot. false

How much does the NHL Advanced Stats Scraper: MoneyPuck xG, Corsi, Odds cost?

from $4.45 per 1,000 results

Charged forWhat it isPrice each
Actor Start Charged when the Actor starts running. Number of events charged depends on Actor memory (one event per GB, minimum one event). $0.02
Source file scanned Charged once per MoneyPuck file downloaded and parsed, for example one season summary or one career game log. This is the fixed cost of pagination, spread across every row that file produces. $0.01
Skater season row Charged once per skater season row written to your dataset: 50 fields covering points, expected goals, on-ice percentages and per-60 rates for one player in one situation. $0.00445 to $0.005
Goalie season row Charged once per goalie season row, including goals saved above expected, expected save percentage and rebound control. $0.00445 to $0.005
Team season row Charged once per team season row, with for and against splits on shots, expected goals, danger buckets and discipline. $0.00445 to $0.005
Line or pairing row Charged once per forward line or defence pairing row, with its ice time rank and its expected goals share. $0.00445 to $0.005
Playoff odds row Charged once per team row of MoneyPuck simulation output: playoff, round, conference final, final and Stanley Cup probabilities. $0.00445 to $0.005
Game prediction row Charged once per game win probability row, with the model price, the market price and the difference between them. $0.00445 to $0.005
Schedule row Charged once per scheduled or finished game read from the NHL public API, with venue, broadcast and final score. $0.00267 to $0.003
Standings row Charged once per team standings row, with the home, road, last-ten and streak splits. $0.00267 to $0.003
Player game log row Charged once per player game row, one row per game per situation, with the same advanced columns as the season table. $0.00267 to $0.003
Team game log row Charged once per team game row, one row per game per situation, back to the 2008-09 season. $0.00267 to $0.003
Player biography block Optional. Charged only on rows where the NHL public API returned a biography: height, weight, birth date and place, shoots or catches, draft position and headshot. $0.00356 to $0.004
Shot danger block Optional. Charged only on rows that received the low, medium and high danger shots, expected goals and goals columns. $0.00134 to $0.0015
On-ice offence block Optional. Charged only on rows that received the on-ice-for columns: shots, expected goals, rebounds and danger buckets while the player was on the ice. $0.00178 to $0.002
On-ice defence block Optional. Charged only on rows that received the on-ice-against columns, which is what a defensive impact model needs. $0.00178 to $0.002
Zone starts block Optional. Charged only on rows that received offensive, defensive, neutral and on-the-fly shift starts and ends plus time on bench. $0.00134 to $0.0015
Expected goals detail block Optional. Charged only on rows that received the expected rebound, freeze, play-stopped and score-adjusted components behind the xG number. $0.00134 to $0.0015
Off-ice splits block Optional. Charged only on rows that received the bench-time team splits and the after-shift carryover columns. $0.00134 to $0.0015

Tiered: the lower figure is the price on a higher Apify plan. Billing and the free credit live on Apify.

How do I call the NHL Advanced Stats Scraper: MoneyPuck xG, Corsi, Odds API?

One POST returns the dataset directly. Same shape for every scraper in the library, so swapping the slug is the only change.

POST ยท run and get results
curl -X POST "https://api.apify.com/v2/acts/parseforge~nhl-advanced-stats-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "datasets": [
      "skaters"
    ],
    "season": "latest",
    "gameType": "regular",
    "situations": [
      "all"
    ],
    "maxItems": 10
  }'

What example inputs can I use?

Use these inputs to see how a run is configured.

input.json
{
  "datasets": [
    "skaters"
  ],
  "season": "latest",
  "gameType": "regular",
  "situations": [
    "all"
  ],
  "maxItems": 10
}

What do users say about the NHL Advanced Stats Scraper: MoneyPuck xG, Corsi, Odds?

No reviews yet. Be the first.

How do I report an issue with the NHL Advanced Stats Scraper: MoneyPuck xG, Corsi, Odds?

We build and maintain this scraper, so a problem with it comes to us. Report it on the Apify listing and the thread stays attached to the scraper where the next person can find it: open an issue.

Broken and urgent, or you would rather not post in public? Write to parseforge@protonmail.com and it reaches the people who wrote it.

What related scrapers can I use?

Run NHL Advanced Stats Scraper: MoneyPuck xG, Corsi, Odds on Apify All scrapers