# Understanding Race Results, Driver, Team and Track Data in v3

One of the most significant changes in Motorsport API v3 is how race results and driver data are structured. In v1, result fields were individual properties on each driver/result object. In v3, this data has moved into structured arrays that provide better organization and more detailed information.

This guide specifically addresses the question: **"Where did my v1 data fields go in v3?"**

{% hint style="info" %}
**Key change:** Race result data now lives in the `lineups` array, where each lineup entry represents a driver's participation in a fixture (race/qualifying/practice).

This guide shows examples as Javascript/Node.JS code, however you can use similar approaches in your programming language of choice.
{% endhint %}

***

### High-level structural changes

#### From single properties to arrays

In v1, many data points were direct properties on objects. In v3, most data is organised into typed arrays for better structure and extensibility.

**v1 Approach (flat structure):**

```json
{
  "driver_id": 123,
  "team_id": 456,
  "position": 1,
  "driver_time": "1:32:03.456",
  "best_lap_time": "1:23.456",
  "laps": 58,
  "grid": 3
}
```

**v3 Approach (structured arrays):**

```json
{
  "lineups": [
    {
      "player_id": 123,
      "team_id": 456,
      "grid_position": 3,
      "details": [
        {
          "type_id": 187,
          "type": { "name": "Position", "code": "position" },
          "data": { "position": 1 }
        },
        {
          "type_id": 195,
          "type": { "name": "Time", "code": "time" },
          "data": { "time": "1:32:03.456" }
        },
        {
          "type_id": 197,
          "type": { "name": "Fastest Lap", "code": "fastest-lap" },
          "data": { 
            "time": "1:23.456",
            "lap_number": 42
          }
        }
      ]
    }
  ]
}
```

#### Why this change?

**Benefits of the new structure:**

* **Extensibility**: Easy to add new data types without changing the schema
* **Type safety**: Each data point has an explicit type
* **Consistency**: Same pattern across all motorsport series
* **Clarity**: Clear separation between driver info and result details

***

### Understanding lineups

The `lineups` array is the core container for all driver participation and result data in a fixture.

#### What is a lineup entry?

Each item in the `lineups` array represents **one driver's participation** in a specific fixture (race, qualifying, or practice session).

#### Lineup Structure

```json
{
  "id": 789012,
  "fixture_id": 19444687,
  "player_id": 123,              // Driver ID
  "team_id": 456,                // Team ID
  "grid_position": 3,            // Starting position
  "details": [                   // All result data (when included)
    { /* Position */ },
    { /* Time */ },
    { /* Fastest Lap */ },
    { /* Laps Completed */ }
  ],
  "driver": {                    // Driver info (when included)
    "id": 123,
    "name": "Max Verstappen",
  }
}
```

#### Accessing lineups

**Basic request:**

```bash
GET /v3/motorsport/fixtures/19444687
  ?api_token=YOUR_TOKEN
  &include=lineups
```

**With driver details:**

```bash
GET /v3/motorsport/fixtures/19444687
  ?api_token=YOUR_TOKEN
  &include=lineups.driver;lineups.details.type
```

***

### Complete field mapping: v1 → v3

Here's the definitive guide for finding your v1 fields in v3.

#### Core identifiers

| v1 Field    | v3 Location           | Notes                                                                                                                                                                                                 |
| ----------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `driver_id` | `lineups[].player_id` | Direct mapping; different IDs (see [Drivers](/v3/motorsport-api/tutorials-and-guides/guides/migrating-from-formula-one-api-v1-to-motorsport-api-v3/drivers.md))                                       |
| `team_id`   | `lineups[].team_id`   | Direct mapping; different IDs (see [Teams](/v3/motorsport-api/tutorials-and-guides/guides/migrating-from-formula-one-api-v1-to-motorsport-api-v3/teams.md))                                           |
| `track_id`  | `venue_id`            | Now at fixture level, not driver level; different IDs (see [Tracks / Venues](/v3/motorsport-api/tutorials-and-guides/guides/migrating-from-formula-one-api-v1-to-motorsport-api-v3/tracks-venues.md)) |

**Example:**

**v1:**

```json
{
  "driver_id": 123,
  "team_id": 456,
  "track_id": 789
}
```

**v3:**

```json
{
  "venue_id": 789,  // At fixture level
  "lineups": [
    {
      "player_id": 123,  // Was driver_id
      "team_id": 456
    }
  ]
}
```

{% hint style="info" %}
You can enrich data with normal and nested includes, like `include=venue` and `include=lineups.driver`. However, we recommend caching and/or storing data, and relating them using IDs.
{% endhint %}

#### Result times

<table><thead><tr><th width="166.147705078125">v1 Field</th><th width="270.968994140625">v3 Location</th><th width="152.3828125">Type Name</th><th>Type Code</th></tr></thead><tbody><tr><td><code>driver_time</code></td><td><code>lineups[].details[].data.time</code></td><td>"Time"</td><td><code>time</code></td></tr><tr><td><code>driver_time_int</code></td><td><code>lineups[].details[].data.gap</code></td><td>"Gap To Leader"</td><td><code>gap-to-leader</code></td></tr><tr><td><code>driver_time_int</code>*</td><td><code>lineups[].details[].data.interval</code></td><td>"Interval"</td><td><code>interval</code></td></tr></tbody></table>

