Algobook
- The developer's handbook
mode-switch
back-button
Buy Me A Coffee
Sat Apr 01 2023

Using NHL API to get team and player stats

Well, I am a hockey freak. I loved the game since I was a little boy, and before I entered the world of ones and zeros, I was dreaming of becoming the next Peter Forsberg. In one of my past side project, I was creating a hockey application where I consumed a lot of data from NHL and presented it in a React application. So I thought I would share some of my past experience with their API. And maybe there are someone out there, searching the good old internet for some information of what hockey data one can use - well, you found a good place to start my friend!

Want to use a free NHL API in your project? Check out our free NHL API.

Setting up our NodeJs project

Let's start with the basics

mkdir nhl-api cd nhl-api npm init npm install express axios

Let's create our structure of the project

  • Read more here on how to structure routers in NodeJs.

file structure

Let's start with some code

index.js

const express = require("express"); const { mainRouter } = require("./routers/mainRouter"); const app = express(); app.use("/api", mainRouter); app.listen(3001, function () { console.log("Web server listening on port 3001"); });

mainRouter.js

const express = require("express"); const { playerRouter } = require("./playerRouter/playerRouter"); const { scheduleRouter } = require("./scheduleRouter/scheduleRouter"); const { teamRouter } = require("./teamRouter/teamRouter"); const mainRouter = express.Router({ mergeParams: true }); mainRouter.use("/players", playerRouter); mainRouter.use("/schedule", scheduleRouter); mainRouter.use("/teams", teamRouter); module.exports = { mainRouter };

Cool. Now we have the structure set up. It is now time to create our logic to get NHL data.

Player info

  • create playerRouter.js in playerRouter folder

And now we will create two endpoints. One for getting player information, and one for getting a big chunk of stats data as well.

const express = require("express"); const axios = require("axios"); const playerRouter = express.Router({ mergeParams: true }); const BASE_URL = "https://statsapi.web.nhl.com/api/v1/people"; playerRouter.get("/:id", async (req, res) => { const response = await axios.get(`${BASE_URL}/${req.params.id}`); res.send(response.data.people[0]); }); playerRouter.get("/:id/stats", async (req, res) => { const response = await axios.get( `${BASE_URL}/${req.params.id}?expand=person.stats&stats=yearByYear,yearByYearPlayoffs,careerRegularSeason&expand=stats.team&site=en_nhlNR` ); res.send(response.data.people[0]); }); module.exports = { playerRouter };

Let's try it out

GET localhost:3001/api/players/8480069

Response

{ "id": 8480069, "fullName": "Cale Makar", "link": "/api/v1/people/8480069", "firstName": "Cale", "lastName": "Makar", "primaryNumber": "8", "birthDate": "1998-10-30", "currentAge": 24, "birthCity": "Calgary", "birthStateProvince": "AB", "birthCountry": "CAN", "nationality": "CAN", "height": "5' 11\"", "weight": 187, "active": true, "alternateCaptain": false, "captain": false, "rookie": false, "shootsCatches": "R", "rosterStatus": "Y", "currentTeam": { "id": 21, "name": "Colorado Avalanche", "link": "/api/v1/teams/21" }, "primaryPosition": { "code": "D", "name": "Defenseman", "type": "Defenseman", "abbreviation": "D" } }

And if we try out our stats endpoint, it will look like this.

GET localhost:3001/api/players/8480069/stats

Response

NOTE: This is just a very small part of the data, it is very large amount data that they return so I have stripped it down in this example snippet

{ "id": 8480069, "fullName": "Cale Makar", "link": "/api/v1/people/8480069", "firstName": "Cale", "lastName": "Makar", "primaryNumber": "8", "birthDate": "1998-10-30", "currentAge": 24, "birthCity": "Calgary", "birthStateProvince": "AB", "birthCountry": "CAN", "nationality": "CAN", "height": "5' 11\"", "weight": 187, "active": true, "alternateCaptain": false, "captain": false, "rookie": false, "shootsCatches": "R", "rosterStatus": "Y", "currentTeam": { "id": 21, "name": "Colorado Avalanche", "link": "/api/v1/teams/21" }, "primaryPosition": { "code": "D", "name": "Defenseman", "type": "Defenseman", "abbreviation": "D" }, "stats": { "timeOnIce": "5803:07", "assists": 181, "goals": 65, "pim": 80, "shots": 640, "games": 237, "hits": 253, "powerPlayGoals": 22, "powerPlayPoints": 105, "powerPlayTimeOnIce": "929:45", "evenTimeOnIce": "4557:57", "penaltyMinutes": "80", "shotPct": 10.2, "gameWinningGoals": 19, "overTimeGoals": 5, "shortHandedGoals": 0, "shortHandedPoints": 0, "shortHandedTimeOnIce": "315:25", "blocked": 271, "plusMinus": 90, "points": 246, "shifts": 6308, "timeOnIcePerGame": "24:29", "evenTimeOnIcePerGame": "19:13", "shortHandedTimeOnIcePerGame": "01:19", "powerPlayTimeOnIcePerGame": "03:55" } }

Schedule

  • create scheduleRouter.js in scheduleRouter folder

Let's add our endpoint for getting the schedule. We will allow it to accept two params, one for startDate and one for endDate.

const express = require("express"); const axios = require("axios"); const scheduleRouter = express.Router({ mergeParams: true }); const BASE_URL = "https://statsapi.web.nhl.com/api/v1/schedule"; scheduleRouter.get("/:startDate/:endDate", async (req, res) => { const { startDate, endDate } = req.params; const response = await axios.get( `${BASE_URL}/?startDate=${startDate}&endDate=${endDate}` ); res.send(response.data.dates); }); module.exports = { scheduleRouter };

Let's try it

I will request for one day of data here, and as with the state, I will strip the data since it is a lot of json otherwise.

GET localhost:3001/api/schedule/2023-03-30/2023-03-30

Response

[ { "date": "2023-03-30", "totalItems": 11, "totalEvents": 0, "totalGames": 11, "totalMatches": 0, "games": [ { "gamePk": 2022021189, "link": "/api/v1/game/2022021189/feed/live", "gameType": "R", "season": "20222023", "gameDate": "2023-03-30T23:00:00Z", "status": { "abstractGameState": "Final", "codedGameState": "7", "detailedState": "Final", "statusCode": "7", "startTimeTBD": false }, "teams": { "away": { "leagueRecord": { "wins": 23, "losses": 43, "ot": 8, "type": "league" }, "score": 1, "team": { "id": 29, "name": "Columbus Blue Jackets", "link": "/api/v1/teams/29" } }, "home": { "leagueRecord": { "wins": 58, "losses": 12, "ot": 5, "type": "league" }, "score": 2, "team": { "id": 6, "name": "Boston Bruins", "link": "/api/v1/teams/6" } } }, "venue": { "id": 5085, "name": "TD Garden", "link": "/api/v1/venues/5085" }, "content": { "link": "/api/v1/game/2022021189/content" } } ], "events": [], "matches": [] } ]

So in this example, it was Boston vs Columbus. And Boston won with 2-1. There is possibility to retrieve data very far back if you are interested in historical data of results. And you can also specify a date in the future to get upcoming games.

Teams

  • Create teamRouter.js in teamRouter folder

And now, we will create three endpoints. One for getting all teams, one for getting data on a specific team and one for getting the roster of a team.

const express = require("express"); const axios = require("axios"); const teamRouter = express.Router({ mergeParams: true }); const BASE_URL = "https://statsapi.web.nhl.com/api/v1/teams"; teamRouter.get("/", async (req, res) => { const response = await axios.get(BASE_URL); res.send(response.data); }); teamRouter.get("/:id", async (req, res) => { const response = await axios.get(`${BASE_URL}/${req.params.id}`); res.send(response.data); }); teamRouter.get("/:id/roster", async (req, res) => { const response = await axios.get(`${BASE_URL}/${req.params.id}/roster`); res.send(response.data.roster); }); module.exports = { teamRouter };

