Unofficial tools for NASCAR data
Project description
PyNASCAR
This is obviously not associated with NASCAR and is an unofficial project
Overview
pynascar is a Python package for nascar race data acquisition and hopefully analysis
Installation
Install via pip
pip install pynascar
updates will be made regularly until all public API endpoints are hit
Quickstart
You can use this package to obtain data from the schedule or from any existing or live race including lap times, pit stop laps and times, all in race flags and all race control messages.
from pynascar import Schedule, Race, set_options, get_settings
from pynascar.driver import DriversData
# Enable local caching for faster repeated runs
set_options(cache_enabled=True, cache_dir=".cache/", df_format="parquet")
print(get_settings()) # verify settings
# Series: 1=Cup, 2=Xfinity, 3=Trucks
year = 2025
series_id = 1
race_id = 5577 # replace with a valid race id
# Schedules
schedule = Schedule(year, series_id)
schedule.data.head()
# Race data (use reload=True to force network fetch even if cached)
race = Race(year, series_id, race_id, reload=True)
race.telemetry.lap_times.head()
race.telemetry.pit_stops.head()
race.events.head()
# Example result subset (if available):
# race.results.stage_1
# Driver season aggregates
dd = DriversData.build(2025, 1, use_cache_only=False)
summary = dd.to_dataframe()
summary.sort_values("season_avg_position").head()
Available Classes
Ill replace this with proper documentation if anyone cares. Just leave an issue.
Schedule
schedule = Schedule(year, series_id)
# DataFrames:
schedule.data - Complete race schedule
Columns: race_id, race_name, race_date, track_name, series_id, winner_driver_id, scheduled_at, track_type, plus other race metadata
# Methods:
schedule.get_finished_races() - Completed races DataFrame
schedule.get_remaining_races() - Upcoming races DataFrame
schedule.most_recent_race() - Single row with latest completed race
schedule.next_race() - Single row with next scheduled race
Race
race = Race(year, series_id, race_id, reload=False)
# Results DataFrames:
race.results.results - Main race results
Columns: driver_id, driver_name, car_number, manufacturer, sponsor, team, team_id, qualifying_order, qualifying_position, qualifying_speed, starting_position, finishing_position, laps_completed, points, playoff_points
race.results.stage_1 - Stage 1 results (when available)
race.results.stage_2 - Stage 2 results (when available)
race.results.stage_3 - Stage 3 results (when available)
Columns: driver_id, driver_name, car_number, stage_number, position, stage_points
race.results.cautions - Caution periods
Columns: start_lap, end_lap, caution_type, comment, flag_state
race.results.lead_changes - Race leaders by lap
Columns: start_lap, end_lap, driver_name, car_number
race.results.practice - Practice session results
Columns: driver_id, driver_name, manufacturer, practice_name, position, lap_time, speed, total_laps, delta_to_leader, practice_number
race.results.qualifying - Qualifying results
Columns: driver_id, driver_name, manufacturer, qualifying_name, position, lap_time, speed, total_laps, delta_to_leader, qualifying_number
# Telemetry DataFrames:
race.telemetry.lap_times - Lap-by-lap timing data
Columns: driver_name, car_number, manufacturer, Lap, lap_time, lap_speed, position, driver_id
race.telemetry.pit_stops - Pit stop data
Columns: driver_name, lap, manufacturer, pit_in_flag_status, pit_out_flag_status, pit_in_race_time, pit_out_race_time, total_duration, box_stop_race_time, box_leave_race_time, pit_stop_duration, in_travel_duration, out_travel_duration, pit_stop_type, left_front_tire_changed, left_rear_tire_changed, right_front_tire_changed, right_rear_tire_changed, previous_lap_time, next_lap_time, pit_in_rank, pit_out_rank, positions_gained_lost, driver_id, car_number
race.telemetry.events - Race events and flags
Columns: Lap, Flag_State, Flag, note, driver_ids
# Driver Statistics DataFrames:
race.driver_data.drivers - Basic driver statistics
Columns: driver_id, driver_name, start_position, mid_position, position, closing_position, closing_laps_diff, best_position, worst_position, avg_position, passes_green_flag, passing_diff, passed_green_flag, quality_passes, fast_laps, top15_laps, lead_laps, laps, rating
race.driver_data.driver_stats_advanced - Advanced driver statistics
Columns: driver_id, driver_name, car_number, manufacturer, sponsor_name, best_lap, best_lap_speed, best_lap_time, laps_position_improved, fastest_laps_run, passes_made, times_passed, passing_differential, quality_passes, position_differential_last_10_percent
DriversData
dd = DriversData.build(year, series_id, use_cache_only=False)
# DataFrames:
dd.to_dataframe() - Season summary for all drivers
Columns: driver_id, driver_name, team, car_number, manufacturer, total_races, total_points, total_playoff_points, wins, top5s, top10s, total_leader_laps, total_passes_green_flag, total_passed_green_flag, plus averages of all race metrics
dd.race_dataframe(race_id) - Single race data for all drivers
Columns: All race metrics for specific race including driver_id, driver_name, team, car_number, manufacturer, race_id, finishing_position, starting_position, points, stage_points, avg_lap_speed, fastest_lap, pit_stops, etc.
dd.all_races_dataframe() - All races combined for all drivers
Columns: Same as race_dataframe() but for all races in the season
dd.driver_season_dataframe(driver_id) - All races for a specific driver
Columns: Same as race_dataframe() but filtered to one driver across all races
dd.driver_pit_stops(driver_id, race_id=None) - Pit stops for specific driver
Columns: Same as race.telemetry.pit_stops but filtered to specific driver, optionally by race
# Individual Driver Access:
dd.get_driver(driver_id) - Returns Driver object with race_data dict and pit_stops_df
Documentation
Series IDs: 1 - Cup 2 - Xfinity 3 - Trucks
Data Output Examples
Schedule
Example output:
| race_id | series_id | race_season | race_name | track_name | date_scheduled | track_type |
|---|---|---|---|---|---|---|
| 5546 | 1 | 2025 | DAYTONA 500 | Daytona International Speedway | 2025-02-16T14:30:00 | superspeedway |
| 5547 | 1 | 2025 | Ambetter Health 400 | Atlanta Motor Speedway | 2025-02-23T15:00:00 | intermediate |
| 5551 | 1 | 2025 | EchoPark Automotive Grand Prix | Circuit of The Americas | 2025-03-02T15:30:00 | road course |
Race Laps
| driver_name | car_number | manufacturer | lap | lap_time | lap_speed | position |
|---|---|---|---|---|---|---|
| Kyle Busch | 8 | Chv | 53 | 26.06s | 115.95 | 25 |
| Zane Smith | 38 | Frd | 16 | 26.07s | 115.94 | 24 |
| Austin Cindric | 2 | Frd | 17 | 29.31s | 89.47 | 29 |
Pit Stops
| driver_name | lap | manufacturer | total_duration | pit_stop_type | car_number |
|---|---|---|---|---|---|
| Ryan Blaney | 0 | Frd | 25.04s | OTHER | 12 |
| Shane Van Gisbergen | 0 | Chv | 24.85s | OTHER | 88 |
| Chase Briscoe | 0 | Tyt | 25.10s | OTHER | 19 |
Race Events
| lap | flag_state | flag | note |
|---|---|---|---|
| 0 | 8 | Warm Up | To the rear: #5, #6, #7, #35, #48, ... |
| 1 | 1 | Green | #19 leads the field to the green... |
| 3 | 1 | Green | #19, #23, #2 get single file in front... |
| 5 | 1 | Green | #77 reports fuel pressure issues... |
Race Driver Data
| driver_name | start_position | avg_position | best_position | worst_position | fast_laps | lead_laps | rating |
|---|---|---|---|---|---|---|---|
| Shane Van Gisbergen | 2 | 4.04 | 1 | 23 | 18 | 38 | 143.72 |
Visualizations (Examples)
Below are example plots that can be generated with the package an example of that is in the examples folder:
-
Average Speed per Driver
-
Average Position Difference
-
Quality Passes per race
TODO
| # | Item | Progress | Notes |
|---|---|---|---|
| 1 | Add Caching | Works. Needs to prevent writing when no data | |
| 2 | Add Driver Stats | Collected for stats. Works but is inefficient. Names need to be in sync | |
| 3 | Add Lap Stats | Laps exist within Race. Will add functions to analyze | |
| 3 | Add Pit Stats | Pits exist within Race and Driver. Will add functions to analyze | |
| 4 | Add tests | No work done | |
| 5 | Add Laps from Practice/Qualifying | This end point may not exist |
Acknowledgements
A few redditors found and plotted some of the routes a few years ago which is where i started with this:
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pynascar-0.2.0.tar.gz.
File metadata
- Download URL: pynascar-0.2.0.tar.gz
- Upload date:
- Size: 23.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.9.16
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
58767e6ec7b3cec376cec19c42f4f2f5a0a6a3a74635f0a43710f0cb9b4c12c4
|
|
| MD5 |
5ee43d3ce6301c7b169ca13ea4f6502a
|
|
| BLAKE2b-256 |
85ffff749b0baa69a9d84dce41c8d65b1c1e9b58a1cbde2fd89e2ce479daab05
|
File details
Details for the file pynascar-0.2.0-py3-none-any.whl.
File metadata
- Download URL: pynascar-0.2.0-py3-none-any.whl
- Upload date:
- Size: 22.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.9.16
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b5dd96b67d174d2f2b0baf9ad4df29afa17cfdf000ab25f3d1fb7b44698d0e42
|
|
| MD5 |
0d0dcb64e09e7847223610207fba894f
|
|
| BLAKE2b-256 |
4f4f5966c7ab6c52925c69311012067caaf478947210d3231c4303bac5efd977
|