{% hint style="warning" %}
(\*)The driver\_time\_int field in v1 normally contained the time relative the leader (Gap To Leader), unlike the 'int' in the name suggests. In v3, the naming of those times is corrected and both types of relative time are available.
{% endhint %}

**Finding time in v3:**

```javascript
// Find the Time detail
const timeDetail = lineup.details.find(d => d.type.code === 'time');
const finishTime = timeDetail?.data.time;  // "1:32.456"
```

**Complete example:**

**v1:**

```json
{
  "driver_id": 123,
  "position": 1,
  "driver_time": "1:32.456",
  "driver_time_int": "1:32:03.456"
},
{
  "driver_id": 456,
  "position": 2,
  "driver_time": "1:33.356",
  "driver_time_int": "+0.900"
}
```

**v3:**

```json
{
  "lineups": [
    {
      "player_id": 123,
      "details": [
        {
          "type_id": 187,
          "type": { "name": "Position", "code": "position" },
          "data": { "position": 1 }
        },
        {
          "type_id": 195,
          "type": { "name": "Time", "code": "time" },
          "data": { "time": "1:32.456" }
        },
        {
          "type_id": 196,
          "type": { "name": "Interval", "code": "interval" },
          "data": { "interval": "1:32:03.456" }
        }
      ]
    }
    {
      "player_id": 456,
      "details": [
        {
          "type_id": 187,
          "type": { "name": "Position", "code": "position" },
          "data": { "position": 2 }
        },
        {
          "type_id": 195,
          "type": { "name": "Time", "code": "time" },
          "data": { "time": "1:33.356" }
        },
        {
          "type_id": 196,
          "type": { "name": "Interval", "code": "interval" },
          "data": { "interval": "1:32:03.456" }
        }
      ]
    }
  ]
}
```

{% hint style="info" %}
**Important to note**: For the "type" fields to be available, the nested include '`lineups.details.type`' must be used.
{% endhint %}

#### Fastest lap data

<table><thead><tr><th width="150.5078125">v1 Field</th><th width="328.875">v3 Location</th><th>Type Name</th><th>Type Code</th></tr></thead><tbody><tr><td><code>best_lap_time</code></td><td><code>lineups[].details[].data.time</code></td><td>"Fastest Lap"</td><td><code>fastest-lap</code></td></tr><tr><td><code>fasted_lap_time</code></td><td><code>lineups[].details[].data.time</code></td><td>"Fastest Lap"</td><td><code>fastest-lap</code></td></tr><tr><td><code>fasted_lap</code></td><td><code>lineups[].details[].data.lap_number</code></td><td>"Fastest Lap"</td><td><code>fastest-lap</code></td></tr></tbody></table>

{% hint style="info" %}
**Important:** Both `fasted_lap_time` and `fasted_lap` (lap number) are now in the **same detail object** with type "Fastest Lap" and keys `time` and `lap_number`. `best_lap_time` was a duplicate field and can also be found in this object via the key `time`.
{% endhint %}

**Finding fastest lap in v3:**

```javascript
// Find the Fastest Lap detail
const fastestLapDetail = lineup.details.find(d => d.type.code === 'fastest-lap');

const fastestLapTime = fastestLapDetail?.data.time;        // "1:23.456"
const fastestLapNumber = fastestLapDetail?.data.lap_number; // 42
```

**Complete example:**

**v1:**

```json
{
  "driver_id": 123,
  "best_lap_time": "1:23.456",
  "fasted_lap_time": "1:23.456",
  "fasted_lap": 42
}
```

**v3:**

```json
{
  "lineups": [
    {
      "player_id": 123,
      "details": [
        {
          "type_id": 197,
          "type": { "name": "Fastest Lap", "code": "fastest-lap" },
          "data": {
            "time": "1:23.456",
            "lap_number": 42
          }
        }
      ]
    }
  ]
}
```

#### Laps completed

<table><thead><tr><th width="105.70703125">v1 Field</th><th width="284.45703125">v3 Location</th><th>Type Name</th><th>Type Code</th></tr></thead><tbody><tr><td><code>laps</code></td><td><code>lineups[].details[].data.laps</code></td><td>"Laps"</td><td><code>laps</code></td></tr></tbody></table>

**Finding laps in v3:**

```javascript
// Find the Laps detail
const lapsDetail = lineup.details.find(d => d.type.code === 'laps');
const lapsCompleted = lapsDetail?.data.laps;  // 58
```

**Complete example:**

**v1:**

```json
{
  "driver_id": 123,
  "laps": 58
}
```

**v3:**

```json
{
  "lineups": [
    {
      "player_id": 123,
      "details": [
        {
          "type_id": 192,
          "type": { "name": "Laps", "code": "laps" },
          "data": { "laps": 58 }
        }
      ]
    }
  ]
}
```

