jolpica-f1 API

repository·main·Indexed 21 days ago

https://github.com/jolpica/jolpica-f1

An 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.

Tokens
25.2K
Snippets
74
Records
131
Agent score
72%

What's inside jolpica-f1

  1. Explore SDKs and Community Integrations

    main

    The jolpica-f1 API is used by various community-driven SDKs and applications across multiple languages:

    SDKs

    • Python: FastF1 (analysis and telemetry), LiveF1 (live and historical data), and BaseF1 (client with chainable queries and typed models).
    • R: f1dataR package.
    • Swift: JolpicaKit wrapper.
    • Go: jolpica-go wrapper.

    Applications

    Integrations include Home Assistant cards (FormulaOne Card), Singer taps (tap-f1), mobile apps (Flutter/Android), and various web-based prediction and statistics tools.

  2. Prefer Flat over Nested schemas

    main

    Avoid nesting entities inside one another if they are merely associated. Nesting creates tight coupling and implies ownership where none exists.

    • When to Flatten: Use sibling keys at the same level when two entities are associated but neither owns the other (e.g., a Round and a Circuit), or when the nested entity is a reusable shared type.
    • When to Nest: Nesting is appropriate only when the child is truly owned by the parent (e.g., 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]
  3. Use Display Pairs for machine-readable and human-readable data

    main

    When a field requires both a machine-readable code and a human-readable label, use a paired naming convention:

    • For standard entities: Use {field}: str and {field}_display: str (e.g., type and type_display).
    • For logical groupings (like 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: str
  4. Use the Duration/Time Double pattern for temporal values

    main

    For 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 string
  5. Understand the Jolpica F1 API Schema Architecture

    main

    The Jolpica F1 API schemas (located in jolpica_schemas.f1_api.alpha) are organized into three distinct layers to promote reusability and separation of concerns:

    1. Shared Layer (shared.py): Contains reusable entity schemas like Driver, Round, and Session.
    2. Metadata Layer (metadata.py): Provides generic response wrappers such as DetailResponse[T] for single items and PaginatedResponse[T] for lists.
    3. Endpoint Layer (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.

  6. Understand Database Dump access tiers

    main

    jolpica-f1 provides database dumps containing historical race data, driver information, constructor details, and championship standings. Access is divided into two tiers:

    🆓 Free Tier (Non-Commercial Use)

    • Availability: Dumps are available for download 14 days after they are uploaded.
    • Authentication: No authentication required.
    • Usage: Strictly for non-commercial purposes.

    💎 Supporter Tier (Commercial Use)

    • Availability: Latest dumps are available immediately upon upload.
    • Authentication: Requires an API key.
    • Usage: Licensed for commercial use.
  7. Understand the Jolpica F1 Database Scheme

    main

    The Jolpica F1 database model is distinct from the Ergast model. It uses various enumerations to manage data integrity.

    • Schema Visualization: You can view the relationship diagram and full database documentation at dbdocs.io.
    • Enumerations: Mappings for enumeration values are defined within their respective model files. For example, the PointSystem table enumerations are located in jolpica/formula_one/models/point_scheme.py.
    • Database Dumps: You can download database dumps directly via the API (refer to the database_dumps.md documentation for specific instructions).
  8. Interpret Integer Enumerations in dumps

    main

    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 Enums
    • ChampionshipScheme Enums
    • ChampionshipAdjustmentType (ChampionshipAdjustment.adjustment)
    • TeamDriverRole (TeamDriver.role)
    • SessionType (Session.type) — Note: This uses a Text enum instead of an integer.
  9. Understand the Driver Standings response structure

    main

    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.
  10. Use the Two-Tier Pattern for database-backed entities

    main

    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).
    • Full variant: Inherits from the 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 = None
  11. Configure Endpoint-specific schemas and QueryParams

    main

    Endpoint files should follow a specific structure to manage specialized data and filtering:

    1. Endpoint-specific subclasses: Inherit from shared types to add fields only relevant to that endpoint, or create pass-through subclasses (e.g., class MySubclass(BasicType): pass) to allow for future expansion without affecting the shared type.
    2. Summary/Detail schemas: Compose the response shape.
    3. Response type aliases: Use DetailResponse[T] or PaginatedResponse[T] from the metadata layer.
    4. QueryParams: Use Pydantic models for filtering. Always use 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="...")