BASE_URL = "https://api.jolpi.ca/ergast/f1"RATE_LIMIT_BURST = 2 # requests per secondRATE_LIMIT_SUSTAINED = 500 # requests per hourREQUEST_DELAY = 1 / RATE_LIMIT_BURSTdef fetch_driver_standings(season, round_num): """Fetch driver standings for a specific round in a season""" url = f"{BASE_URL}/{season}/{round_num}/driverstandings/" return fetch_with_rate_limit(url)def process_round(season, round_num): """Process a specific round in a season""" logger.info(f"Processing season: {season}, round: {round_num}") season_folder = Path(f"{season}") create_folder_if_not_exists(season_folder) # Fetch events data for the season events_file = season_folder / "events.json" if not events_file.exists(): logger.warning(f"Events file not found for season {season}. Skipping.") return with open(events_file, "r") as f: events_data = json.load(f) races = events_data.get("MRData", {}).get("RaceTable", {}).get("Races", []) # Find the specific race for the round race = None for r in races: if r.get("round") == str(round_num): race = r break if not race: logger.warning(f"Round {round_num} not found for season {season}. Skipping.") return race_folder_name = get_race_folder_name(race) race_folder = season_folder / race_folder_name create_folder_if_not_exists(race_folder) # Fetch round-level driver standings round_standings = fetch_driver_standings(season, round_num) if round_standings: with open(race_folder / "driverPoints.json", "w") as f: json.dump(round_standings, f, indent=2) logger.info( f"Saved round driver standings to {race_folder}/driverPoints.json" )
The constructor championship wasn’t officially recognized until 1958. Earlier years may show constructor data, but positions are often marked with ”-”.