#### Grid position

| v1 Field | v3 Location               | Notes                                              |
| -------- | ------------------------- | -------------------------------------------------- |
| `grid`   | `lineups[].grid_position` | Available as direct property, and in details array |

{% hint style="success" %}
**Good To Know:** Grid position is a direct property on the lineup object, which you can use instead of the object in the details array.&#x20;
{% endhint %}

**Complete example:**

**v1:**

```json
{
  "driver_id": 123,
  "grid": 3
}
```

**v3:**

```json
{
  "lineups": [
    {
      "player_id": 123,
      "grid_position": 3  // Direct property
    }
  ]
}
```

**v3, alternative way:**

<table><thead><tr><th width="101.66796875">v1 Field</th><th width="350">v3 Location</th><th>Type Name</th><th>Type Code</th></tr></thead><tbody><tr><td><code>grid</code></td><td><code>lineups[].details[].data.position</code></td><td>"Grid Position"</td><td><code>grid-position</code></td></tr></tbody></table>

#### Pitstops

<table><thead><tr><th width="109.4921875">v1 Field</th><th width="286.4921875">v3 Location</th><th>Type Name</th><th>Type Code</th></tr></thead><tbody><tr><td><code>pit</code></td><td><code>lineups[].details[].data.stops</code></td><td>"Pitstops"</td><td><code>pitstops</code></td></tr></tbody></table>

**Finding pitstops in v3:**

```javascript
// Find the Pitstops detail
const pitstopsDetail = lineup.details.find(d => d.type.code === 'pitstops');
const numberOfStops = pitstopsDetail?.data.stops;  // 2
```

**Complete example:**

**v1:**

```json
{
  "driver_id": 123,
  "pit": 2
}
```

**v3:**

```json
{
  "lineups": [
    {
      "player_id": 123,
      "details": [
        {
          "type_id": 193,
          "type": { "name": "Pitstops", "code": "pitstops" },
          "data": { "stops": 2 }
        }
      ]
    }
  ]
}
```

#### Additional detail types

Based on actual v3 data, here are the additional detail types available in lineups:

| v3 Detail Type | Type Code       | Data Fields                   | Description                   |
| -------------- | --------------- | ----------------------------- | ----------------------------- |
| Points         | `points`        | `points`                      | Championship points earned    |
| Gap To Leader  | `gap-to-leader` | `gap`                         | Time gap to race leader       |
| Tyre           | `tyre`          | `compound`, `age_since_start` | Current tyre compound and age |

**Example - Getting tyre information:**

```javascript
const tyreDetail = lineup.details.find(d => d.type.code === 'tyre');
const tyreCompound = tyreDetail?.data.compound;      // "HARD", "MEDIUM", "SOFT", etc.
const tyreAge = tyreDetail?.data.age_since_start;    // 0, 5, 10, etc.
```

**Example - Getting gap to leader:**

```javascript
const gapDetail = lineup.details.find(d => d.type.code === 'gap-to-leader');
const gap = gapDetail?.data.gap;  // "25.624"
```

#### Retirement status

<table><thead><tr><th>v1 Field</th><th width="300.5999755859375">v3 Location</th><th>Type Name</th><th>Type Code</th></tr></thead><tbody><tr><td><code>retired</code></td><td><code>lineups[].details[].data.status</code></td><td>"Status"</td><td><code>status</code></td></tr></tbody></table>

**Status values:**

* `"DNF"` - Did Not Finish
* `"DNS"` - Did Not Start
* `"DSQ"` - Disqualified

**Finding status in v3:**

```javascript
// Find the Status detail
const statusDetail = lineup.details.find(d => d.type.code === 'status');
const driverStatus = statusDetail?.data.status;  // "DNF", etc.

// Check if driver retired
const didNotFinish = ['DNF', 'DNS', 'DSQ'].includes(driverStatus);
```

**Complete Example:**

**v1:**

```json
{
  "driver_id": 123,
  "retired": true
}
```

**v3:**

```json
{
  "lineups": [
    {
      "player_id": 123,
      "details": [
        {
          "type_id": 9713,
          "type": { "name": "Status", "code": "status" },
          "data": { "status": "DNF" }
        }
      ]
    }
  ]
}
```

{% hint style="info" %}
The fixture-level `state` indicates overall race status (NS, LIVE, FULL\_TIME, CANCELLED, etc.), not individual driver status.&#x20;
{% endhint %}

***

### Complete side-by-side comparison

#### v1 race result response

```json
{
  "id": 12345,
  "stage_name": "Monaco Grand Prix - Race",
  "track_id": 789,
  "results": [
    {
      "driver_id": 37920804,
      "team_id": 276189,
      "position": 4,
      "grid": 4,
      "driver_time": "1:21.294",
      "driver_time_int": "4.273",
      "best_lap_time": "1:21.294",
      "fastest_lap": 53,
      "laps": 53,
      "pit": 1,
      "points": 12
    }
  ]
}
```

#### v3 race result response

