# Lineups

### What does the include do?

The `lineups` include allows you to retrieve comprehensive information about the players who participated in a fixture, including starting eleven, substitutes, formations, jersey numbers, and positions on the field. This is essential for displaying team sheets, analysing tactical formations, and tracking player participation.

### Why use lineups?

The lineups include is crucial for:

* **Team sheet displays**: Show starting XIs and substitutes
* **Formation visualisation**: Display tactical formations (4-4-2, 4-3-3, etc.)
* **Player participation tracking**: See who played and in what position
* **Tactical analysis**: Analyse team formations and player positioning
* **Statistics context**: Understand player roles for performance analysis
* **Fantasy football**: Track player appearances and positions

### Requesting lineups

To retrieve lineup information for a fixture, use the following include:

```
https://api.sportmonks.com/v3/football/fixtures/{fixture_id}
?api_token=YOUR_TOKEN&include=lineups
```

Example: Get lineups for a fixture (fixture ID: 18535517)

```
https://api.sportmonks.com/v3/football/fixtures/18535517
?api_token=YOUR_TOKEN&include=lineups
```

### Response structure

When you include lineups in your request, you'll receive an array of lineup objects for all players:

```json
{
  "data": {
    "id": 19568568,
    "sport_id": 1,
    "league_id": 2,
    "season_id": 25580,
    "stage_id": 77478049,
    "group_id": null,
    "aggregate_id": null,
    "round_id": 388953,
    "state_id": 5,
    "venue_id": 131,
    "name": "Paris Saint Germain vs Tottenham Hotspur",
    "starting_at": "2025-11-26 20:00:00",
    "result_info": "Paris Saint Germain won after full-time.",
    "leg": "1/1",
    "details": null,
    "length": 90,
    "placeholder": false,
    "has_odds": true,
    "has_premium_odds": true,
    "starting_at_timestamp": 1764187200,
    "lineups": [
      {
        "id": 14671587223,
        "sport_id": 1,
        "fixture_id": 19568568,
        "player_id": 133779,
        "team_id": 6,
        "position_id": 24,
        "formation_field": "1:1",
        "type_id": 11,
        "formation_position": 1,
        "player_name": "Guglielmo Vicario",
        "jersey_number": 1
      },
      {
        "id": 14671603915,
        "sport_id": 1,
        "fixture_id": 19568568,
        "player_id": 33079,
        "team_id": 6,
        "position_id": 25,
        "formation_field": null,
        "type_id": 12,
        "formation_position": null,
        "player_name": "Kevin Danso",
        "jersey_number": 4
      }
    ]
  }
}
```

### Field descriptions

| Field                | Type         | Description                                                       |
| -------------------- | ------------ | ----------------------------------------------------------------- |
| `id`                 | integer      | Unique identifier for this lineup entry                           |
| `sport_id`           | integer      | Sport type identifier (1 = football)                              |
| `fixture_id`         | integer      | Associated fixture ID                                             |
| `player_id`          | integer      | Unique player identifier                                          |
| `team_id`            | integer      | Team identifier                                                   |
| `position_id`        | integer      | Player's position type (use nested include for details)           |
| `formation_field`    | string/null  | Player's position in formation (1-12), null if not in starting XI |
| `type_id`            | integer      | Player participation type (see below)                             |
| `formation_position` | integer/null | Additional formation position data                                |
| `player_name`        | string       | Player's name                                                     |
| `jersey_number`      | integer      | Jersey number worn in match                                       |

### Type IDs - Player participation

| Type ID | Meaning                              |
| ------- | ------------------------------------ |
| 11      | Starting player (in the starting XI) |
| 12      | Substitute (on the bench)            |

**Filter starters only:**

```python
starters = [p for p in lineups if p['type_id'] == 11]
```

### Formation field numbering (1-12)

The `formation_field` represents a player's position in the formation, numbered 1-12. This field is **null** for players not in the starting XI.

The formation numbers follow a sequential pattern:

* Formation starts at the defence on the right and goes to the left
* Numbers then continue to the midfielders, also from right to left
* Finally, numbers continue to the attackers from right to left
* Position 12 (or sometimes 1 depending on data source) is the goalkeeper

#### Example: 4-4-2 Formation

