If you want to scrape NBA player tracking data in 20 minutes without an API key, you’re in the right place. The NBA’s official stats site is a goldmine of movement metrics—speed, distance, touches, and defensive impact—but it doesn’t offer a public API. However, the underlying JSON endpoints that feed the website are accessible, and with Python plus a couple of clever headers, you can pull the exact same official tracking numbers into your own analysis pipeline. In this guide, you’ll build a scraper that extracts per-game tracking statistics for every player in the league, cleanly formats them, and saves them to a CSV file—all in under twenty minutes.
Why Scrape NBA Tracking Data Instead of Using a Third-Party API?
Third-party APIs often limit free tiers, require authentication, or lag behind the live stats you see on NBA.com. By scraping the official stats endpoints directly, you get the same raw data the site uses, without api keys or rate-limit headaches. In 2026, the tracking data is more detailed than ever, and for basketball analytics projects—whether you’re modeling player efficiency, visualizing court movement, or building a fantasy tool—having direct access to this source is a serious advantage. And because the endpoints are JSON, parsing them with Python is straightforward.
What You’ll Need to Get Started
Before we jump into the code, make sure you have the following:
- Python 3.9+ installed on your machine
- requests and pandas libraries (
pip install requests pandas) - A code editor or Jupyter notebook
- An internet connection capable of reaching stats.nba.com
No API key, no developer account, no paid service. Just the tools above and about 20 minutes.
Step 1: Locate the NBA Stats JSON Endpoint
The website’s player tracking tables are populated by a hidden API endpoint. To find it, open the NBA Player Tracking page in your browser, open the Developer Tools (F12), go to the Network tab, and refresh the page. Look for requests to stats.nba.com with URLs that contain leaguedashptstats. This endpoint returns JSON data for exactly the table you see on screen.
One important thing: the API expects a User-Agent header to identify your request as coming from a browser. Without a proper header, the server returns a 403 Forbidden. We’ll replicate that in our Python script.
Step 2: Write a Python Script to Fetch Tracking Data
Let’s start by building the fetch logic. We’ll create a session that mimics a browser request and then call the leaguedashptstats endpoint with the required query parameters. For this example, we’ll pull the Speed & Distance tracking category, which includes metrics like average speed, distance traveled, and defensive distance.
import requests
import pandas as pd
HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',
'Referer': 'https://www.nba.com/stats/players/tracking',
'Accept-Encoding': 'gzip, deflate, br',
'Accept': '*/*'
}
BASE_URL = 'https://stats.nba.com/stats/leaguedashptstats'
PARAMS = {
'Conference': '',
'Country': '',
'DateFrom': '',
'DateTo': '',
'Division': '',
'DraftPick': '',
'GameScope': '',
'GameSegment': '',
'Height': '',
'LastNGames': '0',
'LeagueID': '00',
'Location': '',
'MeasureType': 'Tracking',
'Month': '0',
'OpponentTeamID': '0',
'Outcome': '',
'PORound': '0',
'PerMode': 'PerGame',
'Period': '0',
'PlayerExperience': '',
'PlayerOrTeam': 'Player',
'PlayerPosition': '',
'PtMeasureType': 'SpeedDistance',
'Season': '2025-26',
'SeasonSegment': '',
'SeasonType': 'Regular Season',
'StarterBench': '',
'TeamID': '0',
'VsConference': '',
'VsDivision': '',
'Weight': ''
}
Notice the Season parameter: we’re pulling the current 2025-26 season. You can easily change it to any season stored on the NBA’s stats engine.
Step 3: Send the Request and Inspect the JSON Structure
Now let’s send the request and look at how the JSON is organized. The response has a nested structure, but the data we need lives at resultSets[0], with headers and rowSet.
resp = requests.get(BASE_URL, params=PARAMS, headers=HEADERS)
resp.raise_for_status()
data = resp.json()
result_set = data['resultSets'][0]
headers = result_set['headers']
rows = result_set['rowSet']
# Display the first row to confirm
print(headers)
print(rows[0])
If you run this, you’ll see a list of field names like PLAYER_ID, PLAYER_NAME, TEAM_ABBREVIATION, AVG_SPEED, DIST_MILES, and more. That’s the official tracking data, exactly as reported by the NBA’s player tracking system.
Step 4: Build a Clean DataFrame
Now we can convert the result into a pandas DataFrame. This makes filtering, sorting, and exporting trivial. We’ll also add a column for the season so you can archive multiple years later.
df = pd.DataFrame(rows, columns=headers)
df['SEASON'] = '2025-26'
# Keep only the most useful columns
keep_cols = [
'PLAYER_NAME', 'TEAM_ABBREVIATION', 'AGE',
'GP', 'MIN', 'AVG_SPEED', 'DIST_MILES',
'DIST_MILES_OFF', 'DIST_MILES_DEF',
'TOUCHES', 'TIME_OF_POSS'
]
df = df[keep_cols]
# Sort by average speed
df = df.sort_values('AVG_SPEED', ascending=False).reset_index(drop=True)
print(df.head(10))
You now have a clean table of the fastest players in the league, their team, and advanced tracking metrics. You can filter by position, compare offensive vs. defensive distance, or merge this with other player data from a fantasy API.
Step 5: Export to CSV for Further Analysis
For quick reuse, save the result to a CSV file. This is your portable dataset for the season.
df.to_csv('nba_2025_26_tracking.csv', index=False)
print('Saved', len(df), 'players to CSV')
That’s the core scraper. But you can go further. Change PtMeasureType to Rebounding, Defense, or Drives to pull other tracking categories. For example, using PtMeasureType=Drives gives you drive attempts, points, and assists per game. The endpoint is powerful—once you understand the parameters, you can scrape a wide range of NBA data without an API key.
Best Practices for Scraping NBA.com in 2026
When you scrape official stats endpoints, it’s essential to be respectful and responsible. The NBA’s stats servers aren’t designed for high-volume crawling, so keep the following in mind:
- Make only necessary requests. Cache your results locally to avoid hitting the same endpoint repeatedly.
- Use a reasonable delay between requests if you’re scraping many seasons or categories—2–3 seconds is a good rule.
- Respect robots.txt and the site’s terms of service. While these endpoints are public, any commercial use should be reviewed against NBA’s guidelines.
- Rotate your user agent occasionally, but don’t fake more than a standard browser would do.
In 2026, the NBA has become more aggressive about bot detection, so using the correct headers and keeping request volumes low is key to staying blocked-free.
Common Issues and Quick Fixes
If you run into a 403 Forbidden error, check your headers. The User-Agent must be a realistic browser string, and the Referer should point to the NBA.com stats page. Another frequent issue is missing query parameters. The easiest way to avoid that is to copy the full URL from the Network tab after selecting your filters, then pass it to requests.get(). Finally, if the JSON returns no rows for a particular season, verify the Season value—it should always be in the format YYYY-YY, like 2025-26.
Take Your Analytics Beyond the Scraper
Once you have this data, the possibilities are endless. You can create a ranking system based on defensive distance per minute, build a radar chart of unusual player movement tendencies, or merge tracking data with play-by-play logs to understand context. The scraper is just the first step; the real value lies in how you analyze and interpret the numbers. With pandas, matplotlib, and a bit of creativity, you can turn raw tracking data into compelling basketball insights that go far beyond box scores.
Conclusion
Scraping NBA player tracking data in 20 minutes without an API key is a practical skill for any basketball data enthusiast. By targeting the official JSON endpoints behind NBA.com and using Python’s requests and pandas libraries, you can pull accurate, current tracking statistics and build a reusable pipeline for your own projects. Whether you’re a fantasy sports player, a data scientist, or just curious about player movement, this approach gives you the data you need—clean, structured, and ready to analyze.