```json
{
  "id": 19444687,
  "name": "Monaco Grand Prix - Race",
  "venue_id": 343590,
  "state_id": 5,
  "lineups": [
    {
      "id": 14670723395,
      "fixture_id": 19444687,
      "player_id": 37920804,
      "team_id": 276189,
      "grid_position": 4,
      "driver_name": "Charles Leclerc",
      "driver_number": 16,
      "details": [
        {
          "type_id": 9711,
          "type": { "name": "Position", "code": "position" },
          "data": { "position": 4 }
        },
        {
          "type_id": 9708,
          "type": { "name": "Time", "code": "time" },
          "data": { "time": "1:21.294" }
        },
        {
          "type_id": 9733,
          "type": { "name": "Interval", "code": "interval" },
          "data": { "interval": "4.273" }
        },
        {
          "type_id": 9732,
          "type": { "name": "Gap To Leader", "code": "gap-to-leader" },
          "data": { "gap": "25.624" }
        },
        {
          "type_id": 9716,
          "type": { "name": "Fastest Lap", "code": "fastest-lap" },
          "data": {
            "time": "1:21.294",
            "lap_number": 53
          }
        },
        {
          "type_id": 9710,
          "type": { "name": "Laps", "code": "laps" },
          "data": { "laps": 53 }
        },
        {
          "type_id": 9709,
          "type": { "name": "Pitstops", "code": "pitstops" },
          "data": { "stops": 1 }
        },
        {
          "type_id": 9707,
          "type": { "name": "Points", "code": "points" },
          "data": { "points": 12 }
        },
        {
          "type_id": 9712,
          "type": { "name": "Grid Position", "code": "grid-position" },
          "data": { "position": 4 }
        },
        {
          "type_id": 9729,
          "type": { "name": "Tyre", "code": "tyre" },
          "data": {
            "compound": "HARD",
            "age_since_start": 0
          }
        }
      ]
    }
  ]
}
```

***

### Practical code examples

#### Extracting race results

**Goal:** Get finishing position, time, and fastest lap for each driver.

```javascript
async function getRaceResults(fixtureId, apiToken) {
  const response = await fetch(
    `https://api.sportmonks.com/v3/motorsport/fixtures/${fixtureId}` +
    `?api_token=${apiToken}` +
    `&include=lineups.driver;lineups.details.type`
  );
  
  const data = await response.json();
  const results = [];
  
  data.data.lineups.forEach(lineup => {
    // Helper function to find detail by type code
    const findDetail = (code) => 
      lineup.details.find(d => d.type.code === code);
    
    // Extract all result data
    const positionDetail = findDetail('position');
    const timeDetail = findDetail('time');
    const fastestLapDetail = findDetail('fastest-lap');
    const lapsDetail = findDetail('laps');
    const statusDetail = findDetail('status');
    
    results.push({
      driver_id: lineup.player_id,
      driver_name: lineup.driver?.name,
      team_id: lineup.team_id,
      position: positionDetail?.data.position,
      finish_time: timeDetail?.data.time,
      fastest_lap_time: fastestLapDetail?.data.time,
      fastest_lap_number: fastestLapDetail?.data.lap_number,
      laps_completed: lapsDetail?.data.laps,
      grid_position: lineup.grid_position,
      status: statusDetail?.data.status || 'FIN'
    });
  });
  
  // Sort by position
  return results.sort((a, b) => a.position - b.position);
}

// Usage
const raceResults = await getRaceResults(19444687, 'YOUR_TOKEN');
console.log(raceResults);
```

**Output:**

```javascript
[
  {
    driver_id: 123,
    driver_name: "Max Verstappen",
    team_id: 456,
    position: 1,
    finish_time: "1:32:03.456",
    fastest_lap_time: "1:23.456",
    fastest_lap_number: 42,
    laps_completed: 58,
    grid_position: 3,
    status: "FIN"
  },
  // ... more results
]
```

#### Building a results table

```javascript
function displayResultsTable(results) {
  console.log('Pos | Driver            | Team | Time        | Fastest Lap | Grid | Status');
  console.log('----+-------------------+------+-------------+-------------+------+--------');
  
  results.forEach(result => {
    const pos = result.position.toString().padStart(3);
    const driver = (result.driver_name || 'Unknown').padEnd(17);
    const team = result.team_id.toString().padStart(4);
    const time = (result.finish_time || 'DNF').padEnd(11);
    const fastLap = (result.fastest_lap_time || 'N/A').padEnd(11);
    const grid = result.grid_position.toString().padStart(4);
    const status = result.status.padEnd(6);
    
    console.log(`${pos} | ${driver} | ${team} | ${time} | ${fastLap} | ${grid} | ${status}`);
  });
}

// Usage
displayResultsTable(raceResults);
```

**Output:**

```
Pos | Driver            | Team | Time        | Fastest Lap | Grid | Status
----+-------------------+------+-------------+-------------+------+--------
  1 | Max Verstappen    |  456 | 1:32:03.456 | 1:23.456    |    3 | FIN   
  2 | Lewis Hamilton    |  457 | +5.234      | 1:23.789    |    1 | FIN   
  3 | Charles Leclerc   |  458 | +12.456     | 1:24.123    |    5 | FIN   