Let's try it out

GET localhost:3001/api/teams

Response

NOTE: I will shorten it down a bit here as well..

"teams": [ { "id": 1, "name": "New Jersey Devils", "link": "/api/v1/teams/1", "venue": { "name": "Prudential Center", "link": "/api/v1/venues/null", "city": "Newark", "timeZone": { "id": "America/New_York", "offset": -4, "tz": "EDT" } }, "abbreviation": "NJD", "teamName": "Devils", "locationName": "New Jersey", "firstYearOfPlay": "1982", "division": { "id": 18, "name": "Metropolitan", "nameShort": "Metro", "link": "/api/v1/divisions/18", "abbreviation": "M" }, "conference": { "id": 6, "name": "Eastern", "link": "/api/v1/conferences/6" }, "franchise": { "franchiseId": 23, "teamName": "Devils", "link": "/api/v1/franchises/23" }, "shortName": "New Jersey", "officialSiteUrl": "http://www.newjerseydevils.com/", "franchiseId": 23, "active": true }, { "id": 2, "name": "New York Islanders", "link": "/api/v1/teams/2", "venue": { "name": "UBS Arena", "link": "/api/v1/venues/null", "city": "Elmont", "timeZone": { "id": "America/New_York", "offset": -4, "tz": "EDT" } }, "abbreviation": "NYI", "teamName": "Islanders", "locationName": "New York", "firstYearOfPlay": "1972", "division": { "id": 18, "name": "Metropolitan", "nameShort": "Metro", "link": "/api/v1/divisions/18", "abbreviation": "M" }, "conference": { "id": 6, "name": "Eastern", "link": "/api/v1/conferences/6" }, "franchise": { "franchiseId": 22, "teamName": "Islanders", "link": "/api/v1/franchises/22" }, "shortName": "NY Islanders", "officialSiteUrl": "http://www.newyorkislanders.com/", "franchiseId": 22, "active": true }, ...

Getting only one team

GET localhost:3001/api/teams/21

Response

{ "teams": [ { "id": 21, "name": "Colorado Avalanche", "link": "/api/v1/teams/21", "venue": { "id": 5064, "name": "Ball Arena", "link": "/api/v1/venues/5064", "city": "Denver", "timeZone": { "id": "America/Denver", "offset": -6, "tz": "MDT" } }, "abbreviation": "COL", "teamName": "Avalanche", "locationName": "Colorado", "firstYearOfPlay": "1979", "division": { "id": 16, "name": "Central", "nameShort": "CEN", "link": "/api/v1/divisions/16", "abbreviation": "C" }, "conference": { "id": 5, "name": "Western", "link": "/api/v1/conferences/5" }, "franchise": { "franchiseId": 27, "teamName": "Avalanche", "link": "/api/v1/franchises/27" }, "shortName": "Colorado", "officialSiteUrl": "http://www.coloradoavalanche.com/", "franchiseId": 27, "active": true } ] }

Getting the roster of a specific team

GET localhost:3001/api/teams/21/roster

Response

[ { "person": { "id": 8476234, "fullName": "Keith Kinkaid", "link": "/api/v1/people/8476234" }, "jerseyNumber": "30", "position": { "code": "G", "name": "Goalie", "type": "Goalie", "abbreviation": "G" } }, { "person": { "id": 8476948, "fullName": "Charles Hudon", "link": "/api/v1/people/8476948" }, "jerseyNumber": "54", "position": { "code": "L", "name": "Left Wing", "type": "Forward", "abbreviation": "LW" } }, .... ]

Outro

In this guide we explored the NHL API and built a small API for consuming parts of it. We focused on three parts - team data, schedule and player data. In this guide we are only touching the surface. NHL are keeping a lot of data and stats available that people like us can consume and use.

I hope you enjoyed this one, and as always, any feedback is welcome!

signatureSat Apr 01 2023
See all our articles