> ## Documentation Index
> Fetch the complete documentation index at: https://docs.framesports.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Get a single fixture

> Returns the fixture along with both sides. Each side includes:

* the team that played (id, name, jersey description, club),
* the full set of team-level stats (tackles, carries, points, lineouts,
  scrums, rucks, etc.),
* every player who took the field (`players`), keyed by jersey number,
  with per-game player stats attached.

Stats may be `null` on an inferred side (a side whose own game has not
been analysed separately — we derive the stats we can from the other
side's events, but e.g. `phase_counts` is not invertible). Consumers
should treat absent stat keys as "not available" rather than zero.




## OpenAPI

````yaml /api-reference/openapi.yaml get /api/v1/fixtures/{id}
openapi: 3.0.3
info:
  title: Framesports API
  version: 1.0.0
  description: >
    The Framesports API exposes read access to the same domain model used by the

    product: **accounts**, **fixtures**, **games**, and **events**.


    This reference documents the v1 surface. It is intentionally small — the API
    is

    used primarily by a small number of integration partners (e.g.
    broadcast-graphics

    tooling). If you need a capability that is not listed here, contact

    `support@framesports.ai` before building around private endpoints.


    ### Base URL


    ```

    https://app.framesports.ai

    ```


    ### Authentication


    All endpoints (except `POST /api/v1/auth` and `POST /api/v1/users`) require
    a

    bearer token in the `Authorization` header:


    ```

    Authorization: Bearer <token>

    ```


    To get a token, sign in to Framesports and open

    [API tokens](https://2.framesports.ai/settings/my/api_tokens).

    Choose **Create token**, give it a name, pick the club it is for and how
    long

    it lasts. The token is shown once, so copy it straight away. The same page

    shows the club's account ID and lists your tokens, and **Delete token**
    stops

    one on its next request.

    A token acts as the person who made it; every request has their access.


    ### Account scoping


    Most endpoints are scoped to a single account (the multi-tenant unit — a
    club or

    governing body). The account is selected in priority order:


    1. The `Account-Id` header on the request (prefix ID, e.g.
    `acct_Ex4mpLeAcc0unt1d567890`).
       The user must have access to that account directly, or through a governance
       relationship to it.
    2. Otherwise, the club the token was made for.

    3. Otherwise, the account stored in the user's session (for
    browser-originated
       calls).
    4. Otherwise, the user's newest / fallback account.


    > **Note on the header name.** The server currently reads `Account-Id`, not

    > `X-Account-Id`. If you send `X-Account-Id`, it is silently ignored and the

    > request falls back to rule 2 or later, which may not be the account you
    intended.


    Responses when the header is present but invalid:


    | Situation | Status |

    | --- | --- |

    | Account does not exist | `404 Not Found` |

    | Account exists but the user cannot access it | `403 Forbidden` |


    ### IDs


    Object IDs in the API are always **prefix IDs** — an opaque string with a

    type-specific prefix:


    | Type | Prefix | Example |

    | --- | --- | --- |

    | Account | `acct_` | `acct_Ex4mpLeAcc0unt1d567890` |

    | Fixture | `fxt_` | `fxt_Ex4mpLeF1xtur31d34567890` |

    | Game | `game_` | `game_Ex4mpLeG4m31d4567890abc` |

    | Event | `evnt_` | `evnt_Ex4mpLe3v3nt1d4567890ab` |

    | Team | `team_` | `team_Ex4mpLeTe4m1d4567890abc` |

    | Player | `pl_` | `pl_Ex4mpLePL4y3r1d4567890abc` |

    | Player game involvement | `pgi_` | `pgi_Ex4mpLePG1nv0Lv3m3nt4aa` |


    Never pass raw integer IDs — the API only accepts prefix IDs for public
    lookups.


    ### Pagination


    List endpoints use [Pagy](https://ddnexus.github.io/pagy/) with
    JSON:API-style

    links. Pass `page` and `per_page` as query parameters (or use the URLs in
    the

    `links` object of the response):


    ```json

    {
      "links": {
        "first": "/api/v1/games/.../events?page[page]=1",
        "last":  "/api/v1/games/.../events?page[page]=9",
        "prev":  null,
        "next":  "/api/v1/games/.../events?page[page]=2"
      },
      "data": [ /* ... */ ]
    }

    ```


    ### Errors


    Errors are returned with an appropriate HTTP status and (usually) a JSON
    body of

    the form `{"error": "..."}`. Unauthenticated responses (`401`) currently
    return

    an empty body with `content-type: text/html` — treat any `401` as "token
    missing

    or invalid" regardless of body.


    ### Versioning & stability


    The `/api/v1/` prefix denotes a stable version of the API. Fields may be
    **added**

    without notice; fields will not be **removed** or change meaning without a
    new

    major version. Any field whose object includes `"deprecated": true` is
    scheduled

    for removal — migrate off it.
  contact:
    name: Framesports support
    email: support@framesports.ai
servers:
  - url: https://app.framesports.ai
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Authentication
    description: Exchange credentials for an API token, or invalidate the current session.
  - name: Accounts
    description: The accounts the authenticated user can access.
  - name: Me
    description: The authenticated user.
  - name: Fixtures
    description: |
      A fixture is a single rugby match with two sides. Each side carries the
      team that played, the roster that took the field, and the full set of
      team-level stats. This is the endpoint to use for broadcast graphics,
      season roll-ups, or anything else that wants "the match" as a single
      object.
  - name: Games
    description: |
      **Deprecated — prefer `Fixtures`.** A game represents one side of a
      fixture (the billing account's own side). The `/api/v1/games` endpoints
      are kept for backwards compatibility; new integrations should consume
      `/api/v1/fixtures` instead, which returns both sides and all stats in
      one payload.
  - name: Events
    description: |
      Tagged plays within a fixture (tackles, carries, tries, etc.). Prefer
      `GET /api/v1/fixtures/{fixture_id}/events`, which spans both sides of
      the match and resolves each event's team + player into prefix IDs. The
      `/api/v1/games/{game_id}/events` endpoint is kept for backwards
      compatibility — it returns only one side and leaves you to map
      `"Home"` / `"Opp"` + jersey numbers yourself.
paths:
  /api/v1/fixtures/{id}:
    get:
      tags:
        - Fixtures
      summary: Get a single fixture
      description: |
        Returns the fixture along with both sides. Each side includes:

        * the team that played (id, name, jersey description, club),
        * the full set of team-level stats (tackles, carries, points, lineouts,
          scrums, rucks, etc.),
        * every player who took the field (`players`), keyed by jersey number,
          with per-game player stats attached.

        Stats may be `null` on an inferred side (a side whose own game has not
        been analysed separately — we derive the stats we can from the other
        side's events, but e.g. `phase_counts` is not invertible). Consumers
        should treat absent stat keys as "not available" rather than zero.
      operationId: getFixture
      parameters:
        - $ref: '#/components/parameters/AccountIdHeader'
        - in: path
          name: id
          required: true
          schema:
            type: string
          example: fxt_Ex4mpLeF1xtur31d34567890
      responses:
        '200':
          description: Fixture with both sides.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Fixture'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
components:
  parameters:
    AccountIdHeader:
      in: header
      name: Account-Id
      required: false
      description: |
        Prefix ID of the account to scope this request to. Settings → API tokens
        shows it next to each token. If omitted, the server uses the club the
        token was made for (then the session account, then the newest account).
        Must be `Account-Id`: `X-Account-Id` is not read.
      schema:
        type: string
        example: acct_Ex4mpLeAcc0unt1d567890
  schemas:
    Fixture:
      type: object
      description: A single rugby match, with both sides and all stats.
      properties:
        id:
          type: string
          example: fxt_Ex4mpLeF1xtur31d34567890
        name:
          type: string
          nullable: true
          example: Sunnybank v Wests | 2nd Grade | Round 3
        recorded_at:
          type: string
          format: date-time
          nullable: true
        competition:
          $ref: '#/components/schemas/Competition'
          nullable: true
        sides:
          type: array
          description: Always exactly two sides.
          items:
            $ref: '#/components/schemas/FixtureSide'
    Competition:
      type: object
      properties:
        id:
          type: integer
          example: 42
        name:
          type: string
          example: Queensland Premier Rugby 2026
    FixtureSide:
      type: object
      properties:
        team:
          $ref: '#/components/schemas/Team'
        jersey_description:
          type: string
          nullable: true
          description: >-
            The kit this team wore in this specific fixture. Stored per-match
            because teams can swap colours.
          example: Green and yellow
        data_source:
          type: string
          enum:
            - analyzed
            - inferred
        stats:
          $ref: '#/components/schemas/SideStats'
          description: Team-level totals for this side of the fixture.
        players:
          type: array
          description: >-
            Players who took the field for this side, ordered by jersey number.
            Includes per-game player stats when a matching analysed player
            record exists.
          items:
            $ref: '#/components/schemas/FixturePlayer'
    Team:
      type: object
      properties:
        id:
          type: string
          example: team_Ex4mpLeTe4m1d4567890abc
        name:
          type: string
          example: Wests Senior 2nd Team Men's
        abbreviated_name:
          type: string
          example: WST
        gender:
          type: string
          enum:
            - Men's
            - Women's
            - Other
        age:
          type: string
          enum:
            - Senior
            - Under 23
            - Under 21
            - Under 20
            - Under 19
            - Under 18
            - Under 17
            - Under 16
            - Under 15
            - Under 14
            - Under 13
            - Under 12
            - Under 11
            - Other
        level:
          type: string
          enum:
            - 1st Team
            - 2nd Team
            - 3rd Team
            - 4th Team
            - 5th Team
            - 6th Team
            - 7th Team
            - Other
        club:
          type: object
          nullable: true
          properties:
            name:
              type: string
              example: Wests Rugby
    SideStats:
      type: object
      description: |
        Team totals for one side of a fixture. Counts are integers; rates are
        decimals. Any field may be `null` on an inferred side when the source
        event doesn't support the inversion (e.g. `phase_counts`).
      properties:
        minutes_played:
          type: number
          nullable: true
          format: float
        involvement_count:
          type: integer
          nullable: true
        work_rate:
          type: number
          nullable: true
          format: float
          description: Involvements per player per minute.
        work_rate_per_minute:
          type: number
          nullable: true
          format: float
          description: Total involvements per minute (across all players).
        player_count:
          type: integer
          nullable: true
        ball_in_play_ms:
          type: integer
          nullable: true
        positive_tackles:
          type: integer
          nullable: true
        negative_tackles:
          type: integer
          nullable: true
        tackle_assists:
          type: integer
          nullable: true
        tackles_missed:
          type: integer
          nullable: true
        positive_carries:
          type: integer
          nullable: true
        negative_carries:
          type: integer
          nullable: true
        passes:
          type: integer
          nullable: true
        offloads:
          type: integer
          nullable: true
        turnovers_won_clean:
          type: integer
          nullable: true
        turnovers_won_penalty:
          type: integer
          nullable: true
        turnovers_lost:
          type: integer
          nullable: true
        knock_ons:
          type: integer
          nullable: true
        infringements:
          type: integer
          nullable: true
        tries:
          type: integer
          nullable: true
        successful_conversions:
          type: integer
          nullable: true
        missed_conversions:
          type: integer
          nullable: true
        successful_penalty_kicks:
          type: integer
          nullable: true
        missed_penalty_kicks:
          type: integer
          nullable: true
        successful_drop_kicks:
          type: integer
          nullable: true
        missed_drop_kicks:
          type: integer
          nullable: true
        points_scored:
          type: integer
          nullable: true
        kicks:
          type: integer
          nullable: true
        kick_distance:
          type: integer
          nullable: true
        ruck_arrivals:
          type: integer
          nullable: true
        ruck_count:
          type: integer
          nullable: true
        average_ruck_duration:
          type: number
          nullable: true
          format: float
        scrums_won:
          type: integer
          nullable: true
        scrums_lost:
          type: integer
          nullable: true
        scrums_reset:
          type: integer
          nullable: true
        lineouts_won:
          type: integer
          nullable: true
        lineouts_lost:
          type: integer
          nullable: true
        lineouts_reset:
          type: integer
          nullable: true
        twenty_two_meter_entries:
          type: integer
          nullable: true
        restart_receives_won:
          type: integer
          nullable: true
        restart_receives_lost:
          type: integer
          nullable: true
        mauls:
          type: integer
          nullable: true
        average_phases_built:
          type: number
          nullable: true
          format: float
        phase_counts:
          type: array
          nullable: true
          items:
            type: integer
          description: Count of phases of each length. Null on inferred sides.
        try_progress_ratios:
          type: array
          nullable: true
          items:
            type: number
            format: float
            minimum: 0
            maximum: 1
          description: 0..1 positions of each try along the match clock.
        points_per_twenty_two_meter_entry:
          type: number
          nullable: true
          format: float
        twenty_two_meter_conversion_percentage:
          type: number
          nullable: true
          format: float
        dominant_tackle_percent:
          type: number
          nullable: true
          format: float
        tackle_success_percent:
          type: number
          nullable: true
          format: float
        dominant_carry_percent:
          type: number
          nullable: true
          format: float
        set_piece_success_percent:
          type: number
          nullable: true
          format: float
    FixturePlayer:
      type: object
      properties:
        id:
          type: string
          example: pgi_Ex4mpLePG1nv0Lv3m3nt4aa
          description: Prefix ID of the player-game involvement row.
        player_id:
          type: string
          nullable: true
          description: >
            Prefix ID of the linked `Player` record — stable across every game
            the

            same real person plays. Use this (not `name`) to aggregate a
            player's

            stats across fixtures. `null` when the jersey isn't linked to a
            player

            yet.
          example: pl_Ex4mpLePL4y3r1d4567890abc
        name:
          type: string
          example: Hamish Ward
          description: |
            Display name as entered on the team sheet. Can be `"Name Withheld"`
            when the source feed masks the real name — two rows in the same
            fixture may share this value. Use `player_id`; don't use `name` as
            an identity key.
        jersey_number:
          type: integer
          example: 1
        stats:
          $ref: '#/components/schemas/PlayerStats'
          nullable: true
          description: >-
            Per-game stats. `null` if this player isn't linked to a `Player`
            record (no stats are computed for unlinked involvements).
    PlayerStats:
      type: object
      description: Per-game totals for one player. All counts integers.
      properties:
        minutes_played:
          type: number
          format: float
        involvement_count:
          type: integer
        work_rate_per_minute:
          type: number
          nullable: true
          format: float
        positive_tackles_made:
          type: integer
        negative_tackles_made:
          type: integer
        tackle_assists:
          type: integer
        tackles_missed:
          type: integer
        positive_carries:
          type: integer
        negative_carries:
          type: integer
        passes:
          type: integer
        offloads:
          type: integer
        turnovers_won_clean:
          type: integer
        turnovers_won_penalty:
          type: integer
        turnovers_lost:
          type: integer
        knock_ons:
          type: integer
        infringements:
          type: integer
        tries:
          type: integer
        successful_conversions:
          type: integer
        missed_conversions:
          type: integer
        successful_penalty_kicks:
          type: integer
        missed_penalty_kicks:
          type: integer
        successful_drop_kicks:
          type: integer
        missed_drop_kicks:
          type: integer
        points_scored:
          type: integer
        kicks:
          type: integer
        kick_distance:
          type: integer
        ruck_arrivals:
          type: integer
  responses:
    Unauthorized:
      description: Token missing or invalid.
    Forbidden:
      description: The account exists but the user cannot access it.
    NotFound:
      description: The account or record does not exist.
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: opaque
      description: |
        Create a token on **Settings → API tokens** in Framesports. It is shown
        once, when you create it.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.