```

#### Identifying drivers who didn't finish

Since there's no direct "retired" or "DNF" status field, determine this by comparing lap counts:

```javascript
function getDriversWhoDidNotFinish(lineups) {
  // Find the maximum laps completed (race winner's laps)
  const maxLaps = Math.max(...lineups.map(lineup => {
    const lapsDetail = lineup.details.find(d => d.type.code === 'laps');
    return lapsDetail?.data.laps || 0;
  }));
  
  // Find drivers who completed fewer laps than the winner
  return lineups
    .filter(lineup => {
      const lapsDetail = lineup.details.find(d => d.type.code === 'laps');
      const lapsCompleted = lapsDetail?.data.laps || 0;
      return lapsCompleted < maxLaps;
    })
    .map(lineup => ({
      driver_id: lineup.player_id,
      driver_name: lineup.driver?.name,
      laps_completed: lineup.details.find(d => d.type.code === 'laps')?.data.laps || 0,
      position: lineup.details.find(d => d.type.code === 'position')?.data.position
    }));
}

// Usage
const dnfs = getDriversWhoDidNotFinish(data.lineups);
console.log(`${dnfs.length} drivers did not finish the race`);
```

#### Finding fastest lap of the race

```javascript
function getFastestLapOfRace(lineups) {
  let fastest = null;
  
  lineups.forEach(lineup => {
    const fastestLapDetail = lineup.details.find(d => d.type.code === 'fastest-lap');
    
    if (fastestLapDetail && fastestLapDetail.data.time) {
      if (!fastest || fastestLapDetail.data.time < fastest.time) {
        fastest = {
          driver_id: lineup.player_id,
          driver_name: lineup.driver?.name,
          time: fastestLapDetail.data.time,
          lap_number: fastestLapDetail.data.lap_number
        };
      }
    }
  });
  
  return fastest;
}

// Usage
const fastestLap = getFastestLapOfRace(data.lineups);
console.log(`Fastest lap: ${fastestLap.driver_name} - ${fastestLap.time} (Lap ${fastestLap.lap_number})`);
```

***

### States & Types: Cacheable reference data

One major improvement in v3 is that **states and types are now standardised** across all motorsport series.

#### Why this matters

In v1, status codes and data types could vary. In v3, they are consistent and **safe to cache long-term**.

#### Caching strategy

{% hint style="info" %}
**Best Practice:** Cache types and states for 30+ days. They rarely change and are consistent across all fixtures.&#x20;
{% endhint %}

```bash
GET /v3/core/types
  ?api_token=YOUR_TOKEN
```

**Common Lineup Detail Types:**

| Type ID | Name          | Code            | Usage                                   |
| ------- | ------------- | --------------- | --------------------------------------- |
| 9711    | Position      | `position`      | Live or final race position             |
| 9708    | Time          | `time`          | Latest lap time                         |
| 9733    | Interval      | `interval`      | Time behind car ahead                   |
| 9732    | Gap To Leader | `gap-to-leader` | Time behind race leader                 |
| 9716    | Fastest Lap   | `fastest-lap`   | Fastest lap time & lap number           |
| 9710    | Laps          | `laps`          | Laps completed                          |
| 9709    | Pitstops      | `pitstops`      | Number of pit stops                     |
| 9707    | Points        | `points`        | Championship points earned in this race |
| 9712    | Grid Position | `grid-position` | Starting grid position                  |
| 9729    | Tyre          | `tyre`          | Current tyre compound & age             |

{% hint style="info" %}
A full detailed list can be found here: [Results & Live Data Type Reference](/v3/motorsport-api/welcome/results-and-live-data-type-reference.md)
{% endhint %}

#### Getting all states

```bash
GET /v3/core/states
  ?api_token=YOUR_TOKEN
```

**Common fixture states:**

<table><thead><tr><th width="100.91015625">State ID</th><th width="136.8984375">State Code</th><th width="117.0546875">Name</th><th>Additional notes</th></tr></thead><tbody><tr><td>1</td><td>NS</td><td>Not Started</td><td></td></tr><tr><td>27</td><td>LIVE</td><td>Live</td><td>Transition to this state indicates the start of the first lap in races (excluding the formation lap) and green light in other session types</td></tr><tr><td>5</td><td>FT</td><td>Full Time</td><td>Known in v1 as 'Finished', indicates a chequered flag is waved in races, and full session time elapsed in other session types</td></tr><tr><td>12</td><td>CANCELLED</td><td>Cancelled</td><td></td></tr><tr><td>16</td><td>DELAYED</td><td>Delayed</td><td></td></tr><tr><td>18</td><td>INTERRUPTED</td><td>Interrupted</td><td>Session is paused, usually when a red flag is waved</td></tr><tr><td>20</td><td>DELETED</td><td>Deleted</td><td>The fixture is removed from the API, it is no longer active in standard calls but can still be fetched with <code>filter=deleted</code>.</td></tr></tbody></table>

#### Using cached types

```javascript
// Cache types once
const typesCache = new Map();

