DEV Community

noah
noah

Posted on Originally published at dunkapi.com

How do I get NBA play-by-play data in Python?

One call returns a game's plays in order, with shot location, zone, expected points and win probability on each. This pulls the shots from one game into pandas.

Code verified October 10, 2026. It takes one call to get_plays, 10 credits.

The call

game_id comes from list_games or get_schedule.

import requests
import pandas as pd

API_KEY = "YOUR_API_KEY"

resp = requests.post(
    "https://mcp.dunkapi.com/v1/get_plays",
    headers={"x-api-key": API_KEY},
    json={
        "game_id": "401866759",
        "shots_only": True,
        "limit": 100,
    },
    timeout=60,
)
resp.raise_for_status()
data = resp.json()["data"]

plays = pd.json_normalize(data["plays"])
summary = plays.groupby("team").agg(
    attempts=("seq", "count"),
    points=("points", "sum"),
    expected=("shot.expected_points", "sum"),
)
print(summary.round(1))
Enter fullscreen mode Exit fullscreen mode

You need requests and pandas (pip install requests pandas) and a free API key from DunkAPI.

What comes back

The data object, trimmed to the first rows. Every value is real output from the run above.

{
  "plays": [
    {
      "game_id": "401866759",
      "seq": 1,
      "period": 1,
      "clock_seconds": 707,
      "team": "GS",
      "type": "Driving Layup Shot",
      "text": "Draymond Green makes driving layup",
      "shot": {
        "made": true,
        "points_attempted": 2,
        "x": 27,
        "y": 2,
        "distance_ft": 2.8,
        "zone": "restricted_area",
        "expected_points": 1.12
      },
      "points": 2,
      "player_id": "6589",
      "player": "Draymond Green",
      "player2_id": null,
      "home_score": 0,
      "away_score": 2,
      "home_win_prob": 0.601
    },
    {
      "game_id": "401866759",
      "seq": 2,
      "period": 1,
      "clock_seconds": 689,
      "team": "PHX",
      "type": "Jump Shot",
      "text": "Dillon Brooks misses 25-foot three point jumper",
      "shot": {
        "made": false,
        "points_attempted": 3,
        "x": 43,
        "y": 18,
        "distance_ft": 25.5,
        "zone": "above_break_3",
        "expected_points": 1.075
      },
      "points": 0,
      "player_id": "3155526",
      "player": "Dillon Brooks",
      "player2_id": null,
      "home_score": 0,
      "away_score": 2,
      "home_win_prob": 0.584
    },
    "...98 more"
  ],
  "count": 100,
  "next_offset": 100
}
Enter fullscreen mode Exit fullscreen mode

What to do next

  • get_shot_chart (10 credits). Roll the same shots up by zone for a player, a team or a defence.
  • get_game (5 credits). Add both box scores and every sportsbook's line for the game.

Originally published at dunkapi.com.

Top comments (0)