{
  "openapi": "3.1.0",
  "info": {
    "title": "Vertex API",
    "version": "v2"
  },
  "servers": [
    {
      "url": "https://trading.vertex-api.com",
      "description": "Production: live data and real bet placement."
    }
  ],
  "security": [
    {
      "API Key": []
    }
  ],
  "tags": [
    {
      "name": "Catalog",
      "description": "Reference data: sports, market types, competitions, participants. Call these first to learn what's available."
    },
    {
      "name": "Events & Markets",
      "description": "Find events, list their markets and selections, and check price history and freshness before placing a bet."
    },
    {
      "name": "Bets",
      "description": "Place bets and retrieve bet history."
    }
  ],
  "paths": {
    "/api/v2/sports": {
      "get": {
        "tags": [
          "Catalog"
        ],
        "operationId": "getSports",
        "summary": "List sports",
        "description": "Returns every sport your API key has access to. This is the entry point to the catalog: every pricing call and bet placement is ultimately scoped to a sport returned here. Use `expand` to fold related entities into the same call instead of issuing follow-up requests.",
        "parameters": [
          {
            "name": "expand",
            "in": "query",
            "description": "Fold related entities into each sport.\n\n- `market_types`: market families (moneyline, totals, spreads, etc.) priced for this sport.",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "market_types"
                ]
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of sports the API key has access to.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponseSportDTO"
                },
                "examples": {
                  "default": {
                    "summary": "Default response (no expand)",
                    "value": {
                      "data": [
                        {
                          "id": 1,
                          "name": "Soccer",
                          "code": "soc"
                        },
                        {
                          "id": 2,
                          "name": "Basketball",
                          "code": "bask"
                        },
                        {
                          "id": 3,
                          "name": "Tennis",
                          "code": "ten"
                        },
                        {
                          "id": 11,
                          "name": "MMA",
                          "code": "mma"
                        },
                        {
                          "id": 12,
                          "name": "Boxing",
                          "code": "box"
                        }
                      ]
                    }
                  },
                  "expanded": {
                    "summary": "With expand=market_types",
                    "value": {
                      "data": [
                        {
                          "id": 1,
                          "name": "Soccer",
                          "code": "soc",
                          "market_types": [
                            {
                              "id": 1,
                              "code": "moneyline",
                              "name": "Moneyline",
                              "category": "result"
                            },
                            {
                              "id": 2,
                              "code": "spreads",
                              "name": "Asian Handicap",
                              "category": "spreads"
                            },
                            {
                              "id": 3,
                              "code": "totals",
                              "name": "Total goals",
                              "category": "total"
                            }
                          ]
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v2/sports/{id}": {
      "get": {
        "tags": [
          "Catalog"
        ],
        "operationId": "getSport",
        "summary": "Get a sport",
        "description": "Fetch a single sport by its id. Returns the same sport object you get from the list endpoint, including its code. Use this when you already hold a sport id and want its current details.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Sport id from `GET /sports`.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The sport.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SportDTO"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v2/competitions": {
      "get": {
        "tags": [
          "Catalog"
        ],
        "operationId": "getCompetitions",
        "summary": "List competitions",
        "description": "Returns the competitions your API key has access to: leagues, tournaments, championships, etc. Filter by `sport_id` to scope to one sport; use `expand=sport` to embed the full sport object inline instead of issuing a follow-up call to `/sports`.",
        "parameters": [
          {
            "name": "sport_id",
            "in": "query",
            "description": "Restrict results to competitions in this sport. Use an `id` from `GET /sports`.",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "expand",
            "in": "query",
            "description": "Fold related entities into each competition.\n\n- `sport`: embed the full `SportDTO` alongside the `sport_id`.",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "sport"
                ]
              }
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "1-based page number (default 1).",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 1,
              "minimum": 1
            }
          },
          {
            "name": "page_size",
            "in": "query",
            "required": false,
            "description": "Records per page (default 100, max 1000). Values above 1000 are rejected with an HTTP 400 in the framework error shape — see the [Errors](/docs/rest-api/errors) page.",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 100,
              "minimum": 1,
              "maximum": 1000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of competitions the API key has access to.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponseCompetitionDTO"
                },
                "examples": {
                  "default": {
                    "summary": "Default response (no expand)",
                    "value": {
                      "data": [
                        {
                          "id": 7,
                          "sport_id": 6,
                          "name": "National League"
                        },
                        {
                          "id": 14,
                          "sport_id": 6,
                          "name": "NHL"
                        },
                        {
                          "id": 22,
                          "sport_id": 1,
                          "name": "Premier League"
                        },
                        {
                          "id": 31,
                          "sport_id": 1,
                          "name": "La Liga"
                        }
                      ],
                      "pagination": {
                        "page": 1,
                        "size": 100,
                        "total": 4
                      }
                    }
                  },
                  "expanded": {
                    "summary": "With expand=sport",
                    "value": {
                      "data": [
                        {
                          "id": 22,
                          "sport_id": 1,
                          "sport": {
                            "id": 1,
                            "name": "Soccer",
                            "code": "soc"
                          },
                          "name": "Premier League"
                        }
                      ],
                      "pagination": {
                        "page": 1,
                        "size": 100,
                        "total": 4
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v2/competitions/{id}": {
      "get": {
        "tags": [
          "Catalog"
        ],
        "operationId": "getCompetition",
        "summary": "Get a competition",
        "description": "Fetch a single competition by its id. Returns the same competition object you get from the list endpoint. Use this when you already hold a competition id and want its current details.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Competition id from `GET /competitions`.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The competition.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CompetitionDTO"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v2/participants": {
      "get": {
        "tags": [
          "Catalog"
        ],
        "operationId": "getParticipants",
        "summary": "List participants",
        "description": "Returns the participants your API key has access to: teams and players that compete in events. Responses are paginated — the participant catalog runs to tens of thousands of records, so page through with `page` and `page_size`. Filter by `sport_id` to scope to one sport; use `expand=sport` to embed the full sport object inline instead of issuing a follow-up call to `/sports`.",
        "parameters": [
          {
            "name": "sport_id",
            "in": "query",
            "description": "Restrict results to participants in this sport. Use an `id` from `GET /sports`.",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "expand",
            "in": "query",
            "description": "Fold related entities into each participant.\n\n- `sport`: embed the full `SportDTO` alongside the `sport_id`.",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "sport"
                ]
              }
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "1-based page number (default 1).",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 1,
              "minimum": 1
            }
          },
          {
            "name": "page_size",
            "in": "query",
            "required": false,
            "description": "Records per page (default 100, max 1000). Values above 1000 are rejected with an HTTP 400 in the framework error shape — see the [Errors](/docs/rest-api/errors) page.",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 100,
              "minimum": 1,
              "maximum": 1000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of participants the API key has access to.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponseParticipantDTO"
                },
                "examples": {
                  "default": {
                    "summary": "Default response (no expand)",
                    "value": {
                      "data": [
                        {
                          "id": 7,
                          "sport_id": 6,
                          "name": "Tampa Bay Lightning",
                          "type": "team"
                        },
                        {
                          "id": 11,
                          "sport_id": 6,
                          "name": "Vegas Golden Knights",
                          "type": "team"
                        },
                        {
                          "id": 19,
                          "sport_id": 6,
                          "name": "Colorado Avalanche",
                          "type": "team"
                        },
                        {
                          "id": 3344,
                          "sport_id": 6,
                          "name": "Mikhail Sidorov",
                          "type": "player"
                        }
                      ],
                      "pagination": {
                        "page": 1,
                        "size": 100,
                        "total": 42191
                      }
                    }
                  },
                  "expanded": {
                    "summary": "With expand=sport",
                    "value": {
                      "data": [
                        {
                          "id": 7,
                          "sport_id": 6,
                          "sport": {
                            "id": 6,
                            "name": "Ice Hockey",
                            "code": "ih"
                          },
                          "name": "Tampa Bay Lightning",
                          "type": "team"
                        }
                      ],
                      "pagination": {
                        "page": 1,
                        "size": 100,
                        "total": 42191
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v2/participants/{id}": {
      "get": {
        "tags": [
          "Catalog"
        ],
        "operationId": "getParticipant",
        "summary": "Get a participant",
        "description": "Fetch a single participant by its id. Returns the same participant object you get from the list endpoint. Use this when you already hold a participant id and want its current details.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Participant id from `GET /participants`.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The participant.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ParticipantDTO"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v2/events": {
      "get": {
        "tags": [
          "Events & Markets"
        ],
        "operationId": "getEvents",
        "summary": "List events",
        "description": "Returns events the API key has access to. Filter by sport, competition, or time window, or use `has_prices` to get back only events that have prices. Default behavior returns only `scheduled` events. Pass `status` explicitly to query live, finished, or cancelled events.\n\nResponses are paginated — every response includes a `pagination` object; use `page` and `page_size` to walk through the full result set.\n\nUse `expand` to embed related entities (sport, competition, participants) inline and avoid follow-up calls.",
        "parameters": [
          {
            "name": "sport_id",
            "in": "query",
            "description": "Restrict to events in this sport. Use an `id` from `GET /sports`.",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "competition_ids",
            "in": "query",
            "description": "Restrict to events in any of these competitions. Pass an array of competition IDs from `GET /competitions`.",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "integer",
                "format": "int64"
              }
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Filter by event status (matches any). Defaults to `scheduled` when omitted.",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "scheduled",
                  "live",
                  "finished",
                  "cancelled"
                ]
              }
            }
          },
          {
            "name": "start_from",
            "in": "query",
            "description": "Return events with `start_time` at or after this value (ISO 8601, e.g. `2026-10-04T00:00:00Z`).",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "start_to",
            "in": "query",
            "description": "Return events with `start_time` at or before this value (ISO 8601, e.g. `2026-10-05T00:00:00Z`).",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "has_prices",
            "in": "query",
            "description": "Set to `true` to get back only events that have prices available to you. Defaults to `false`, which returns all events, whether or not they have prices.",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "expand",
            "in": "query",
            "description": "Fold related entities into each event.\n\n- `sport`: embed the full `SportDTO` alongside the `sport_id`.\n- `competition`: embed the full `CompetitionDTO` alongside the `competition_id`.\n- `participants`: include the array of participants competing in the event, each with their `role` (home/away).",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "sport",
                  "competition",
                  "participants"
                ]
              }
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "1-based page number (default 1).",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 1,
              "minimum": 1
            }
          },
          {
            "name": "page_size",
            "in": "query",
            "required": false,
            "description": "Records per page (default 100, max 1000). Values above 1000 are rejected with an HTTP 400 in the framework error shape — see the [Errors](/docs/rest-api/errors) page.",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 100,
              "minimum": 1,
              "maximum": 1000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of events the API key has access to.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponseEventDTO"
                },
                "examples": {
                  "default": {
                    "summary": "Default response (no expand)",
                    "value": {
                      "data": [
                        {
                          "id": 6848,
                          "sport_id": 5,
                          "competition_id": 131,
                          "start_time": "2026-10-04T20:25:00Z",
                          "status": "scheduled",
                          "active": true
                        }
                      ],
                      "pagination": {
                        "page": 1,
                        "size": 100,
                        "total": 679
                      }
                    }
                  },
                  "expanded": {
                    "summary": "With expand=sport,competition,participants",
                    "value": {
                      "data": [
                        {
                          "id": 6848,
                          "sport_id": 5,
                          "sport": {
                            "id": 5,
                            "name": "American Football",
                            "code": "usf"
                          },
                          "competition_id": 131,
                          "competition": {
                            "id": 131,
                            "sport_id": 5,
                            "name": "NFL"
                          },
                          "start_time": "2026-10-04T20:25:00Z",
                          "status": "scheduled",
                          "active": true,
                          "participants": [
                            {
                              "id": 5311,
                              "name": "San Francisco 49ers",
                              "type": "team",
                              "role": "home"
                            },
                            {
                              "id": 5312,
                              "name": "Denver Broncos",
                              "type": "team",
                              "role": "away"
                            }
                          ]
                        }
                      ],
                      "pagination": {
                        "page": 1,
                        "size": 100,
                        "total": 679
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v2/events/{eventId}": {
      "get": {
        "tags": [
          "Events & Markets"
        ],
        "operationId": "getEvent",
        "summary": "Get event by ID",
        "description": "Returns a single event by its `id`. Use `expand` to embed sport, competition, and participants inline.\n\nThe response is a bare `EventDTO` object (no `data` wrapper), consistent with the other single-resource lookups.",
        "parameters": [
          {
            "name": "eventId",
            "in": "path",
            "description": "Event identifier from `GET /events`.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "expand",
            "in": "query",
            "description": "Fold related entities into the event. See `GET /events` for the per-value semantics.",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "sport",
                  "competition",
                  "participants"
                ]
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The event, as a bare `EventDTO` object.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventDTO"
                },
                "example": {
                  "id": 6848,
                  "sport_id": 5,
                  "competition_id": 131,
                  "start_time": "2026-10-04T20:25:00Z",
                  "status": "scheduled",
                  "active": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v2/events/{eventId}/markets": {
      "get": {
        "tags": [
          "Events & Markets"
        ],
        "operationId": "getMarkets",
        "summary": "List markets for an event",
        "description": "Returns the markets priced for a single event. Each market represents a betting line (moneyline, totals, spreads, etc.) and has one or more selections (e.g. Home/Away, Over/Under).\n\n**Always pass `expand=selections` if you need pricing data.** Prices live inside each selection; without expanding, the response contains markets but no prices.\n\nAdd `stake_limits` to `expand` to read each market's minimum and maximum stake before placing a bet.\n\nPrices come from the Vertex pricing provider (`pricing_provider_id: 1`).",
        "parameters": [
          {
            "name": "eventId",
            "in": "path",
            "description": "Event identifier from `GET /events`.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "expand",
            "in": "query",
            "description": "Fold related entities into each market.\n\n- `selections`: include the `selections` array with prices on each selection. **Effectively required** for any partner that wants pricing data.\n- `participants`: embed the full `ParticipantDTO` on each market and selection alongside `participant_id`.\n- `stake_limits`: include the `stake_limits` array with the minimum and maximum stake per market. See the schema notes on `MarketStakeLimitDTO` for how `max_stake` interacts with price.",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "selections",
                  "participants",
                  "stake_limits"
                ]
              }
            }
          },
          {
            "name": "pricing_provider_id",
            "in": "query",
            "description": "Pricing provider id. Always `1` (Vertex).",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "integer",
                "format": "int64"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Markets priced for the event.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponseMarketDTO"
                },
                "examples": {
                  "with_selections": {
                    "summary": "Moneyline market with selections, prices and stake limits (expand=selections,participants,stake_limits)",
                    "value": {
                      "data": [
                        {
                          "id": 64795,
                          "event_id": 6721,
                          "segment": {
                            "id": 2,
                            "code": "result",
                            "name": "result",
                            "scope": "event"
                          },
                          "status": "open",
                          "live": false,
                          "market_type": {
                            "id": 1,
                            "code": "moneyline",
                            "name": "Moneyline",
                            "category": "result"
                          },
                          "selections": [
                            {
                              "id": 37440,
                              "market_id": 64795,
                              "name": "Away",
                              "participant_id": 19,
                              "participant": {
                                "id": 19,
                                "sport_id": 6,
                                "name": "Colorado Avalanche",
                                "type": "team"
                              },
                              "prices": [
                                {
                                  "pricing_provider_id": 1,
                                  "price": 1.8707,
                                  "status": "active"
                                }
                              ]
                            },
                            {
                              "id": 37441,
                              "market_id": 64795,
                              "name": "Home",
                              "participant_id": 11,
                              "participant": {
                                "id": 11,
                                "sport_id": 6,
                                "name": "Vegas Golden Knights",
                                "type": "team"
                              },
                              "prices": [
                                {
                                  "pricing_provider_id": 1,
                                  "price": 1.9501,
                                  "status": "active"
                                }
                              ]
                            }
                          ],
                          "stake_limits": [
                            {
                              "pricing_provider_id": 1,
                              "participant_id": null,
                              "min_stake": 10,
                              "max_stake": 1000
                            }
                          ]
                        },
                        {
                          "id": 96327,
                          "event_id": 6721,
                          "segment": {
                            "id": 3,
                            "code": "total",
                            "name": "total",
                            "scope": "event"
                          },
                          "specifier": "5.0",
                          "status": "open",
                          "live": false,
                          "market_type": {
                            "id": 3,
                            "code": "totals",
                            "name": "Total goals",
                            "category": "total"
                          },
                          "selections": [
                            {
                              "id": 69444,
                              "market_id": 96327,
                              "name": "Over",
                              "prices": []
                            },
                            {
                              "id": 69447,
                              "market_id": 96327,
                              "name": "Under",
                              "prices": []
                            }
                          ],
                          "stake_limits": [
                            {
                              "pricing_provider_id": 1,
                              "participant_id": null,
                              "min_stake": 10,
                              "max_stake": 500
                            }
                          ]
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v2/events/{eventId}/markets/{marketId}": {
      "get": {
        "tags": [
          "Events & Markets"
        ],
        "operationId": "getMarket",
        "summary": "Get a market",
        "description": "Fetch a single market on an event by its id. Use `expand` to fold in selections, participants, or stake limits in the same call. This returns the same market shape as the markets list endpoint.",
        "parameters": [
          {
            "name": "eventId",
            "in": "path",
            "required": true,
            "description": "Event identifier from `GET /events`.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "marketId",
            "in": "path",
            "required": true,
            "description": "Market identifier from `GET /events/{eventId}/markets`.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "expand",
            "in": "query",
            "description": "Fold related entities into each market.\n\n- `selections`: include the `selections` array with prices on each selection. **Effectively required** for any partner that wants pricing data.\n- `participants`: embed the full `ParticipantDTO` on each market and selection alongside `participant_id`.\n- `stake_limits`: include the `stake_limits` array with the minimum and maximum stake per market. See the schema notes on `MarketStakeLimitDTO` for how `max_stake` interacts with price.",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "selections",
                  "participants",
                  "stake_limits"
                ]
              }
            }
          },
          {
            "name": "pricing_provider_id",
            "in": "query",
            "description": "Pricing provider id. Always `1` (Vertex).",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "integer",
                "format": "int64"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The market.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MarketDTO"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v2/selections/{selectionId}": {
      "get": {
        "tags": [
          "Events & Markets"
        ],
        "operationId": "getSelection",
        "summary": "Get a selection",
        "description": "Fetch a single selection by its id. The response includes the selection with its current Vertex price. Use this to refresh the price on one selection without re-fetching the whole market.",
        "parameters": [
          {
            "name": "selectionId",
            "in": "path",
            "required": true,
            "description": "Selection id from a market's `selections` array.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "expand",
            "in": "query",
            "description": "Fold related entities into the selection.\n\n- `participants`: embed the full `ParticipantDTO` alongside the `participant_id`.",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "participants"
                ]
              }
            }
          },
          {
            "name": "pricing_provider_id",
            "in": "query",
            "description": "Pricing provider id. Always `1` (Vertex).",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "integer",
                "format": "int64"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The selection.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SelectionDTO"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v2/bets": {
      "post": {
        "tags": [
          "Bets"
        ],
        "operationId": "placeBet",
        "summary": "Place a bet",
        "description": "Places a bet on a single selection at a given price. The request must reference a `selection_id` from `GET /events/{eventId}/markets?expand=selections`, a `pricing_provider_id` (always `1`, Vertex), and your bettor's `external_account_ref_tokens`. Your client identity is taken from the API key on the request.\n\nThe `price` you submit must match the price your client observed for that selection at the Vertex pricing provider; the server validates it. If pricing has moved, the request is rejected.\n\nSee the [Place a bet walkthrough](/docs/rest-api/walkthroughs/place-a-bet) for a full end-to-end example.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PlaceBetRequest"
              },
              "example": {
                "selection_id": 37440,
                "pricing_provider_id": 1,
                "external_account_ref_tokens": "user-12345",
                "stake": 10,
                "price": 1.87
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Bet accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "bet_id",
                    "message"
                  ],
                  "properties": {
                    "bet_id": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Identifier of the newly created bet. Use it to look up the bet in `GET /bets?bet_id={bet_id}`.",
                      "example": 299
                    },
                    "message": {
                      "type": "string",
                      "description": "Always `Bet placed` on success.",
                      "example": "Bet placed"
                    }
                  }
                },
                "example": {
                  "bet_id": 299,
                  "message": "Bet placed"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "get": {
        "tags": [
          "Bets"
        ],
        "operationId": "getBets",
        "summary": "List bets",
        "description": "Returns bets placed by your API key, filtered by time window, event, sport, and other dimensions.\n\nWhen no time filter is provided, the response defaults to all bets placed.\n\nResponses are paginated — every response includes a `pagination` object; use `page` and `page_size` to walk through the full result set.",
        "parameters": [
          {
            "name": "bet_id",
            "in": "query",
            "description": "Return a single bet by its `id` (from the response of `POST /bets`). When set, other filters are ignored.",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "placed_from",
            "in": "query",
            "description": "Inclusive lower bound on `placed_at` (ISO 8601). When omitted, no lower bound is applied.",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "placed_to",
            "in": "query",
            "description": "Inclusive upper bound on `placed_at` (ISO 8601). When omitted, defaults to the time of the request.",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "sport_ids",
            "in": "query",
            "description": "Comma-separated list of sport IDs to filter by. Non-numeric values return 400.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "competition_ids",
            "in": "query",
            "description": "Comma-separated list of competition IDs to filter by. Non-numeric values return 400.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "selection_names",
            "in": "query",
            "description": "Comma-separated list of selection names to filter by (e.g. `Home,Away,Over`).",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "market_type_codes",
            "in": "query",
            "description": "Comma-separated list of market type codes to include (e.g. `moneyline,totals,spreads`). Codes match `MarketTypeDTO.code`.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "team_names",
            "in": "query",
            "description": "Comma-separated list of team names to include, matching participant names exactly as returned by `GET /participants` (e.g. `Detroit Red Wings`).",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "event_ids",
            "in": "query",
            "description": "Comma-separated list of event IDs to filter by. Non-numeric values return 400.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "market_definition_ids",
            "in": "query",
            "description": "Comma-separated list of market-definition IDs to filter by. Non-numeric values return 400.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "market_side_filters",
            "in": "query",
            "description": "Per-market side filter: a URL-encoded JSON array of groups, each pairing market-definition IDs with the sides to keep for those markets — e.g. `[{\"market_definition_ids\":[1],\"sides\":[\"home\"]}]` keeps only Home bets on Moneyline. Valid sides: `home`, `away`, `over`, `under`, `yes`, `no`, `draw` (case-insensitive). Sides apply only within their group's `market_definition_ids`. When present, this parameter supersedes the flat `market_definition_ids` parameter. Malformed JSON or an unknown side returns 400.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "participant_ids",
            "in": "query",
            "description": "Comma-separated list of participant IDs to filter by. Non-numeric values return 400.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "statuses",
            "in": "query",
            "description": "Comma-separated list of bet statuses to filter by (e.g. `open,won`). Accepts the same values as `BetListDto.status` (common values: `open`, `won`, `lost`, `void`).",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "1-based page number.",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 1,
              "minimum": 1
            }
          },
          {
            "name": "page_size",
            "in": "query",
            "description": "Records per page (default 100, max 1000). Values above 1000 are rejected with an HTTP 400 in the framework error shape — see the [Errors](/docs/rest-api/errors) page.",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 100,
              "minimum": 1,
              "maximum": 1000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of bets matching the filter.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponseBetListDto"
                },
                "example": {
                  "data": [
                    {
                      "id": 299,
                      "stake": 10,
                      "price_at_placement": 1.87,
                      "potential_payout": 18.7,
                      "status": "open",
                      "placed_at": "2026-05-26T14:00:00Z",
                      "event": {
                        "id": 6721,
                        "start_time": "2026-05-26T19:00:00Z",
                        "sport_id": 6,
                        "competition_id": 11,
                        "participants": [
                          {
                            "id": 19,
                            "name": "Colorado Avalanche",
                            "role": "away"
                          },
                          {
                            "id": 11,
                            "name": "Vegas Golden Knights",
                            "role": "home"
                          }
                        ]
                      },
                      "sport": {
                        "id": 6,
                        "name": "Ice Hockey",
                        "code": "ih"
                      },
                      "competition": {
                        "id": 11,
                        "name": "NHL",
                        "sport_id": 6
                      },
                      "pricing_provider": {
                        "id": 1,
                        "name": "Vertex",
                        "code": "vx"
                      },
                      "market": {
                        "id": 64795,
                        "market_definition_id": 1,
                        "participant": {
                          "id": 19,
                          "name": "Colorado Avalanche"
                        }
                      },
                      "selection": {
                        "id": 37440,
                        "name": "Away"
                      },
                      "flashscore_reference": "abcXYZ123"
                    }
                  ],
                  "pagination": {
                    "page": 1,
                    "size": 100,
                    "total": 1
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v2/bets/{id}": {
      "get": {
        "tags": [
          "Bets"
        ],
        "operationId": "getBet",
        "summary": "Get a bet",
        "description": "Fetch a single bet by its id. Returns the full bet record, including its current status and the selection it was placed on. Use the id returned to you by `POST /bets`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Bet id returned by `POST /bets`.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The bet.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BetListDto"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v2/bets/by-reference/{reference}": {
      "get": {
        "tags": [
          "Bets"
        ],
        "operationId": "getBetByReference",
        "summary": "Get a bet by reference",
        "description": "Look up a bet by the `bet_reference` you supplied when placing it. This lets you retrieve a bet using your own identifier instead of our bet id. If no bet matches the reference, a 404 is returned with the standard error envelope and the message `Bet not found`. Responses never echo your `bet_reference` back — store the reference-to-bet mapping on your side when you place the bet.",
        "parameters": [
          {
            "name": "reference",
            "in": "path",
            "required": true,
            "description": "The `bet_reference` you supplied when placing the bet.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The bet.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BetListDto"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "PlaceBetRequest": {
        "description": "Body of `POST /bets`. All fields are required except `bet_reference`.",
        "type": "object",
        "required": [
          "selection_id",
          "pricing_provider_id",
          "external_account_ref_tokens",
          "stake",
          "price"
        ],
        "properties": {
          "selection_id": {
            "type": "integer",
            "format": "int64",
            "description": "Identifier of the selection being bet on. Use a selection `id` from `GET /events/{eventId}/markets?expand=selections`."
          },
          "pricing_provider_id": {
            "type": "integer",
            "format": "int64",
            "description": "Pricing provider to bet against. Always `1` (Vertex)."
          },
          "external_account_ref_tokens": {
            "type": "string",
            "maxLength": 100,
            "description": "Your end-user (bettor) identifier. You choose this value, typically your internal user ID or account handle. Up to 100 characters. Pass the same value for all bets booked against the same bettor; the first time we see a new value we persist it as a new bettor record. Your client identity is taken from the API key on the request, so there is no separate client ID field."
          },
          "stake": {
            "type": "number",
            "format": "double",
            "exclusiveMinimum": 0,
            "description": "Wager amount. Must fall within the market's stake limits (`min_stake` ≤ stake ≤ the price-adjusted maximum). Read them with `expand=stake_limits` on `GET /events/{eventId}/markets`. Bets outside the limits are rejected with `400`."
          },
          "price": {
            "type": "number",
            "format": "double",
            "minimum": 1.0,
            "description": "Decimal odds at placement time. Must be at least 1.0. The server validates the price against the current market. If pricing has moved, the request is rejected."
          },
          "bet_reference": {
            "type": "string",
            "description": "Optional partner-supplied reference for this bet, unique per client. Use it as an idempotency key for safe retries: re-submitting the same reference with the same stake and price returns the existing bet (HTTP 201, message \"Bet already placed\") instead of placing a duplicate. Re-submitting the same reference with a different stake or price is rejected with HTTP 409. Look the bet up later via `GET /bets/by-reference/{reference}`. The reference is not returned in any response — store your reference-to-bet mapping when you place the bet."
          }
        }
      },
      "MarketTypeDTO": {
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "code": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "category": {
            "type": "string"
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "description": "Standard error envelope returned by authentication and by endpoints that validate explicitly. Some framework-level errors (malformed JSON, unknown route, wrong method, unsupported content type) return a different shape. See the [Errors](/docs/rest-api/errors) page.",
        "required": [
          "status",
          "message"
        ],
        "properties": {
          "status": {
            "type": "integer",
            "description": "HTTP status code, mirrored from the response headers."
          },
          "message": {
            "type": "string",
            "description": "Human-readable explanation. Multi-field validation errors are joined with `; `."
          },
          "data": {
            "type": [
              "string",
              "null"
            ],
            "description": "Reserved for additional context. Currently always `null`."
          }
        }
      },
      "Pagination": {
        "type": "object",
        "description": "Pagination metadata. Returned by the paginated list endpoints: `GET /events`, `GET /competitions`, `GET /participants`, and `GET /bets`. `page` is 1-based; the default page size is 100.",
        "required": [
          "page",
          "size",
          "total"
        ],
        "properties": {
          "page": {
            "type": "integer",
            "format": "int32",
            "description": "1-based page index of this response."
          },
          "size": {
            "type": "integer",
            "format": "int32",
            "description": "Number of records per page."
          },
          "total": {
            "type": "integer",
            "format": "int64",
            "description": "Total record count across all pages matching the query."
          }
        }
      },
      "SportDTO": {
        "description": "A sport that Vertex prices. Sport `id` is the primary key referenced by competitions, events, and market type filters elsewhere in the API.",
        "type": "object",
        "required": [
          "id",
          "name",
          "code"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "Unique sport identifier. Stable across releases; safe to cache."
          },
          "name": {
            "type": "string",
            "description": "Human-readable sport name (e.g. `Soccer`, `Basketball`)."
          },
          "code": {
            "type": "string",
            "description": "Short sport code used internally (e.g. `soc`, `bask`, `ten`, `mma`, `box`)."
          }
        }
      },
      "SuccessResponseSportDTO": {
        "type": "object",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "array",
            "description": "Sports the API key has access to.",
            "items": {
              "$ref": "#/components/schemas/SportDTO"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination",
            "description": "Reserved. Not currently returned for `/sports`. The full list fits in one response. Will be populated when pagination is introduced."
          }
        }
      },
      "CompetitionDTO": {
        "description": "A competition that Vertex prices: a league (Premier League), tournament (Champions League), or championship (World Championship). Competition `id` is the primary key referenced by events elsewhere in the API.",
        "type": "object",
        "required": [
          "id",
          "sport_id",
          "name"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "Unique competition identifier. Stable across releases; safe to cache."
          },
          "sport_id": {
            "type": "integer",
            "format": "int64",
            "description": "Foreign key to the parent sport (`id` from `GET /sports`). Use `expand=sport` to embed the full sport object alongside this id."
          },
          "sport": {
            "$ref": "#/components/schemas/SportDTO",
            "description": "Full sport object. Only present when `expand=sport` is set."
          },
          "name": {
            "type": "string",
            "description": "Full canonical competition name (e.g. `Premier League`, `NHL`, `Champions League`)."
          },
          "country": {
            "type": "string",
            "description": "Country or region the competition belongs to (e.g. `USA`, `Switzerland`, `International`)."
          }
        }
      },
      "MarketDTO": {
        "description": "A betting market on an event. Markets group selections (e.g. Home/Away for a moneyline market, Over/Under for a totals market). Market `id` is the primary key referenced by selections and bets.",
        "type": "object",
        "required": [
          "id",
          "event_id",
          "market_type",
          "segment",
          "status",
          "live"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "Unique market identifier. Stable across releases."
          },
          "event_id": {
            "type": "integer",
            "format": "int64",
            "description": "Foreign key to the parent event (`id` from `GET /events`)."
          },
          "segment": {
            "$ref": "#/components/schemas/MarketSegmentDTO",
            "description": "What the market prices (result, total, spread, etc.) and its scope."
          },
          "market_type": {
            "$ref": "#/components/schemas/MarketTypeDTO",
            "description": "The market type (e.g. moneyline, totals). Always present."
          },
          "specifier": {
            "type": "string",
            "description": "Numeric or string qualifier that distinguishes alternate lines within the same segment (e.g. `5.0` for an over/under line at 5 goals, `-1.5` for a spread). Absent when the market has no alternate-line concept (most moneyline markets)."
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "suspended",
              "closed"
            ],
            "description": "Market trading state. `open` = accepting bets. `suspended` = temporarily not accepting (e.g. during a goal). `closed` = no longer accepting bets."
          },
          "live": {
            "type": "boolean",
            "description": "`true` when the market is priced in-play (event is live). `false` for pre-match markets."
          },
          "participant_id": {
            "type": "integer",
            "format": "int64",
            "description": "Participant the market is scoped to, when the segment scope is `participant` (e.g. player props). Absent on event-scoped markets."
          },
          "participant": {
            "$ref": "#/components/schemas/ParticipantDTO",
            "description": "Full participant object. Only present when `expand=participants` is set and the market has a `participant_id`."
          },
          "selections": {
            "type": "array",
            "description": "Selections (betting options) within this market, with prices. Only present when `expand=selections` is set.",
            "items": {
              "$ref": "#/components/schemas/SelectionDTO"
            }
          },
          "stake_limits": {
            "type": "array",
            "description": "Stake limits for this market. Only present when `expand=stake_limits` is set.",
            "items": {
              "$ref": "#/components/schemas/MarketStakeLimitDTO"
            }
          }
        }
      },
      "SelectionDTO": {
        "description": "A single bettable option within a market (e.g. `Home`, `Away`, `Over`, `Under`). Selections carry the prices partners use to place bets. Selection `id` is the primary key referenced by `POST /bets`.",
        "type": "object",
        "required": [
          "id",
          "market_id",
          "name",
          "prices"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "Unique selection identifier. Pass this as `selection_id` when placing a bet via `POST /bets`."
          },
          "market_id": {
            "type": "integer",
            "format": "int64",
            "description": "Foreign key to the parent market."
          },
          "name": {
            "type": "string",
            "description": "Display name for the selection (e.g. `Home`, `Away`, `Over`, `Under`, or a team/player name on participant-scoped markets)."
          },
          "line_value": {
            "type": "number",
            "format": "double",
            "description": "Present only on Asian Handicap (`spreads`) selections, where each side has its own line (e.g. Home `-0.5`, Away `+0.5`). Over/Under and team-total selections omit it because both sides share one line, which is given by the market's `specifier`. Moneyline selections have no line at all."
          },
          "participant_id": {
            "type": "integer",
            "format": "int64",
            "description": "Participant this selection refers to, when applicable (e.g. the home team on a moneyline market). Absent on totals/spread selections that don't map to one participant."
          },
          "participant": {
            "$ref": "#/components/schemas/ParticipantDTO",
            "description": "Full participant object. Only present when `expand=participants` is set and the selection has a `participant_id`."
          },
          "result": {
            "type": "string",
            "description": "Settlement result of the selection once the event finishes (e.g. `win`, `loss`, `push`). Empty or pending while the event is unsettled."
          },
          "prices": {
            "type": "array",
            "description": "The Vertex price for this selection (`pricing_provider_id: 1`). On `GET /selections/{selectionId}` this array is empty when nothing is currently quoted. On the markets endpoints it is never empty: a selection with no tradable price is omitted from the response altogether, so a selection you receive there always carries a price.",
            "items": {
              "$ref": "#/components/schemas/SelectionPriceDTO"
            }
          }
        }
      },
      "SelectionPriceDTO": {
        "description": "A price offered by one pricing provider on one selection. Multiple prices appear per selection, one for each pricing provider quoting the market.",
        "type": "object",
        "required": [
          "pricing_provider_id",
          "price",
          "status"
        ],
        "properties": {
          "pricing_provider_id": {
            "type": "integer",
            "format": "int64",
            "description": "Identifies the pricing provider that quoted this price. Always `1` (Vertex). Pass the same id to `POST /bets.pricing_provider_id` to place the bet against this price."
          },
          "price": {
            "type": "number",
            "format": "double",
            "description": "Decimal odds. A `price` of `1.91` means a winning bet returns `stake × 1.91` (stake included)."
          },
          "status": {
            "type": "string",
            "enum": [
              "active"
            ],
            "description": "Whether this price is tradable. Always `active` here: prices that are not tradable are filtered out before they reach a partner API key, so every price this endpoint returns is one you can bet on. The WebSocket `market_updates` stream is filtered the same way. The exception is `suspended_market_updates`, which exists to tell you a price has been withdrawn and therefore does carry `suspended` — see the WebSocket messages reference."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "When this price was last written, as an ISO-8601 UTC timestamp. It is stamped with the same instant carried by the matching WebSocket price update, so a REST read and a streamed update for the same price agree. Use it to tell a fresh quote from a stale one."
          }
        }
      },
      "MarketStakeLimitDTO": {
        "description": "The stake limits in force on one market. Limits change over time: the `stake_limit_update` WebSocket message signals a change, and this endpoint is the authoritative read. See the *Stake Limits* concepts page for worked examples.",
        "type": "object",
        "required": [
          "pricing_provider_id",
          "min_stake",
          "max_stake"
        ],
        "properties": {
          "pricing_provider_id": {
            "type": "integer",
            "format": "int64",
            "description": "Pricing provider the limit applies to. Always `1` (Vertex)."
          },
          "participant_id": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Participant the limit is scoped to on participant-scoped markets (e.g. player props). `null` on event-scoped markets."
          },
          "min_stake": {
            "type": "integer",
            "format": "int32",
            "description": "Minimum stake accepted on this market. Bets below this are rejected with `400`."
          },
          "max_stake": {
            "type": "integer",
            "format": "int32",
            "description": "Caps your stake at prices ≥ `2.00`; below `2.00` it caps your potential profit instead, so the maximum accepted stake rises to `max_stake / (price − 1)`. Bets above the effective maximum are rejected with `400`."
          }
        }
      },
      "SuccessResponseMarketDTO": {
        "type": "object",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "array",
            "description": "Markets priced for the event.",
            "items": {
              "$ref": "#/components/schemas/MarketDTO"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination",
            "description": "Reserved. Not currently returned for `/markets`. The full market book for an event is returned in one response."
          }
        }
      },
      "MarketSegmentDTO": {
        "description": "Groups markets by what they price (`result`, `total`, `spread`, etc.) and by scope (event-level vs participant-level). Returned inline on each `MarketDTO`.",
        "type": "object",
        "required": [
          "id",
          "code",
          "name",
          "scope"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "Unique segment identifier."
          },
          "code": {
            "type": "string",
            "description": "Short segment code (e.g. `result`, `total`, `spread`)."
          },
          "name": {
            "type": "string",
            "description": "Human-readable segment name."
          },
          "scope": {
            "type": "string",
            "description": "Whether this segment prices the event as a whole (`event`) or individual participants/sub-events.",
            "enum": [
              "event",
              "participant"
            ]
          }
        }
      },
      "ParticipantDTO": {
        "description": "A participant in a sporting event: a team (e.g. `Tampa Bay Lightning`) or an individual (e.g. a tennis player or fighter). Participant `id` is the primary key referenced by events and selections elsewhere in the API.",
        "type": "object",
        "required": [
          "id",
          "sport_id",
          "name",
          "type"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "Unique participant identifier. Stable across releases; safe to cache."
          },
          "sport_id": {
            "type": "integer",
            "format": "int64",
            "description": "Foreign key to the parent sport (`id` from `GET /sports`). Use `expand=sport` to embed the full sport object alongside this id."
          },
          "sport": {
            "$ref": "#/components/schemas/SportDTO",
            "description": "Full sport object. Only present when `expand=sport` is set."
          },
          "name": {
            "type": "string",
            "description": "Full canonical name (e.g. `Tampa Bay Lightning`, `Mikhail Sidorov`)."
          },
          "type": {
            "type": "string",
            "enum": [
              "team",
              "player"
            ],
            "description": "Participant kind: `team` for franchise/club entries, `player` for individual competitors (tennis players, fighters, individual athletes)."
          }
        }
      },
      "SuccessResponseParticipantDTO": {
        "type": "object",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "array",
            "description": "Participants the API key has access to.",
            "items": {
              "$ref": "#/components/schemas/ParticipantDTO"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination",
            "description": "Pagination metadata for the result set. Always returned; the default page size is 100. Page through with `page` and `page_size`."
          }
        }
      },
      "EventParticipantDTO": {
        "description": "Lean participant shape embedded inside `EventDTO.participants` when `expand=participants`. Includes the participant's `role` (home, away, etc.) within this event. For the full participant catalog object, see `ParticipantDTO`.",
        "type": "object",
        "required": [
          "id",
          "name",
          "type",
          "role"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "Participant identifier. Matches `id` in `GET /participants`."
          },
          "name": {
            "type": "string",
            "description": "Participant name (e.g. `San Francisco 49ers`, `Mikhail Sidorov`)."
          },
          "type": {
            "type": "string",
            "enum": [
              "team",
              "player"
            ],
            "description": "Participant kind. See `ParticipantDTO.type` for full semantics."
          },
          "role": {
            "type": "string",
            "description": "The participant's role in this event. For team sports: `home` or `away`. For individual sports (tennis, fighting, etc.), the value may identify position differently. Handle unknown values defensively."
          }
        }
      },
      "EventDTO": {
        "description": "A scheduled sporting fixture: a single match, game, fight, or tournament instance. Event `id` is the primary key referenced by markets, selections, and bets elsewhere in the API.",
        "type": "object",
        "required": [
          "id",
          "sport_id",
          "competition_id",
          "start_time",
          "status"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "Unique event identifier. Stable across releases; safe to cache."
          },
          "sport_id": {
            "type": "integer",
            "format": "int64",
            "description": "Foreign key to the parent sport (`id` from `GET /sports`). Use `expand=sport` to embed the full sport object."
          },
          "sport": {
            "$ref": "#/components/schemas/SportDTO",
            "description": "Full sport object. Only present when `expand=sport` is set."
          },
          "competition_id": {
            "type": "integer",
            "format": "int64",
            "description": "Foreign key to the parent competition (`id` from `GET /competitions`). Use `expand=competition` to embed the full competition object."
          },
          "competition": {
            "$ref": "#/components/schemas/CompetitionDTO",
            "description": "Full competition object. Only present when `expand=competition` is set."
          },
          "start_time": {
            "type": "string",
            "format": "date-time",
            "description": "Scheduled kickoff/start time (ISO 8601, UTC). The fixture may go live shortly before or after this value."
          },
          "status": {
            "type": "string",
            "enum": [
              "scheduled",
              "live",
              "finished",
              "cancelled"
            ],
            "description": "Lifecycle status. `scheduled` = upcoming. `live` = in progress. `finished` = settled. `cancelled` = abandoned or postponed."
          },
          "active": {
            "type": "boolean",
            "description": "Whether the event is open for trading. Always `true` on the REST endpoints: inactive events are not returned. On the WebSocket `event` channel an update with `active: false` tells you to stop trading the event."
          },
          "participants": {
            "type": "array",
            "description": "Participants competing in the event with their per-event role (home/away). Only present when `expand=participants` is set.",
            "items": {
              "$ref": "#/components/schemas/EventParticipantDTO"
            }
          }
        }
      },
      "SuccessResponseEventDTO": {
        "type": "object",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "array",
            "description": "Events the API key has access to.",
            "items": {
              "$ref": "#/components/schemas/EventDTO"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination",
            "description": "Pagination metadata for the result set. Always returned; the default page size is 100. Page through with `page` and `page_size`."
          }
        }
      },
      "SuccessResponseCompetitionDTO": {
        "type": "object",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "array",
            "description": "Competitions the API key has access to.",
            "items": {
              "$ref": "#/components/schemas/CompetitionDTO"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination",
            "description": "Pagination metadata for the result set. Always returned; the default page size is 100. Page through with `page` and `page_size`."
          }
        }
      },
      "BetListDto": {
        "description": "A bet record as returned by `GET /bets`. Combines the bet itself with denormalised event, market, selection, and pricing-provider context for at-a-glance reading.",
        "type": "object",
        "required": [
          "id",
          "stake",
          "price_at_placement",
          "potential_payout",
          "status",
          "placed_at",
          "event",
          "sport",
          "competition",
          "pricing_provider",
          "market",
          "selection"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "Bet identifier. Returned by `POST /bets` as `bet_id`."
          },
          "stake": {
            "type": "number",
            "format": "double",
            "description": "Wager amount, in the bettor's currency."
          },
          "price_at_placement": {
            "type": "number",
            "format": "double",
            "description": "Decimal odds locked in when the bet was placed."
          },
          "potential_payout": {
            "type": "number",
            "format": "double",
            "description": "Stake × price. Returned to the bettor on a winning settlement."
          },
          "profit": {
            "type": "number",
            "format": "double",
            "description": "Realised profit or loss on a settled bet, from the bettor's perspective: total amount won minus stake — `potential_payout − stake` on a win, `−stake` on a loss, and `0` on a void (stake returned). Not meaningful while the bet is still `open`."
          },
          "status": {
            "type": "string",
            "description": "Bet lifecycle status. Common values: `open` (placed, not yet settled), `won`, `lost`, `void`."
          },
          "placed_at": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp the bet was accepted by the server (ISO 8601, UTC)."
          },
          "event": {
            "$ref": "#/components/schemas/EventInfo",
            "description": "Lean event context (id, start_time, sport_id, competition_id, participants with roles)."
          },
          "sport": {
            "$ref": "#/components/schemas/SportInfo",
            "description": "Lean sport context (id, name, code)."
          },
          "competition": {
            "$ref": "#/components/schemas/CompetitionInfo",
            "description": "Lean competition context (id, name, sport_id)."
          },
          "pricing_provider": {
            "$ref": "#/components/schemas/PricingProviderInfo",
            "description": "The pricing provider the bet was placed against (Vertex)."
          },
          "market": {
            "$ref": "#/components/schemas/MarketInfo",
            "description": "Lean market context (id, market_definition_id, specifier, participant for participant-scoped markets)."
          },
          "selection": {
            "$ref": "#/components/schemas/SelectionInfo",
            "description": "The specific selection bet on (id, name, line_value where applicable)."
          },
          "flashscore_reference": {
            "type": "string",
            "description": "External Flashscore event reference, for cross-referencing with public sports-data feeds keyed on Flashscore IDs."
          }
        }
      },
      "CompetitionInfo": {
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "name": {
            "type": "string"
          },
          "sport_id": {
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "EventInfo": {
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "start_time": {
            "type": "string",
            "format": "date-time"
          },
          "sport_id": {
            "type": "integer",
            "format": "int64"
          },
          "competition_id": {
            "type": "integer",
            "format": "int64"
          },
          "participants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ParticipantInfo"
            }
          }
        }
      },
      "MarketInfo": {
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "market_definition_id": {
            "type": "integer",
            "format": "int64"
          },
          "specifier": {
            "type": "string"
          },
          "participant": {
            "$ref": "#/components/schemas/MarketParticipantInfo"
          }
        }
      },
      "MarketParticipantInfo": {
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "name": {
            "type": "string"
          }
        }
      },
      "ParticipantInfo": {
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "name": {
            "type": "string"
          },
          "role": {
            "type": "string"
          }
        }
      },
      "PricingProviderInfo": {
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "name": {
            "type": "string"
          },
          "code": {
            "type": "string"
          }
        }
      },
      "SelectionInfo": {
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "name": {
            "type": "string"
          },
          "line_value": {
            "type": "number",
            "format": "double",
            "description": "Selection line. Present only on Asian Handicap selections, where each side has its own value. See `SelectionDTO.line_value`."
          }
        }
      },
      "SportInfo": {
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "name": {
            "type": "string"
          },
          "code": {
            "type": "string"
          }
        }
      },
      "SuccessResponseBetListDto": {
        "type": "object",
        "required": [
          "data",
          "pagination"
        ],
        "properties": {
          "data": {
            "type": "array",
            "description": "Bets matching the query.",
            "items": {
              "$ref": "#/components/schemas/BetListDto"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination",
            "description": "Pagination metadata for the result set. Always returned; the default page size is 100. Page through with `page` and `page_size`."
          }
        }
      }
    },
    "securitySchemes": {
      "API Key": {
        "type": "http",
        "scheme": "bearer",
        "description": "External API key issued during onboarding, passed as a Bearer token in the `Authorization` header. Contact your integration manager to rotate or revoke."
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Request was rejected by validation. Multi-field errors are joined into a single `message` separated by `;`. Framework-level errors (malformed JSON, non-numeric ID in a CSV filter, `size` outside `1..1000`) return the alternate framework shape. See the [Errors](/docs/rest-api/errors) page.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "status": 400,
              "message": "stake must be greater than 0; price must be at least 1.0",
              "data": null
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Bearer token missing, malformed, or rejected. See the [Authentication](/docs/rest-api/authentication) page for the full list of triggering conditions.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "status": 401,
              "message": "Missing or invalid Authorization header",
              "data": null
            }
          }
        }
      },
      "Forbidden": {
        "description": "Token authenticated but lacks permission for the requested resource.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "status": 403,
              "message": "Not permitted to access the requested resource",
              "data": null
            }
          }
        }
      },
      "NotFound": {
        "description": "Requested resource does not exist (e.g. unknown event ID). Some 404 responses on path-not-found return an empty body. Branch on the status code, not the body. When a body is returned, its `path` field mirrors the path of the request that failed — the `/api/v2/events/...` path in the example below is illustrative.",
        "content": {
          "application/json": {
            "example": {
              "timestamp": "2026-05-26T14:36:30.235+00:00",
              "status": 404,
              "error": "Not Found",
              "path": "/api/v2/events/999999"
            }
          }
        }
      },
      "ServerError": {
        "description": "Unexpected server-side failure. Retry with exponential backoff; contact your integration manager if the failure persists.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "Conflict": {
        "description": "The `bet_reference` already exists for this client with a different stake or price. The original bet is unchanged; no duplicate is placed.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "status": 409,
              "message": "A bet with reference 'order-12345' already exists with different stake",
              "data": null
            }
          }
        }
      }
    }
  }
}