async function loadTypes(apiToken, page = 1) {
  const response = await fetch(
    `https://api.sportmonks.com/v3/core/types?api_token=${apiToken}&filter=populate&page=${page}`
  );
  const data = await response.json();
  
  data.data.forEach(type => {
    typesCache.set(type.code, type);
  });
  
  // Recursively fetch the next page, if available
  if(data.pagination.has_more) {
    await loadTypes(apiToken, page + 1);
  }
}

// Use cached types to identify details
function getDetailValue(lineup, typeCode) {
  const type = typesCache.get(typeCode);
  const detail = lineup.details.find(d => d.type_id === type.id);
  return detail?.data;
}

// Load once at application start
await loadTypes('YOUR_TOKEN');

// Use throughout application without additional API calls
const position = getDetailValue(lineup, 'position')?.position;
const fastestLap = getDetailValue(lineup, 'fastest-lap')?.time;
```

***

### Driver information

In v1, debut information was sometimes included in driver responses. In v3, all driver-specific metadata (including debut info) is now in a dedicated `metadata` object.

#### Accessing debut data

**Endpoint:**

```bash
GET /v3/motorsport/drivers
  ?api_token=YOUR_TOKEN
  &include=metadata
```

{% hint style="info" %}
You can also use the lineups.driver.metadata nested include on the livescores or fixture endpoints to include this data for all drivers in a live or past fixture
{% endhint %}

**Response structure:**

```json
{
  "data": [
    {
      "id": 123,
      "name": "Charles Leclerc",
      "metadata": [
        {
          "id": 9082090,
          "metadatable_id": 37920804,
          "type_id": 109851,
          "value_type": "object",
          "values": {
            "season": "2018"
          }
        },
        {
          "id": 9082118,
          "metadatable_id": 37920804,
          "type_id": 109852,
          "value_type": "object",
          "values": {
            "track": "Albert Park"
          }
        },
        {
          "id": 9082145,
          "metadatable_id": 37920804,
          "type_id": 109853,
          "value_type": "object",
          "values": {
            "result": "P13"
          }
        }
      ]
    }
  ]
}
```

{% hint style="info" %}
A full list of available types can be found here: [Metadata & Per-Season Data Type Reference](/v3/motorsport-api/welcome/metadata-and-per-season-data-type-reference.md#driver)
{% endhint %}

#### Code Example: Retrieving driver information

{% code expandable="true" %}

```javascript
const typesCache = new Map();

async function getDriverDetails(apiToken) {
  const response = await fetch(
    `https://api.sportmonks.com/v3/motorsport/drivers` +
      `?api_token=${apiToken}` +
      `&include=metadata` +
      `&per_page=50`
  );

  const json = await response.json();
  const drivers = Array.isArray(json.data) ? json.data : [];

  return drivers
    .map((driver) => {
      const metadata = (driver.metadata ?? []);

      const driverData = {};

      for (const meta of metadata) {
        const type = typesCache.get(meta.type_id);
        if (!type || meta.values == null) continue;

        const key = type.developer_name?.toLowerCase();
        if (!key) continue;

        let value;

        const entries = Object.entries(meta.values);

        // Single-property object - unwrap
        if (entries.length === 1) {
          value = entries[0][1];
        } else {
          // Multiple properties - keep full object
          value = meta.values;
        }

        driverData[key] = value;
      }

      return {
        driver_id: driver.id,
        driver_name: driver.name,
        ...driverData,
      };
    });
}

// Pre-load types
async function loadTypes(apiToken, page = 1) {
  const response = await fetch(
    `https://api.sportmonks.com/v3/core/types?api_token=${apiToken}&filter=populate&page=${page}`
  );
  const data = await response.json();

  data.data.forEach(type => {
    typesCache.set(type.id, type);
  });

  // Recursively fetch the next page, if available
  if(data.pagination.has_more) {
    await loadTypes(apiToken, page + 1)
  }
}

const API_TOKEN = 'YOUR_TOKEN';

loadTypes(API_TOKEN) // Load types before retrieving data
  .then(async () => await getDriverDetails(API_TOKEN))
  .then(console.log);
```

{% endcode %}

***

### Team information (per season)

In v3, teams now have richer season-specific information via the `seasondetails` include. The advantage of this, is that the season-specific data for previous seasons will stay in place.

We'll cover the car and lead information here, however the data also contain previous team names and logos, so you can retrieve the older branding for displaying historical data.

#### Getting teams for a specific season

**v1 approach:**

```bash
GET /v1/teams/season/SEASON_ID
```

**v3 approach (Option 1 - All teams and data):**

```bash
GET /v3/motorsport/teams
  ?api_token=YOUR_TOKEN
  &include=seasondetails
```

**v3 approach (Option 2 - Specific season):**

```bash
GET /v3/motorsport/teams/seasons/26733
  ?api_token=YOUR_TOKEN
  &include=seasondetails
  &filters=participantseasondetailSeasons:26733
