Documentation
Everything you need to connect and query 12 NHL data endpoints.
Getting Started
Authentication
Every request requires an API key. Pass it via header:
x-api-key: YOUR_API_KEY
# or
Authorization: Bearer YOUR_API_KEYGet your API key from the dashboard after signing up. Free accounts get 500 credits.
Base URL
https://mcp.puckapi.com/mcpMCP protocol over Streamable HTTP. Works with Claude Desktop, Claude Code, Cursor, and any MCP-compatible client. Also accepts standard JSON-RPC POST requests.
Rate Limits
| Plan | Rate Limit |
|---|---|
| Free | 10 req/min |
| Starter / Pro / Scale | 60 req/min |
| Enterprise | Custom |
Error Handling
| Code | Meaning | What to do |
|---|---|---|
| 401 | Invalid or missing API key | Check your x-api-key header |
| 402 | Insufficient credits | Top up your wallet or upgrade your plan |
| 429 | Rate limit exceeded | Wait and retry (see limits above) |
| 500 | Server error | Retry with idempotency key. Credits auto-refund on server errors. |
Pass x-idempotency-key or x-request-id headers to make retries duplicate-safe.
Setup
Claude Desktop
Add to your claude_desktop_config.json:
{
"mcpServers": {
"puckapi": {
"url": "https://mcp.puckapi.com/mcp",
"headers": {
"x-api-key": "YOUR_API_KEY"
}
}
}
}Claude Code
One command:
claude mcp add puckapi https://mcp.puckapi.com/mcp --header "x-api-key: YOUR_API_KEY"REST / cURL
Send JSON-RPC POST requests directly. No MCP client needed:
curl -X POST https://mcp.puckapi.com/mcp \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "list_teams",
"arguments": {}
},
"id": 1
}'Which Endpoint Should I Use?
Start from what you want to do, not the API path.
Endpoint Reference
12 endpoints grouped by credit cost. Each includes parameters, example requests in three formats, and documented response fields.
Static reference
1 credit/calllist_teams
1 creditAll 32 active NHL teams with divisions, conferences, and arenas.
When to use: You need a reference list of teams -- team abbreviations for other endpoints, arena names for travel planning, or division/conference groupings for analysis.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| conference | string | optional | "Eastern" or "Western" |
| division | string | optional | Division name (e.g. "Atlantic") |
Example Request
{
"tool": "list_teams",
"arguments": {
"conference": "Eastern",
"division": "Atlantic"
}
}Example Response
{
"teams": [
{
"abbrev": "BUF",
"name": "Sabres",
"city": "Buffalo",
"conference": "Eastern",
"division": "Atlantic",
"arena": "KeyBank Center"
},
{
"abbrev": "TOR",
"name": "Maple Leafs",
"city": "Toronto",
"conference": "Eastern",
"division": "Atlantic",
"arena": "Scotiabank Arena"
},
{
"abbrev": "FLA",
"name": "Panthers",
"city": "Florida",
"conference": "Eastern",
"division": "Atlantic",
"arena": "Amerant Bank Arena"
}
]
}Response Fields
| Field | Type | Description |
|---|---|---|
| teams[].abbrev | string | 3-letter team code (e.g. "BUF") |
| teams[].name | string | Team name (e.g. "Sabres") |
| teams[].city | string | City name |
| teams[].conference | string | Eastern or Western |
| teams[].division | string | Division name |
| teams[].arena | string | Home arena name |
Basic lookup
2 credits/callget_schedule
2 creditsUpcoming games for the next N days. Defaults to 7 days.
When to use: You want to know what games are coming up this week -- for a specific team or across the league. Useful for building game-day alerts or planning which games to watch.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| team | string | optional | Team abbreviation (e.g. "BUF") |
| days | integer | optional | Days ahead (1-30, default 7) |
Example Request
{
"tool": "get_schedule",
"arguments": {
"team": "BUF",
"days": 7
}
}Example Response
{
"games": [
{
"id": "2025021143",
"gameDate": "2026-04-10",
"startTime": "19:00:00",
"homeTeam": "BUF",
"awayTeam": "TOR",
"homeTeamName": "Buffalo Sabres",
"awayTeamName": "Toronto Maple Leafs",
"venue": "KeyBank Center",
"gameType": "regular"
},
{
"id": "2025021187",
"gameDate": "2026-04-12",
"startTime": "19:30:00",
"homeTeam": "FLA",
"awayTeam": "BUF",
"homeTeamName": "Florida Panthers",
"awayTeamName": "Buffalo Sabres",
"venue": "Amerant Bank Arena",
"gameType": "regular"
}
]
}Response Fields
| Field | Type | Description |
|---|---|---|
| games[].id | string | Unique game ID |
| games[].gameDate | string | Date (YYYY-MM-DD) |
| games[].startTime | string | Start time (HH:MM:SS) |
| games[].homeTeam | string | Home team abbreviation |
| games[].awayTeam | string | Away team abbreviation |
| games[].venue | string | Arena name |
| games[].gameType | string | regular, playoff, or preseason |
search_players
2 creditsFind players by name, team, or position.
When to use: You need a player's ID to look up their stats, or you want to search a roster by position. For example, "find all active centers on the Sabres" or "look up Tage Thompson's player ID."
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| query | string | required | Search string (e.g. "Tage Thompson") |
| team | string | optional | Filter by team abbreviation |
| position | string | optional | G, D, C, LW, or RW |
| active | boolean | optional | Active players only (default true) |
| limit | integer | optional | Max results (1-50, default 10) |
Example Request
{
"tool": "search_players",
"arguments": {
"query": "Thompson",
"team": "BUF"
}
}Example Response
{
"players": [
{
"id": 8480208,
"name": "Tage Thompson",
"team": "BUF",
"teamName": "Buffalo Sabres",
"position": "C",
"jerseyNumber": 72,
"active": true
}
]
}Response Fields
| Field | Type | Description |
|---|---|---|
| players[].id | integer | Player ID (use in get_player_stats) |
| players[].name | string | Full name |
| players[].team | string | Team abbreviation |
| players[].position | string | Position code |
| players[].jerseyNumber | integer | Jersey number |
| players[].active | boolean | Currently active |
get_standings
2 creditsCurrent standings with advanced stats (Corsi%, Fenwick%, point pace).
When to use: You want to see how teams rank in the league right now -- points, win percentage, goal differential, and advanced possession stats like Corsi and Fenwick.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| conference | string | optional | "Eastern" or "Western" |
| division | string | optional | Division name (e.g. "Atlantic") |
| season | string | optional | Season (e.g. "20252026", default current) |
Example Request
{
"tool": "get_standings",
"arguments": {
"conference": "Eastern",
"division": "Atlantic"
}
}Example Response
{
"standings": [
{
"rank": 1,
"team": "FLA",
"teamName": "Florida Panthers",
"points": 112,
"wins": 52,
"losses": 20,
"otLosses": 8,
"pointPct": 0.683,
"goalDiff": 48,
"corsiPct": 53.2,
"fenwickPct": 52.8
},
{
"rank": 2,
"team": "TOR",
"teamName": "Toronto Maple Leafs",
"points": 106,
"wins": 49,
"losses": 23,
"otLosses": 8,
"pointPct": 0.65,
"goalDiff": 31,
"corsiPct": 51.4,
"fenwickPct": 51.1
},
{
"rank": 3,
"team": "BUF",
"teamName": "Buffalo Sabres",
"points": 98,
"wins": 44,
"losses": 28,
"otLosses": 10,
"pointPct": 0.598,
"goalDiff": 12,
"corsiPct": 50.1,
"fenwickPct": 49.8
}
]
}Response Fields
| Field | Type | Description |
|---|---|---|
| standings[].rank | integer | League or division rank |
| standings[].team | string | Team abbreviation |
| standings[].points | integer | Total points |
| standings[].wins | integer | Wins |
| standings[].losses | integer | Losses |
| standings[].otLosses | integer | Overtime losses |
| standings[].goalDiff | integer | Goal differential |
| standings[].corsiPct | number | Corsi percentage (5v5 shot attempts) |
| standings[].fenwickPct | number | Fenwick percentage (unblocked shots) |
Stats + aggregation
5 credits/callget_games
5 creditsQuery games by date range, team, season, game state, or type.
When to use: You're building a dataset for analysis -- pull all games from a date range, all playoff games for a team, or all final scores from last season. This is the workhorse endpoint for model training data.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| date_from | string | optional | Start date (YYYY-MM-DD) |
| date_to | string | optional | End date (YYYY-MM-DD) |
| team | string | optional | Team abbreviation |
| season | string | optional | Season (e.g. "20252026") |
| game_state | string | optional | FUT, LIVE, FINAL, or OFF |
| game_type | string | optional | regular, playoff, or preseason |
| limit | integer | optional | Max results (1-500, default 100) |
Example Request
{
"tool": "get_games",
"arguments": {
"team": "TOR",
"date_from": "2026-03-01",
"date_to": "2026-03-07",
"game_state": "FINAL"
}
}Example Response
{
"games": [
{
"id": "2025020934",
"season": "20252026",
"gameDate": "2026-03-02",
"startTime": "19:00:00",
"homeTeam": "TOR",
"awayTeam": "BOS",
"homeScore": 4,
"awayScore": 2,
"gameState": "FINAL",
"gameType": "regular",
"venue": "Scotiabank Arena"
},
{
"id": "2025020958",
"season": "20252026",
"gameDate": "2026-03-05",
"startTime": "19:30:00",
"homeTeam": "MTL",
"awayTeam": "TOR",
"homeScore": 1,
"awayScore": 3,
"gameState": "FINAL",
"gameType": "regular",
"venue": "Bell Centre"
}
]
}Response Fields
| Field | Type | Description |
|---|---|---|
| games[].id | string | Unique game ID |
| games[].season | string | Season code (e.g. "20252026") |
| games[].gameDate | string | Date (YYYY-MM-DD) |
| games[].homeTeam | string | Home team abbreviation |
| games[].awayTeam | string | Away team abbreviation |
| games[].homeScore | integer | Home team score (FINAL only) |
| games[].awayScore | integer | Away team score (FINAL only) |
| games[].gameState | string | FUT, LIVE, FINAL, or OFF |
| games[].venue | string | Arena name |
get_team_stats
5 creditsDetailed stats for a specific team including advanced metrics and league rankings.
When to use: You want a complete statistical profile of a team -- goals, power play, penalty kill, Corsi, Fenwick, expected goals, plus where they rank in the league on each metric.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| team | string | required | Team abbreviation (e.g. "BUF") |
| season | string | optional | Season (e.g. "20252026") |
Example Request
{
"tool": "get_team_stats",
"arguments": {
"team": "BUF",
"season": "20252026"
}
}Example Response
{
"team": {
"abbrev": "BUF",
"name": "Sabres",
"city": "Buffalo",
"conference": "Eastern",
"division": "Atlantic"
},
"current_stats": {
"gamesPlayed": 82,
"wins": 44,
"losses": 28,
"otLosses": 10,
"points": 98,
"goalsFor": 268,
"goalsAgainst": 256,
"powerPlayPct": 22.4,
"penaltyKillPct": 79.8,
"corsiPct": 50.1,
"fenwickPct": 49.8,
"xGoalsFor": 261.3,
"xGoalsAgainst": 258.7
},
"rankings": {
"overall": 14,
"powerPlay": 10,
"penaltyKill": 20,
"goalsFor": 12,
"goalsAgainst": 18
}
}Response Fields
| Field | Type | Description |
|---|---|---|
| team.abbrev | string | Team abbreviation |
| current_stats.gamesPlayed | integer | Games played |
| current_stats.wins | integer | Wins |
| current_stats.points | integer | Total points |
| current_stats.powerPlayPct | number | Power play percentage |
| current_stats.penaltyKillPct | number | Penalty kill percentage |
| current_stats.corsiPct | number | Corsi percentage |
| current_stats.xGoalsFor | number | Expected goals for |
| rankings.overall | integer | League rank |
| rankings.powerPlay | integer | PP rank |
get_player_stats
5 creditsFull season stats for a skater including bio information.
When to use: You have a player ID (from search_players) and want their full stat line -- goals, assists, points, shooting percentage, ice time, power play production, and biographical info.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| player_id | integer | required | Player ID (from search_players) |
Example Request
{
"tool": "get_player_stats",
"arguments": {
"player_id": 8480208
}
}Example Response
{
"player": {
"id": 8480208,
"name": "Tage Thompson",
"team": "BUF",
"teamName": "Buffalo Sabres",
"position": "C",
"jerseyNumber": 72,
"birthDate": "1997-10-30",
"birthCity": "Phoenix",
"birthCountry": "USA",
"height": "6'7\"",
"weight": 225
},
"stats": {
"season": "20252026",
"gamesPlayed": 78,
"goals": 41,
"assists": 37,
"points": 78,
"plusMinus": 12,
"pim": 28,
"shots": 298,
"shootingPct": 13.8,
"timeOnIce": "20:14",
"powerPlayGoals": 14,
"shortHandedGoals": 1,
"gameWinningGoals": 8,
"faceoffPct": 51.2
}
}Response Fields
| Field | Type | Description |
|---|---|---|
| player.id | integer | Player ID |
| player.name | string | Full name |
| player.position | string | Position |
| player.height | string | Height |
| player.weight | integer | Weight (lbs) |
| stats.gamesPlayed | integer | Games played |
| stats.goals | integer | Goals |
| stats.assists | integer | Assists |
| stats.points | integer | Points |
| stats.shootingPct | number | Shooting percentage |
| stats.timeOnIce | string | Average TOI (MM:SS) |
get_goalie_stats
5 creditsGoalie leaderboard sorted by save%, GAA, GSAX, or wins.
When to use: You want to rank goalies across the league or within a team. GSAX (goals saved above expected) is the advanced metric that tells you who's actually good versus who plays behind a strong defense.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| team | string | optional | Filter by team abbreviation |
| season | string | optional | Season (e.g. "20252026") |
| min_games | integer | optional | Minimum games played (default 10) |
| sort_by | string | optional | save_pct, gaa, gsax, or wins |
| limit | integer | optional | Max results (1-50, default 20) |
Example Request
{
"tool": "get_goalie_stats",
"arguments": {
"sort_by": "gsax",
"limit": 3
}
}Example Response
{
"goalies": [
{
"playerId": 8479361,
"playerName": "Connor Hellebuyck",
"team": "WPG",
"gamesPlayed": 64,
"wins": 38,
"losses": 18,
"savePct": 0.924,
"gaa": 2.31,
"gsax": 24.7,
"shutouts": 5
},
{
"playerId": 8477424,
"playerName": "Sergei Bobrovsky",
"team": "FLA",
"gamesPlayed": 55,
"wins": 34,
"losses": 14,
"savePct": 0.918,
"gaa": 2.48,
"gsax": 18.3,
"shutouts": 4
},
{
"playerId": 8480045,
"playerName": "Ukko-Pekka Luukkonen",
"team": "BUF",
"gamesPlayed": 58,
"wins": 30,
"losses": 20,
"savePct": 0.912,
"gaa": 2.71,
"gsax": 12.1,
"shutouts": 3
}
]
}Response Fields
| Field | Type | Description |
|---|---|---|
| goalies[].playerId | integer | Player ID |
| goalies[].playerName | string | Goalie name |
| goalies[].team | string | Team abbreviation |
| goalies[].gamesPlayed | integer | Games played |
| goalies[].wins | integer | Wins |
| goalies[].savePct | number | Save percentage |
| goalies[].gaa | number | Goals against average |
| goalies[].gsax | number | Goals saved above expected |
| goalies[].shutouts | integer | Shutouts |
Multi-table
10 credits/callget_odds
10 creditsOdds for a specific game with optional bookmaker and snapshot type filters.
When to use: You have a game ID and want the betting lines -- moneyline, spread, and total from specific bookmakers. Filter by opening, current, or closing lines to see how the market priced the game.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| game_id | string | required | Game ID (e.g. "2025020887") |
| bookmaker | string | optional | Filter by bookmaker (e.g. "draftkings") |
| snapshot_type | string | optional | opening, current, or closing |
Example Request
{
"tool": "get_odds",
"arguments": {
"game_id": "2025020887",
"bookmaker": "draftkings"
}
}Example Response
{
"odds": [
{
"bookmaker": "draftkings",
"snapshotType": "opening",
"mlHome": -115,
"mlAway": -105,
"spreadHome": -1.5,
"spreadAway": 1.5,
"total": 6,
"overPrice": -110,
"underPrice": -110,
"capturedAt": "2026-03-14T12:00:00Z"
},
{
"bookmaker": "draftkings",
"snapshotType": "closing",
"mlHome": -125,
"mlAway": 105,
"spreadHome": -1.5,
"spreadAway": 1.5,
"total": 6.5,
"overPrice": -110,
"underPrice": -110,
"capturedAt": "2026-03-15T18:50:00Z"
}
]
}Response Fields
| Field | Type | Description |
|---|---|---|
| odds[].bookmaker | string | Bookmaker name |
| odds[].snapshotType | string | opening, current, or closing |
| odds[].mlHome | integer | Home moneyline |
| odds[].mlAway | integer | Away moneyline |
| odds[].spreadHome | number | Home puck line |
| odds[].total | number | Over/under total |
| odds[].overPrice | integer | Over price |
| odds[].underPrice | integer | Under price |
| odds[].capturedAt | string | Timestamp of snapshot |
get_game_detail
10 creditsFull game detail including odds snapshots and confirmed goalie starts.
When to use: You want everything about a single game in one call -- scores, odds from multiple bookmakers, and confirmed goalie starts. Saves you from making separate get_odds and search_players calls.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| game_id | string | required | Game ID (e.g. "2025020887") |
Example Request
{
"tool": "get_game_detail",
"arguments": {
"game_id": "2025020887"
}
}Example Response
{
"game": {
"id": "2025020887",
"season": "20252026",
"gameDate": "2026-03-15",
"startTime": "19:00:00",
"gameState": "FINAL",
"gameType": "regular",
"venue": "KeyBank Center"
},
"home_team": {
"abbrev": "BUF",
"name": "Buffalo Sabres",
"score": 5
},
"away_team": {
"abbrev": "TOR",
"name": "Toronto Maple Leafs",
"score": 3
},
"odds": [
{
"bookmaker": "draftkings",
"mlHome": -125,
"mlAway": 105,
"spreadHome": -1.5,
"total": 6.5,
"snapshotType": "closing"
},
{
"bookmaker": "fanduel",
"mlHome": -130,
"mlAway": 110,
"spreadHome": -1.5,
"total": 6.5,
"snapshotType": "closing"
}
],
"goalie_starts": {
"home": {
"id": 8480045,
"name": "Ukko-Pekka Luukkonen",
"confirmed": true
},
"away": {
"id": 8479361,
"name": "Joseph Woll",
"confirmed": true
}
}
}Response Fields
| Field | Type | Description |
|---|---|---|
| game.id | string | Game ID |
| game.gameDate | string | Date |
| game.gameState | string | FUT, LIVE, FINAL, or OFF |
| home_team.abbrev | string | Home team abbreviation |
| home_team.score | integer | Home team score |
| away_team.abbrev | string | Away team abbreviation |
| away_team.score | integer | Away team score |
| odds[] | array | Odds from all bookmakers |
| goalie_starts.home | object | Home goalie (id, name, confirmed) |
| goalie_starts.away | object | Away goalie (id, name, confirmed) |
get_head_to_head
10 creditsMatchup history between two teams with win/loss records.
When to use: You're previewing a matchup and want to know the rivalry history -- who has the edge, recent results, and head-to-head record for a specific season or all-time.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| team1 | string | required | First team abbreviation |
| team2 | string | required | Second team abbreviation |
| season | string | optional | Filter to specific season |
| limit | integer | optional | Max games (1-100, default 20) |
Example Request
{
"tool": "get_head_to_head",
"arguments": {
"team1": "BUF",
"team2": "TOR",
"season": "20252026"
}
}Example Response
{
"record": {
"team1": "BUF",
"team2": "TOR",
"team1_wins": 2,
"team2_wins": 1,
"ties": 0
},
"total_games": 3,
"games": [
{
"id": "2025020412",
"gameDate": "2025-12-14",
"homeTeam": "TOR",
"awayTeam": "BUF",
"homeScore": 3,
"awayScore": 4,
"venue": "Scotiabank Arena"
},
{
"id": "2025020623",
"gameDate": "2026-01-28",
"homeTeam": "BUF",
"awayTeam": "TOR",
"homeScore": 2,
"awayScore": 5,
"venue": "KeyBank Center"
}
]
}Response Fields
| Field | Type | Description |
|---|---|---|
| record.team1_wins | integer | Team 1 wins |
| record.team2_wins | integer | Team 2 wins |
| total_games | integer | Total games in range |
| games[].id | string | Game ID |
| games[].gameDate | string | Date |
| games[].homeScore | integer | Home team score |
| games[].awayScore | integer | Away team score |
Time-series
25 credits/callget_line_movement
25 creditsTime-series odds snapshots grouped by bookmaker. Shows how lines moved from open to close.
When to use: You're analyzing sharp money or building a betting model and need to see how odds changed over time. Opening vs. closing line differences reveal where the market moved, which is the strongest signal in sports betting.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| game_id | string | required | Game ID (e.g. "2025020887") |
| bookmaker | string | optional | Filter to one bookmaker |
Example Request
{
"tool": "get_line_movement",
"arguments": {
"game_id": "2025020887",
"bookmaker": "draftkings"
}
}Example Response
{
"game_id": "2025020887",
"movement": {
"draftkings": {
"snapshots": [
{
"snapshotType": "opening",
"mlHome": -115,
"mlAway": -105,
"spreadHome": -1.5,
"total": 6,
"capturedAt": "2026-03-14T12:00:00Z"
},
{
"snapshotType": "current",
"mlHome": -120,
"mlAway": 100,
"spreadHome": -1.5,
"total": 6.5,
"capturedAt": "2026-03-15T14:30:00Z"
},
{
"snapshotType": "closing",
"mlHome": -125,
"mlAway": 105,
"spreadHome": -1.5,
"total": 6.5,
"capturedAt": "2026-03-15T18:50:00Z"
}
],
"summary": {
"mlHome_open": -115,
"mlHome_close": -125,
"total_open": 6,
"total_close": 6.5
}
}
}
}Response Fields
| Field | Type | Description |
|---|---|---|
| game_id | string | Game ID |
| movement[book].snapshots[] | array | Chronological odds snapshots |
| movement[book].snapshots[].snapshotType | string | opening, current, or closing |
| movement[book].snapshots[].mlHome | integer | Home moneyline at this snapshot |
| movement[book].snapshots[].total | number | Over/under at this snapshot |
| movement[book].snapshots[].capturedAt | string | Timestamp |
| movement[book].summary | object | Open vs close comparison |