{"id":18268398,"url":"https://github.com/coreyjs/nhl-api-py","last_synced_at":"2025-04-05T16:03:23.856Z","repository":{"id":171762974,"uuid":"648241468","full_name":"coreyjs/nhl-api-py","owner":"coreyjs","description":"NHL API (2024 Updated) - For accessing most of the NHL EDGE statistical API's, scores, schedules and more","archived":false,"fork":false,"pushed_at":"2025-03-29T01:06:07.000Z","size":235,"stargazers_count":55,"open_issues_count":4,"forks_count":10,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-03-29T15:02:57.837Z","etag":null,"topics":["gamecenter","hacktoberfest","nhl","nhl-ai","nhl-api","nhl-data","nhl-edge","nhl-machine-learning","nhl-scores","nhl-stats","sports-analytics","sports-stats"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/coreyjs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"license.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":null,"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"lfx_crowdfunding":null,"polar":null,"buy_me_a_coffee":"coreyjs","thanks_dev":null,"custom":null}},"created_at":"2023-06-01T14:13:41.000Z","updated_at":"2025-03-25T12:15:30.000Z","dependencies_parsed_at":"2023-07-25T21:32:30.443Z","dependency_job_id":"96c2a8b8-056e-48d4-92f3-faeca3f8350d","html_url":"https://github.com/coreyjs/nhl-api-py","commit_stats":null,"previous_names":["coreyjs/nhl-api-py"],"tags_count":33,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coreyjs%2Fnhl-api-py","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coreyjs%2Fnhl-api-py/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coreyjs%2Fnhl-api-py/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coreyjs%2Fnhl-api-py/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/coreyjs","download_url":"https://codeload.github.com/coreyjs/nhl-api-py/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247361600,"owners_count":20926641,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["gamecenter","hacktoberfest","nhl","nhl-ai","nhl-api","nhl-data","nhl-edge","nhl-machine-learning","nhl-scores","nhl-stats","sports-analytics","sports-stats"],"created_at":"2024-11-05T11:31:19.363Z","updated_at":"2025-04-05T16:03:23.831Z","avatar_url":"https://github.com/coreyjs.png","language":"Python","funding_links":["https://buymeacoffee.com/coreyjs"],"categories":[],"sub_categories":[],"readme":"[![PyPI version](https://badge.fury.io/py/nhl-api-py.svg)](https://badge.fury.io/py/nhl-api-py)\n![nhl-api-py workflow](https://github.com/coreyjs/nhl-api-py/actions/workflows/python-app.yml/badge.svg?branch=main)\n\n# NHL API \u0026 NHL Edge Stats\n\n\n## About\n\nNHL-api-py is a Python package that provides a simple wrapper around the \nNHL API, allowing you to easily access and retrieve NHL data in your Python \napplications.\n\n\nNote:  I created this to help me with some machine learning\nprojects around the NHL and the NHL data sets.  Special thanks to https://github.com/erunion/sport-api-specifications/tree/master/nhl,\nhttps://gitlab.com/dword4/nhlapi/-/blob/master/stats-api.md and https://github.com/Zmalski/NHL-API-Reference.\n\n\n### Developer Note: This is being updated with the new, also undocumented, NHL API.  \n\nAs of 10/5/24 I seem to have a majority of the endpoints added from what I can tell, but every once and awhile I come across one that needs to be added/changed.  These will most likely be a minor ver bump.\n\nIf you find any, open a ticket or post in the discussions tab.   I would love to hear more.\n\n\n---\n# Contact\n\nIm available on [Bluesky](https://bsky.app/profile/coreyjs.dev) for any questions or just general chats about enhancements.\n\n---\n\n# Wiki\n\nMore in depth examples can be found in the wiki, feel free to add more: [Examples](https://github.com/coreyjs/nhl-api-py/wiki/Example-Use-Cases)\n\n---\n\n# Usage\n\n```bash\npip install nhl-api-py\n```\n\n```python\nfrom nhlpy import NHLClient\n\nclient = NHLClient()\n# Fore more verbose logging\nclient = NHLClient(verbose=True)\n# OR Other available configurations:\nclient = NHLClient(verbose={bool}, timeout={int}, ssl_verify={bool}, follow_redirects={bool})\n```\n---\n## Stats with QueryBuilder\n\nThe skater stats endpoint can be accessed using the new query builder.  It should make\ncreating and understanding the queries a bit easier.  Filters are being added as I go, and will match up\nto what the NHL API will allow.\n\nThe idea is to easily, and programatically, build up more complex queries using the query filters.  A quick example below:\n```python\nfilters = [\n    GameTypeQuery(game_type=\"2\"),\n    DraftQuery(year=\"2020\", draft_round=\"2\"),\n    SeasonQuery(season_start=\"20202021\", season_end=\"20232024\"),\n    PositionQuery(position=PositionTypes.ALL_FORWARDS)\n]\n```\n\n\n\n### Sorting\nThe sorting is a list of dictionaries similar to below.  You can supply your own, otherwise it will\ndefault to the default sort properties that the stat dashboard uses.  All sorting defaults are found\nin the `nhl-api-py/nhlpy/api/query/sorting/sorting_options.py` file.\n\n\u003cdetails\u003e\n\u003csummary\u003eDefault Sorting\u003c/summary\u003e\n\n```python\nskater_summary_default_sorting = [\n    {\"property\": \"points\", \"direction\": \"DESC\"},\n    {\"property\": \"gamesPlayed\", \"direction\": \"ASC\"},\n    {\"property\": \"playerId\", \"direction\": \"ASC\"},\n]\n```\n\u003c/details\u003e\n\n---\n\n### Report Types\nThe following report types are available.  These are used to build the request url.  So `/summary`, `/bios`, etc.\n\n```bash\nsummary\nbios\nfaceoffpercentages\nfaceoffwins\ngoalsForAgainst\nrealtime\npenalties\npenaltykill\npenaltyShots\npowerplay\npuckPossessions\nsummaryshooting\npercentages\nscoringRates\nscoringpergame\nshootout\nshottype\ntimeonice\n```\n\n### Available Filters\n\n```python\nfrom nhlpy.api.query.filters.franchise import FranchiseQuery\nfrom nhlpy.api.query.filters.shoot_catch import ShootCatchesQuery\nfrom nhlpy.api.query.filters.draft import DraftQuery\nfrom nhlpy.api.query.filters.season import SeasonQuery\nfrom nhlpy.api.query.filters.game_type import GameTypeQuery\nfrom nhlpy.api.query.filters.position import PositionQuery, PositionTypes\nfrom nhlpy.api.query.filters.status import StatusQuery\nfrom nhlpy.api.query.filters.opponent import OpponentQuery\nfrom nhlpy.api.query.filters.home_road import HomeRoadQuery\nfrom nhlpy.api.query.filters.experience import ExperienceQuery\nfrom nhlpy.api.query.filters.decision import DecisionQuery\n\nfilters = [\n    GameTypeQuery(game_type=\"2\"),\n    DraftQuery(year=\"2020\", draft_round=\"2\"),\n    SeasonQuery(season_start=\"20202021\", season_end=\"20232024\"),\n    PositionQuery(position=PositionTypes.ALL_FORWARDS),\n    ShootCatchesQuery(shoot_catch=\"L\"),\n    HomeRoadQuery(home_road=\"H\"),\n    FranchiseQuery(franchise_id=\"1\"),\n    StatusQuery(is_active=True),#for active players OR for HOF players StatusQuery(is_hall_of_fame=True),\n    OpponentQuery(opponent_franchise_id=\"2\"),\n    ExperienceQuery(is_rookie=True), # for rookies || ExperienceQuery(is_rookie=False) #for veteran\n    DecisionQuery(decision=\"W\") # OR DecisionQuery(decision=\"L\") OR DecisionQuery(decision=\"O\")\n]\n```\n\n\n### Example\n```python\nfrom nhlpy.api.query.builder import QueryBuilder, QueryContext\nfrom nhlpy.nhl_client import NHLClient\nfrom nhlpy.api.query.filters.draft import DraftQuery\nfrom nhlpy.api.query.filters.season import SeasonQuery\nfrom nhlpy.api.query.filters.game_type import GameTypeQuery\nfrom nhlpy.api.query.filters.position import PositionQuery, PositionTypes\n\nclient = NHLClient(verbose=True)\n\nfilters = [\n    GameTypeQuery(game_type=\"2\"),\n    DraftQuery(year=\"2020\", draft_round=\"2\"),\n    SeasonQuery(season_start=\"20202021\", season_end=\"20232024\"),\n    PositionQuery(position=PositionTypes.ALL_FORWARDS)\n]\n\nquery_builder = QueryBuilder()\nquery_context: QueryContext = query_builder.build(filters=filters)\n\ndata = client.stats.skater_stats_with_query_context(\n    report_type='summary',\n    query_context=query_context,\n    aggregate=True\n)\n```\n\n### Granular Filtering\nEach API request uses an additional query parameter called `factCayenneExp`.  This defaults to `gamesPlayed\u003e=1`\nbut can be overridden by setting the `fact_query` parameter in the `QueryContextObject` object.  These can\nbe combined together with `and` to create a more complex query.  It supports `\u003e`, `\u003c`, `\u003e=`, `\u003c=`.  For example: `shootingPct\u003e=0.01 and timeOnIcePerGame\u003e=60 and faceoffWinPct\u003e=0.01 and shots\u003e=1`\n\n\nThis should support the following filters:\n\n- `gamesPlayed`\n- `points`\n- `goals`\n- `pointsPerGame`\n- `penaltyMinutes`\n- `plusMinus`\n- `ppGoals` # power play goals\n- `evGoals` # even strength goals\n- `pointsPerGame`\n- `penaltyMinutes`\n- `evPoints` # even strength points\n- `ppPoints` # power play points\n- `gameWinningGoals`\n- `otGoals`\n- `shPoints` # short handed points\n- `shGoals` # short handed goals\n- `shootingPct`\n- `timeOnIcePerGame`\n- `faceoffWinPct`\n- `shots`\n\n```python\n.....\nquery_builder = QueryBuilder()\nquery_context: QueryContext = query_builder.build(filters=filters)\n\nquery_context.fact_query = \"gamesPlayed\u003e=1 and goals\u003e=10\"  # defaults to gamesPlayed\u003e=1\n\ndata = client.stats.skater_stats_with_query_context(\n    report_type='summary',\n    query_context=query_context,\n    aggregate=True\n)\n```\n\n\n### Invalid Query / Errors\n\nThe `QueryContext` object will hold the result of the built query with the supplied queries.\nIn the event of an invalid query (bad data, wrong option, etc), the `QueryContext` object will\nhold all the errors that were encountered during the build process.  This should help in debugging.\n\nYou can quickly check the `QueryContext` object for errors by calling `query_context.is_valid()`.  Any \"invalid\" filters\nwill be removed from the output query, but anything that is still valid will be included.\n\n```python\n...\nquery_context: QueryContext = query_builder.build(filters=filters)\nquery_context.is_valid() # False if any of the filters fails its validation check\nquery_context.errors\n```\n\n---\n\n## Additional Stats Endpoints (In development)\n\n```python\n\nclient.stats.gametypes_per_season_directory_by_team(team_abbr=\"BUF\") # kinda weird endpoint.\n\nclient.stats.player_career_stats(player_id=\"8478402\")\n\nclient.stats.player_game_log(player_id=\"8478402\", season_id=\"20242025\", game_type=\"2\")\n\n# Team Summary Stats.\n#   These have lots of available parameters.  You can also tap into the apache cayenne expressions to build custom\n#   queries, if you have that knowledge.\nclient.stats.team_summary(start_season=\"20202021\", end_season=\"20212022\", game_type_id=2)\nclient.stats.team_summary(start_season=\"20202021\", end_season=\"20212022\")\n\n\n# Skater Summary Stats.\n#   Queries for skaters for year ranges, filterable down by franchise.\nclient.stats.skater_stats_summary_simple(start_season=\"20232024\", end_season=\"20232024\")\nclient.stats.skater_stats_summary_simple(franchise_id=10, start_season=\"20232024\", end_season=\"20232024\")\n\n# For the following query context endpoints, see the above section\nclient.stats.skater_stats_with_query_context(...)\n\n# Goalies\nclient.stats.goalie_stats_summary_simple(start_season=\"20242025\", stats_type=\"summary\")\n\n```\n---\n\n\n## Schedule Endpoints\n\n```python\n\n# Returns the games for the given date.\nclient.schedule.get_schedule(date=\"2021-01-13\")\n\n# Return games for the week of (date)\nclient.schedule.get_weekly_schedule(date=\"2021-01-13\")\n\nclient.schedule.get_schedule_by_team_by_month(team_abbr=\"BUF\")\nclient.schedule.get_schedule_by_team_by_month(team_abbr=\"BUF\", month=\"2021-01\")\n\nclient.schedule.get_schedule_by_team_by_week(team_abbr=\"BUF\")\nclient.schedule.get_schedule_by_team_by_week(team_abbr=\"BUF\", date=\"2024-01-01\")\n\nclient.schedule.get_season_schedule(team_abbr=\"BUF\", season=\"20212022\")\n\nclient.schedule.schedule_calendar(date=\"2023-11-23\")\n```\n\n---\n\n## Standings Endpoints\n\n```python\nclient.standings.get_standings()\nclient.standings.get_standings(date=\"2021-01-13\")\nclient.standings.get_standings(season=\"20222023\")\n\n# standings manifest.  This returns a ton of information for every season ever it seems like\n# This calls the API for this info, I also cache this in /data/seasonal_information_manifest.json\n# for less API calls since this only changes yearly.\nclient.standings.season_standing_manifest()\n```\n---\n\n## Teams Endpoints\n\n```python\nclient.teams.teams_info() # returns id + abbrevation + name of all teams\n```\n\n---\n\n## Game Center\n```python\nclient.game_center.boxscore(game_id=\"2023020280\")\n\nclient.game_center.play_by_play(game_id=\"2023020280\")\n\nclient.game_center.landing(game_id=\"2023020280\")\n\nclient.game_center.score_now()\n\n# this is used via the website to provide additional related game information\nclient.game_center.right_rail(game_id=\"2023020280\")\n```\n\n\n---\n\n## Helpers\n\nThese are expieremental and often times make many requests, can return DFs or do calculations. Stuff I find myself doing over and over I tend to move into helpers for convience.\n\n```python\n# Game types 1=preseason, 2=regular season, 3 playoffs\nclient.helpers.get_gameids_by_season(\"20242025\", game_types=[2])\n```\n\n\n---\n\n## Misc Endpoints\n```python\nclient.misc.glossary()\n\nclient.misc.config()\n\nclient.misc.countries()\n\nclient.misc.season_specific_rules_and_info()\n\nclient.misc.draft_year_and_rounds()\n```\n\n---\n## Insomnia Rest Client Export\n\n[Insomnia Rest Client](https://insomnia.rest) is a great tool for testing\n\nnhl_api-{ver}.json in the root folder is an export of the endpoints I have\nbeen working through using the Insomnia Rest Client.  You can import this directly\ninto the client and use it to test the endpoints.  I will be updating this as I go\n\n\n- - - \n\n\n## Developers\n\n1) Install [Poetry](https://python-poetry.org/docs/#installing-with-the-official-installer)\n\n`curl -sSL https://install.python-poetry.org | python3 -`\n\nor using pipx\n\n`pipx install poetry`\n\n\n2) `poetry install --with dev`\n\n3) `poetry shell`\n\n\n### Build Pipeline\nThe build pipeline will run `black`, `ruff`, and `pytest`.  Please make sure these are passing before submitting a PR.\n\n```python\n\n$ poetry shell\n\n# You can then run the following\n$ pytest\n$ ruff .\n$ black .\n\n```\n\n\n### pypi test net\n```\npoetry build\npoetry publish -r test-pypi\n```\n\n\n#### Poetry version management\n```\n# View current version\npoetry version\n\n# Bump version\npoetry version patch  # 0.1.0 -\u003e 0.1.1\npoetry version minor  # 0.1.0 -\u003e 0.2.0\npoetry version major  # 0.1.0 -\u003e 1.0.0\n\n# Set specific version\npoetry version 2.0.0\n\n# Set pre-release versions\npoetry version prepatch  # 0.1.0 -\u003e 0.1.1-alpha.0\npoetry version preminor  # 0.1.0 -\u003e 0.2.0-alpha.0\npoetry version premajor  # 0.1.0 -\u003e 1.0.0-alpha.0\n\n# Specify pre-release identifier\npoetry version prerelease  # 0.1.0 -\u003e 0.1.0-alpha.0\npoetry version prerelease beta  # 0.1.0-alpha.0 -\u003e 0.1.0-beta.0\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoreyjs%2Fnhl-api-py","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcoreyjs%2Fnhl-api-py","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoreyjs%2Fnhl-api-py/lists"}