```

#### Understanding season details

The `seasondetails` include provides season-specific team information:

```json
{
  "id": 456,
  "name": "Red Bull Racing",
  "seasondetails": [
    {
      "id": 27,
      "participant_id": 276190,
      "season_id": 26733,
      "type_id": 109856,
      "data": {
        "color_code": "#0600ef"
      }
    },
    {
      "id": 38,
      "participant_id": 276190,
      "season_id": 26733,
      "type_id": 109857,
      "data": {
        "team_lead": "Laurent Mekies"
      }
    },
    {
      "id": 49,
      "participant_id": 276190,
      "season_id": 26733,
      "type_id": 109858,
      "data": {
        "technical_lead": "Pierre Wache"
      }
    },
    {
      "id": 82,
      "participant_id": 276190,
      "season_id": 26736,
      "type_id": 109854,
      "data": {
        "engine": "Red Bull Powertrains–Ford"
      }
    }
  ]
}
```

{% hint style="info" %}
A full list of available types can be found here: [Metadata & Per-Season Data Type Reference](/v3/motorsport-api/welcome/metadata-and-per-season-data-type-reference.md#team)
{% endhint %}

#### Code Example: Getting team details for the 2025 season

{% code expandable="true" %}

```javascript
const typesCache = new Map();

async function getTeamDetails(seasonId, apiToken) {
  const response = await fetch(
    `https://api.sportmonks.com/v3/motorsport/teams` +
      `?api_token=${apiToken}` +
      `&include=seasondetails` +
      `&filters=participantseasondetailSeasons:${seasonId}`
  );

  const json = await response.json();
  const teams = Array.isArray(json.data) ? json.data : [];

  return teams
    .map((team) => {
      const seasonDetails = (team.seasondetails ?? []).filter(
        (sd) => sd.season_id === seasonId
      );

      const seasonData = {};

      for (const detail of seasonDetails) {
        const type = typesCache.get(detail.type_id);
        if (!type || detail.data == null) continue;

        const key = type.developer_name?.toLowerCase();
        if (!key) continue;

        let value;

        const entries = Object.entries(detail.data);

        // Single-property object - unwrap
        if (entries.length === 1) {
          value = entries[0][1];
        } else {
          // Multiple properties - keep full object
          value = detail.data;
        }

        seasonData[key] = value;
      }

      return {
        team_id: team.id,
        team_name: seasonData.team_name ?? team.name,
        ...seasonData,
      };
    });
}

// Pre-load types
async function loadTypes(apiToken, page = 1) {
  const response = await fetch(
    `https://api.sportmonks.com/v3/core/types?api_token=${apiToken}&filter=populate&page=${page}`
  );
  const data = await response.json();

  data.data.forEach(type => {
    typesCache.set(type.id, type);
  });

  // Recursively fetch the next page, if available
  if(data.pagination.has_more) {
    await loadTypes(apiToken, page + 1)
  }
}

const API_TOKEN = 'YOUR_TOKEN';
const SEASON_ID = 25273;

loadTypes(API_TOKEN) // Load types before retrieving data
  .then(async () => await getTeamDetails(SEASON_ID, API_TOKEN))
  .then(console.log);
```

{% endcode %}

***

### Track information

In v1, track information was included in track responses. In v3, all track-specific metadata (length, direction and type) is now in a dedicated `metadata` object.

#### Accessing track data

**Endpoint:**

```bash
GET /v3/motorsport/venues
  ?api_token=YOUR_TOKEN
  &include=metadata
```

{% hint style="info" %}
You can also use the venue include on the livescores or fixture endpoints to include this data for any fixture
{% endhint %}

**Response structure:**

```json
{
  "data": [
    {
      "id": 123,
      "name": "Hungaroring",
      "metadata": [
        {
          "id": 9079931,
          "metadatable_id": 343588,
          "type_id": 109729,
          "value_type": "object",
          "values": {
            "length": "4.381 km"
          }
        },
        {
          "id": 9079963,
          "metadatable_id": 343588,
          "type_id": 109730,
          "value_type": "object",
          "values": {
            "direction": "clockwise"
          }
        },
        {
          "id": 9079995,
          "metadatable_id": 343588,
          "type_id": 109731,
          "value_type": "object",
          "values": {
            "type": "race-circuit"
          }
        }
      ]
    }
  ]
}
```

{% hint style="info" %}
A full list of available types can be found here: [Metadata & Per-Season Data Type Reference](/v3/motorsport-api/welcome/metadata-and-per-season-data-type-reference.md#venue)
{% endhint %}

#### Code Example: Retrieving track information

{% code expandable="true" %}

```javascript
const typesCache = new Map();

