Tutorial · 10 min · endpoints verified 2026-09-22

How to build an NBA shot chart

Turn served shot coordinates into a court view without guessing coordinate orientation, multi-season depth or player identity.

API coverage and endpoint overview: Explore the NBA API

Why this is hard

A scatter plot is only useful when its coordinate contract is explicit

Shot rows need a player, season, court coordinate pair, result and shot context. The chart must preserve the served coordinate system and refuse to invent depth outside loaded seasons.

  • Coordinate axes must be mapped once and tested against the documented court orientation.
  • Make and miss are categorical outcomes, not values to infer from score changes.
  • The player route requires a canonical id; names are display fields, not joins.
  • Shot-coordinate access has a Pro floor even though player discovery is Free.

Path 1 · recommended

Have your AI agent build it

Use MCP to inspect basketball coverage and resolve a player. Fetch the shot rows over REST, because the shot-coordinate route is not an MCP tool.

1. Connect the MCP server

Add the server in your MCP client and sign in with your Big Balls account. Your client registers itself and handles the token exchange — there is no key to copy.

MCP client configjson
{
  "mcpServers": {
    "bigballs-sports-data": {
      "url": "https://mcp.bigballsdata.com/mcp"
    }
  }
}

2. Tools your agent gets

  • find_players

    Find canonical player IDs by name for use with player-stat tools.

  • get_coverage

    Machine-readable map of what we hold and what we do not.

3. Ask for what you want

Paste this at your agent. It is written to make the model check coverage before it designs anything, which is what stops it inventing a field we do not serve.

Prompttext
Build an NBA shot chart. Resolve the player through Big Balls Sports Data,
check basketball coverage, and fetch /v1/players/:id/shots over REST. Plot only
served coordinate_x and coordinate_y values. Separate makes from misses, show
the selected season, and render an empty state when no shot rows are returned.

What the agent cannot reach

The MCP server does not expose shot coordinates. The agent must use REST, and the shot route requires a Pro key.

Path 2 · hand-coded

Build it yourself

The chart starts with coverage and identity, then draws only valid coordinate pairs.

  1. 01

    Check shot coverage before the chart

    Read basketball coverage and choose only a season the shot surface supports.

    Read basketball coveragebash
    curl -s "https://api.bigballsdata.com/v1/coverage?sport=basketball" \
      -H "Authorization: Bearer $BBS_API_KEY"
  2. 02

    Fetch one player’s shots

    Call the canonical player-shot route with the selected season and retain make/miss, type and distance beside the coordinates.

    Fetch shot rowsbash
    curl -s "https://api.bigballsdata.com/v1/players/$PLAYER_ID/shots?season=$SEASON" \
      -H "Authorization: Bearer $BBS_API_KEY"
  3. 03

    Reject incomplete coordinate rows

    Filter only rows whose two coordinate values are finite. Preserve the original shot payload for tooltips rather than recomputing it from screen pixels.

    Prepare chart pointstypescript
    const points = shots.filter((shot) =>
      Number.isFinite(shot.coordinate_x) && Number.isFinite(shot.coordinate_y),
    ).map((shot) => ({ x: shot.coordinate_x, y: shot.coordinate_y, made: shot.made, type: shot.shot_type }));

Reference

Every endpoint this tutorial uses

All on the gateway at api.bigballsdata.com. Verified against production on 2026-09-22.

MethodPathReturnsWhy you need itPlan
GET/v1/players/:id/shotsPer-shot coordinates, result and shot context for loaded seasonsThe chart points. The route shares the NBA play-by-play product floor. Read the shot-coordinate guide.Pro
GET/v1/coverage?sport=basketballMachine-readable season and data-type availabilityPrevents the season selector from promising an unloaded chart.Free

Pricing, honestly

Free resolves the player; Pro unlocks the shot rows

Coverage and player discovery can be implemented before upgrading. The coordinate data itself is part of the Pro play-by-play capability.

Free discovery

texttext
GET /v1/coverage?sport=basketball
GET /v1/players?search=…

Pro shot data

texttext
GET /v1/players/:id/shots?season=…
  • Coverage remains readable before upgrade.
  • Pro supplies the shot rows; it does not expand unloaded seasons.
  • Null coordinate rows still need filtering.
  • Player headshots remain a separate nullable media capability.

More tutorials

Other build guides

Known gaps

What the chart does not infer

Keep the first release anchored to the served shot event.

  • No guessed defender distance or contest level.
  • No heatmap smoothing presented as a recorded shot.
  • No season offered until coverage confirms it.

Questions

Is the shot route Free?
No. Shot coordinates share the Pro floor of full NBA play-by-play.
Can I mirror or rescale the coordinates?
Yes for presentation, but document the transformation and retain the original values for verification.
What should happen when there are no rows?
Render an empty state naming the player and season. Do not populate a sample chart.
Does Pro guarantee every historical season?
No. Entitlement and dataset coverage are separate; coverage decides which seasons have rows.

Plot the served shots

Resolve the player, verify the season, and keep every point traceable to one API row.