![](https://3469464275-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9zsNGu3HAzSnl5DWBD3Y%2Fuploads%2FT8fY5pPiraAVl3TQz5eJ%2F4-4-2.webp?alt=media\&token=0da4a106-56fb-4fb7-827f-f7ffa7b3e82d)

#### Example: 4-3-3 Formation

![](https://3469464275-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9zsNGu3HAzSnl5DWBD3Y%2Fuploads%2FNvcoGlr7ztFQqzc5atvh%2F4-3-3.webp?alt=media\&token=17d7de03-8419-477e-8d5a-1f3a83c22eda)

**Important:** Always use `formation_field` for positioning, not player position IDs. The numbering may vary between matches and data sources.

### Position IDs and nested includes

To get detailed position information, use nested includes rather than relying on position\_id values:

**General positions (use nested include):**

```
&include=lineups.position
```

Returns: Goalkeeper, Defender, Midfielder, Attacker

**Detailed positions (use nested include):**

```
&include=lineups.detailedPosition
```

Returns: Specific positions like Left Back, Center Forward, Defensive Midfielder, etc.

### Nested includes

#### lineups.details.type

Get detailed statistics for each player's performance:

```
&include=lineups.details.type
```

This returns player fixture statistics (shots, passes, tackles, etc.).

#### lineups.position

Get general position information:

```
&include=lineups.position
```

Returns position data (Goalkeeper, Defender, Midfielder, Attacker).

#### lineups.player

Get complete player information:

```
&include=lineups.player
```

Returns full player profile including country, height, weight, birth date.

#### lineups.type

Get participation type information:

```
&include=lineups.type
```

Returns whether player is in starting lineup or on bench.

#### lineups.detailedPosition

Get specific position information:

```
&include=lineups.detailedPosition
```

Returns detailed position (Left Back, Center Forward, etc.).

### Code examples

#### Python example

```python
import requests

API_TOKEN = "YOUR_TOKEN"
FIXTURE_ID = 19568568

def get_match_lineups():
    url = f"https://api.sportmonks.com/v3/football/fixtures/{FIXTURE_ID}"
    params = {
        "api_token": API_TOKEN,
        "include": "lineups.player,lineups.position"
    }
    
    response = requests.get(url, params=params)
    data = response.json()
    lineups = data['data'].get('lineups', [])
    
    return lineups

# Separate lineups by team
def separate_by_team(lineups):
    teams = {}
    for player in lineups:
        team_id = player['team_id']
        if team_id not in teams:
            teams[team_id] = []
        teams[team_id].append(player)
    return teams

# Get starters and substitutes
def categorise_players(lineups):
    starters = [p for p in lineups if p['type_id'] == 11]
    substitutes = [p for p in lineups if p['type_id'] == 12]
    
    return {
        'starters': starters,
        'substitutes': substitutes
    }

# Display starting XI
def display_starting_xi(starters):
    # Sort by formation_field for positional display
    starters = sorted(
        [p for p in starters if p.get('formation_field')],
        key=lambda x: int(x.get('formation_field').split(':')[0])
    )
    
    print("Starting XI:\n")
    for player in starters:
        position = player.get('formation_field', '?')
        jersey = player.get('jersey_number', '?')
        name = player['player_name']
        print(f"#{jersey} {name} (Position: {position})")

# Build formation visualisation
def visualise_formation(starters):
    """Create a simple text-based formation display"""
    formation_dict = {}
    
    for player in starters:
        pos = player.get('formation_field')
        if pos:
            # Handle formation_field as "X:Y" format
            try:
                main_pos = int(pos.split(':')[0])
                formation_dict[main_pos] = {
                    'name': player['player_name'],
                    'jersey': player['jersey_number']
                }
            except (ValueError, AttributeError):
                continue
    
    # Group by lines (approximate)
    gk = formation_dict.get(1, {})
    defenders = [formation_dict.get(i) for i in [2, 3, 4, 5, 6] if i in formation_dict]
    midfielders = [formation_dict.get(i) for i in [7, 8, 9] if i in formation_dict]
    attackers = [formation_dict.get(i) for i in [10, 11, 12] if i in formation_dict]
    
    print("\n=== Formation ===\n")
    
    # Attackers
    if attackers:
        print("ATTACKERS:")
        for p in attackers:
            if p:
                print(f"  {p['jersey']} {p['name']}")
    
    # Midfielders
    if midfielders:
        print("\nMIDFIELDERS:")
        for p in midfielders:
            if p:
                print(f"  {p['jersey']} {p['name']}")
    
    # Defenders
    if defenders:
        print("\nDEFENDERS:")
        for p in defenders:
            if p:
                print(f"  {p['jersey']} {p['name']}")
    
    # Goalkeeper
    if gk:
        print(f"\nGOALKEEPER:")
        print(f"  {gk['jersey']} {gk['name']}")

# Usage
lineups = get_match_lineups()
by_team = separate_by_team(lineups)

for team_id, players in by_team.items():
    print(f"\n=== TEAM {team_id} ===")
    categories = categorise_players(players)
    
    print(f"\nStarters: {len(categories['starters'])}")
    display_starting_xi(categories['starters'])
    visualise_formation(categories['starters'])
    
    print(f"\nSubstitutes: {len(categories['substitutes'])}")
```

#### JavaScript example

```javascript
const API_TOKEN = "YOUR_TOKEN";
const FIXTURE_ID = 19568568;

async function getMatchLineups() {
    const url = `https://api.sportmonks.com/v3/football/fixtures/${FIXTURE_ID}`;
    const params = new URLSearchParams({
        api_token: API_TOKEN,
        include: "lineups.player,lineups.position"
    });

    try {
        const response = await fetch(`${url}?${params}`);
        const data = await response.json();
        return data.data.lineups || [];
    } catch (error) {
        console.error("Error fetching lineups:", error);
        return [];
    }
}

function categorisePlayer(lineups) {
    return {
        starters: lineups.filter(p => p.type_id === 11),
        substitutes: lineups.filter(p => p.type_id === 12)
    };
}

function buildLineupsDisplay(lineups) {
    const container = document.createElement('div');
    container.className = 'lineups-container';
    
    // Separate by team
    const teams = {};
    lineups.forEach(player => {
        if (!teams[player.team_id]) {
            teams[player.team_id] = [];
        }
        teams[player.team_id].push(player);
    });
    
    // Display each team
    Object.entries(teams).forEach(([teamId, players]) => {
        const teamSection = document.createElement('div');
        teamSection.className = 'team-lineups';
        
        const categories = categorisePlayer(players);
        
        // Starting XI
        const startingXI = document.createElement('div');
        startingXI.className = 'starting-xi';
        startingXI.innerHTML = '<h3>Starting XI</h3>';
        
        // Sort by formation_field
        const starters = categories.starters
            .filter(p => p.formation_field)
            .sort((a, b) => {
                const aPos = parseInt(a.formation_field.split(':')[0]) || 99;
                const bPos = parseInt(b.formation_field.split(':')[0]) || 99;
                return aPos - bPos;
            });
        
        starters.forEach(player => {
            const playerDiv = document.createElement('div');
            playerDiv.className = 'player-card';
            playerDiv.innerHTML = `
                <span class="jersey">${player.jersey_number}</span>
                <span class="name">${player.player_name}</span>
                <span class="position">Pos: ${player.formation_field || '?'}</span>
            `;
            startingXI.appendChild(playerDiv);
        });
        
        teamSection.appendChild(startingXI);
        
        // Substitutes
        if (categories.substitutes.length > 0) {
            const subsDiv = document.createElement('div');
            subsDiv.className = 'substitutes';
            subsDiv.innerHTML = '<h4>Substitutes</h4>';
            
            categories.substitutes.forEach(player => {
                const playerDiv = document.createElement('div');
                playerDiv.className = 'player-card sub';
                playerDiv.innerHTML = `
                    <span class="jersey">${player.jersey_number}</span>
                    <span class="name">${player.player_name}</span>
                `;
                subsDiv.appendChild(playerDiv);
            });
            
            teamSection.appendChild(subsDiv);
        }
        
        container.appendChild(teamSection);
    });
    
    document.body.appendChild(container);
}

// Build formation diagram
function buildFormationDiagram(starters) {
    const field = document.createElement('div');
    field.className = 'football-field';
    field.style.cssText = `
        width: 600px;
        height: 800px;
        background: #228B22;
        position: relative;
        border: 2px solid white;
    `;
    
    starters.forEach(player => {
        const formationPos = player.formation_field ? parseInt(player.formation_field.split(':')[0]) : null;
        if (!formationPos) return;
        
        // Calculate position on field (simplified)
        let x = 50; // percentage
        let y = 80 - (formationPos * 6); // approximate
        
        const playerDot = document.createElement('div');
        playerDot.className = 'player-position';
        playerDot.style.cssText = `
            position: absolute;
            left: ${x}%;
            top: ${y}%;
            transform: translate(-50%, -50%);
            background: white;
            border-radius: 50%;
            width: 40px;
            height: 40px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-weight: bold;
        `;
        playerDot.textContent = player.jersey_number;
        playerDot.title = player.player_name;
        
        field.appendChild(playerDot);
    });
    
    document.body.appendChild(field);
}

// Run
getMatchLineups().then(lineups => {
    buildLineupsDisplay(lineups);
    const starters = lineups.filter(p => p.type_id === 11 && p.formation_field);
    buildFormationDiagram(starters);
});
```

### Best practices

1. **Always use type\_id to filter**: Use type\_id 11 for starters and type\_id 12 for substitutes.
2. **Check formation\_field before using**: formation\_field is null for bench players. Always validate.
3. **Sort by formation\_field**: When displaying starting XI, sort by formation\_field for positional order.
4. **Use nested includes for position data**: Always use `lineups.position` or `lineups.detailedPosition` for reliable position information rather than hardcoding position IDs.
5. **Use nested includes for complete data**: Include `lineups.player` to get full player data in one request.
6. **Cache completed matches**: Lineups don't change after a match ends.
7. **Always show jersey numbers**: Jersey numbers are important for display and identification.
8. **Handle null values gracefully**: formation\_field, formation\_position, and other fields may be null. Always validate.

### Related includes

* **Events** - Cross-reference substitution events with lineup changes
* **Formations** - Get team formation strings (4-4-2, 4-3-3, etc.)
* **Statistics** - Get player performance statistics via `lineups.details.type`
* **Participants** - Get team and player relationship data

### Summary

The `lineups` include provides comprehensive player participation data including starting XIs, substitutes, formations, jersey numbers, and field positions. With `type_id` filtering (11 = starters, 12 = substitutes) and `formation_field` positioning (1-12 sequential formation positions), it enables building team sheets, formation visualisations, and tactical analysis. Always use nested includes like `lineups.player` and `lineups.position` for complete player information and reliable position data in a single API call.