async function getTrackDetails(apiToken) {
  const response = await fetch(
    `https://api.sportmonks.com/v3/motorsport/venues` +
      `?api_token=${apiToken}` +
      `&include=metadata` +
      `&per_page=50`
  );

  const json = await response.json();
  const tracks = Array.isArray(json.data) ? json.data : [];

  return tracks
    .map((venue) => {
      const metadata = (venue.metadata ?? []);

      const trackData = {};

      for (const meta of metadata) {
        const type = typesCache.get(meta.type_id);
        if (!type || meta.values == null) continue;

        const key = type.developer_name?.toLowerCase();
        if (!key) continue;

        let value;

        const entries = Object.entries(meta.values);

        // Single-property object - unwrap
        if (entries.length === 1) {
          value = entries[0][1];
        } else {
          // Multiple properties - keep full object
          value = meta.values;
        }

        trackData[key] = value;
      }

      return {
        track_id: venue.id,
        track_name: venue.name,
        ...trackData,
      };
    });
}

// Pre-load types
async function loadTypes(apiToken, page = 1) {
  const response = await fetch(
    `https://api.sportmonks.com/v3/core/types?api_token=${apiToken}&filter=populate&page=${page}`
  );
  const data = await response.json();

  data.data.forEach(type => {
    typesCache.set(type.id, type);
  });

  // Recursively fetch the next page, if available
  if(data.pagination.has_more) {
    await loadTypes(apiToken, page + 1)
  }
}

const API_TOKEN = 'YOUR_TOKEN';

loadTypes(API_TOKEN) // Load types before retrieving data
  .then(async () => await getTrackDetails(API_TOKEN))
  .then(console.log);
```

{% endcode %}

***

### Quick reference: Common scenarios

#### Scenario 1: "I need race finishing positions"

**v1:**

```javascript
results.forEach(r => console.log(r.position));
```

**v3:**

```javascript
lineups.forEach(lineup => {
  const posDetail = lineup.details.find(d => d.type.code === 'position');
  console.log(posDetail?.data.position);
});
```

#### Scenario 2: "I need to check who DNF'd"

**v1:**

```javascript
const dnfs = results.filter(r => r.retired);
```

**v3:**

```javascript
const dnfs = lineups.filter(lineup => {
  const statusDetail = lineup.details.find(d => d.type.code === 'status');
  return statusDetail?.data.status === 'DNF';
});
```

#### Scenario 3: "I need fastest lap times"

**v1:**

```javascript
results.forEach(r => console.log(r.best_lap_time));
```

**v3:**

```javascript
lineups.forEach(lineup => {
  const fastLapDetail = lineup.details.find(d => d.type.code === 'fastest-lap');
  console.log(fastLapDetail?.data.time);
});
```

#### Scenario 4: "I need starting grid positions"

**v1:**

```javascript
results.forEach(r => console.log(r.grid));
```

**v3:**

```javascript
lineups.forEach(lineup => {
  console.log(lineup.grid_position);  // Direct property!
});
```

#### Scenario 5: "I need driver IDs"

**v1:**

```javascript
results.forEach(r => console.log(r.driver_id));
```

**v3:**

```javascript
lineups.forEach(lineup => {
  console.log(lineup.player_id);  // Note: player_id, not driver_id
});
```

***

### Migration checklist

Use this checklist when migrating from v1 to v3:

#### Data access

* Replace `results` array with `lineups` array
* Change `driver_id` to `player_id`
* Change `track_id` to `venue_id` (at fixture level)
* Update `grid` to `grid_position`

#### Result details

* Implement helper function to find details by type code
* Update time extraction: `driver_time` → `details[type=time].data.time`
* Update interval extraction: `driver_time_int` → `details[type=interval].data.interval`
* Update fastest lap: `best_lap_time` → `details[type=fastest-lap].data.time`
* Update fastest lap number: `fastest_lap` → `details[type=fastest-lap].data.lap_number`
* Update laps: `laps` → `details[type=laps].data.laps`
* Update pitstops: `pit` → `details[type=pitstops].data.stops`
* Update status: `retired` boolean → `details[type=status].data.status` enum

#### Includes

* Add `include=lineups` to fixture requests
* Add `include=lineups.details.type` for typed result data
* Add `include=lineups.driver` for driver names
* Add `include=lineups.driver.metadata` for debut info

#### Caching

* Implement types caching (30+ days)
* Implement states caching (30+ days)
* Cache type IDs for faster detail lookups

***

### Getting Help

If you encounter issues or have questions during migration:

* **Email**: <support@sportmonks.com>
* **Documentation**: [Motorsport API v3 Docs](https://docs.sportmonks.com/v3/motorsport-api)
* **Migration Guide**: [Main Migration Page](https://docs.sportmonks.com/v3/motorsport-api/tutorials-and-guides/guides/migrating-from-formula-one-api-v1-to-motorsport-api-v3)

We're here to help make your migration as smooth as possible!

***

**Tip:** Start by updating one endpoint at a time. Test thoroughly before moving to the next. The structure is consistent across all fixtures, so once you understand lineups and details, the pattern repeats everywhere.&#x20;


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.sportmonks.com/v3/motorsport-api/tutorials-and-guides/guides/understanding-race-results-driver-team-and-track-data-in-v3.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
