Overview of jolpica-f1 API
mainjolpica-f1 is an open-source API for querying Formula 1 data. It serves as the successor to the Ergast F1 API and provides backwards-compatible endpoints for the soon-to-be-deprecated Ergast API.
Key resources:
repository·main·Indexed 21 days ago
https://github.com/jolpica/jolpica-f1An open-source API for querying Formula 1 data, designed as a successor to the Ergast F1 API. It provides backwards-compatible endpoints for Ergast while introducing a more robust database schema. The API includes endpoints for circuits, constructors, drivers, standings, and race results, and offers Pydantic schemas via the jolpica_schemas package. It also provides database dumps in CSV format with tiered access for free and commercial use.
jolpica-f1 is an open-source API for querying Formula 1 data. It serves as the successor to the Ergast F1 API and provides backwards-compatible endpoints for the soon-to-be-deprecated Ergast API.
Key resources:
The jolpica-f1 API is used by various community-driven SDKs and applications across multiple languages:
FastF1 (analysis and telemetry), LiveF1 (live and historical data), and BaseF1 (client with chainable queries and typed models).f1dataR package.JolpicaKit wrapper.jolpica-go wrapper.Integrations include Home Assistant cards (FormulaOne Card), Singer taps (tap-f1), mobile apps (Flutter/Android), and various web-based prediction and statistics tools.
Avoid nesting entities inside one another if they are merely associated. Nesting creates tight coupling and implies ownership where none exists.
Round and a Circuit), or when the nested entity is a reusable shared type.ResultComponent inside ResultItem) or when the child is a list of sub-items tightly coupled to the parent (e.g., sessions inside FullSession).Comparison:
# BAD: Circuit is nested inside Round (implies ownership)
class ScheduleRound(BasicRound):
circuit: ScheduleCircuit
sessions: list[ScheduleSession]
# GOOD: Round and Circuit are siblings (implies association)
class ScheduleEntry(BaseModel):
round: Round
circuit: Circuit
date: datetime.date | None = None
sessions: list[ScheduleFullSession]# BAD: Circuit is nested inside Round (implies ownership)
class ScheduleRound(BasicRound):
circuit: ScheduleCircuit
sessions: list[ScheduleSession]
# GOOD: Round and Circuit are siblings
class ScheduleEntry(BaseModel):
round: Round
circuit: Circuit
date: datetime.date | None = None
sessions: list[ScheduleFullSession]When a field requires both a machine-readable code and a human-readable label, use a paired naming convention:
{field}: str and {field}_display: str (e.g., type and type_display).FullSession): Use code and title instead of the *_display suffix.Examples:
type: str # Machine-readable code
type_display: str # Human-readable label
status: int
status_display: strFor temporal values that require multiple representations, use a double pattern to provide both a machine-readable duration and a human-readable string:
time: timedelta | None # ISO 8601 duration (machine-readable)
time_display: str | None # Human-readable stringThe Jolpica F1 API schemas (located in jolpica_schemas.f1_api.alpha) are organized into three distinct layers to promote reusability and separation of concerns:
shared.py): Contains reusable entity schemas like Driver, Round, and Session.metadata.py): Provides generic response wrappers such as DetailResponse[T] for single items and PaginatedResponse[T] for lists.driver.py, schedule.py, etc.): Contains endpoint-specific compositions, query parameters, and specialized subclasses.This layered approach ensures that core entities are defined once while allowing endpoints to customize the data shape they return.
jolpica-f1 provides database dumps containing historical race data, driver information, constructor details, and championship standings. Access is divided into two tiers:
The Jolpica F1 database model is distinct from the Ergast model. It uses various enumerations to manage data integrity.
PointSystem table enumerations are located in jolpica/formula_one/models/point_scheme.py.database_dumps.md documentation for specific instructions).Many fields in the database (such as PointScheme, SessionEntry.status, etc.) use Integer Enumerations to map pre-defined values.
Currently, the meanings of these integer encodings are not included in the dumps themselves and must be referenced from the model source code in the main branch of the repository. Common enums include:
SessionStatus (SessionEntry.status)PointSystem EnumsChampionshipScheme EnumsChampionshipAdjustmentType (ChampionshipAdjustment.adjustment)TeamDriverRole (TeamDriver.role)SessionType (Session.type) — Note: This uses a Text enum instead of an integer.A successful request returns a 200 OK response. The data is nested within the MRData object.
Key Response Fields:
MRData.StandingsTable: The object containing the season's drivers standing information.MRData.StandingsTable.season: The filtered season.MRData.StandingsTable.round: The round that the season the standings represent.MRData.StandingsTable.StandingsLists: A list of drivers standings list objects.MRData.StandingsTable.StandingsLists[i].DriverStandings: The list of individual driver standings objects.To balance payload size and detail, database-backed entities follow a two-tier pattern:
Basic* variant: A minimal schema used when an entity appears as a related object in another endpoint's response. It must include id: str and url: HttpUrl as the first two fields, along with the minimum fields needed for identification and display (e.g., names).Basic* class and adds optional or extended detail fields. This is used when the entity is the primary resource being returned (e.g., the /drivers/ endpoint).Example Pattern:
class BasicDriver(BaseModel):
id: str
url: HttpUrl
abbreviation: str | None = Field(None, max_length=10)
given_name: str
family_name: str
class Driver(BasicDriver):
nationality: str | None = None
country_code: str | None = Field(None, max_length=3)
permanent_car_number: int | None = None
date_of_birth: datetime.date | None = None
wikipedia: HttpUrl | None = NoneEndpoint files should follow a specific structure to manage specialized data and filtering:
class MySubclass(BasicType): pass) to allow for future expansion without affecting the shared type.DetailResponse[T] or PaginatedResponse[T] from the metadata layer.model_config = ConfigDict(extra="forbid") to reject unknown parameters, and ensure all filter fields are optional with None defaults.Example Endpoint File Structure:
from .shared import Circuit
from .metadata import DetailResponse, PaginatedResponse
class CircuitSummary(Circuit):
pass
PaginatedCircuitSummary = PaginatedResponse[list[CircuitSummary]]
RetrievedCircuitDetail = DetailResponse[CircuitSummary]
class CircuitQueryParams(BaseModel):
model_config = ConfigDict(extra="forbid")
year: int | None = Field(None, description="...")