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.
{
"mcpServers": {
"bigballs-sports-data": {
"url": "https://mcp.bigballsdata.com/mcp"
}
}
}2. Tools your agent gets
find_playersFind canonical player IDs by name for use with player-stat tools.
get_coverageMachine-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.
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.
- 01
Check shot coverage before the chart
Read basketball coverage and choose only a season the shot surface supports.
Read basketball coveragebashcurl -s "https://api.bigballsdata.com/v1/coverage?sport=basketball" \ -H "Authorization: Bearer $BBS_API_KEY" - 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 rowsbashcurl -s "https://api.bigballsdata.com/v1/players/$PLAYER_ID/shots?season=$SEASON" \ -H "Authorization: Bearer $BBS_API_KEY" - 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 pointstypescriptconst 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.
| Method | Path | Returns | Why you need it | Plan |
|---|---|---|---|---|
| GET | /v1/players/:id/shots | Per-shot coordinates, result and shot context for loaded seasons | The chart points. The route shares the NBA play-by-play product floor. Read the shot-coordinate guide. | Pro |
| GET | /v1/coverage?sport=basketball | Machine-readable season and data-type availability | Prevents 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
GET /v1/coverage?sport=basketball
GET /v1/players?search=…Pro shot data
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
- How to build a fantasy hockey app
- How to build a fantasy basketball app
- How to build an NBA scoreboard
- How to build an NHL scoreboard
- How to build an NBA props tracker
- How to build an NBA matchup page
- How to build an NHL props tracker
- How to build an NHL xG explorer
- How to build an NHL goalie dashboard
- How to build a soccer xG app
- How to build a cricket app
- NFL fantasy football API tutorial
- How to build an NFL scouting tool
- How to build an NFL conditions analytics tool
- NFL betting model API tutorial
- NFL live game center API tutorial
- How to build an NCAAF scoreboard and live game center
- How to build college football rankings and conference standings
- How to build an NCAAF fantasy football app
- How to build an MLB live scoreboard
- How to build a fantasy baseball app
- How to build an MLB pitching dashboard
- How to build an MLB betting model
- How to build an MLB standings dashboard
- How to build a soccer fantasy app
- How to build a soccer betting model
- How to build a soccer live match center
- How to build a soccer player production board
- How to build a soccer fixture alert app
- How to build a soccer scouting tool
- How to build an NCAAF betting model
- How to build an NCAAF conditions analytics tool
- How to build a WNBA live scoreboard
- How to build a WNBA quarter tracker
- How to build a WNBA odds board
- How to build a WNBA schedule browser
- How to build a WNBA team-form tool
- How to build an NCAAB scoreboard
- How to build a March Madness archive
- How to build an NCAAB game-detail view
- How to build an NCAAB team-form dashboard
- How to build an NCAAB head-to-head tool
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.
Related APIs
Other APIs you can build with: