Data Hierarchy
Every price in the Vertex feed is anchored to a five-level hierarchy: Sport → Competition → Event → Market → Selection. Understanding this structure is essential for routing messages, building catalogs, and storing data on your side.
The five levels
Sport
The top-level category of competition, for example soccer, tennis, basketball, ice hockey, or MMA. Each sport has an id, a name, and a short code (e.g. soc, ten, mma). The full set is returned by /sports. Sport is the coarsest filter you can apply when discovering competitions and events over REST.
Competition
A named competition within a sport, for example the English Premier League or ATP Masters 1000 Madrid. A competition carries id, sport_id, name, and country. Competitions are discovered via /competitions (filterable by sport).
Event
A single scheduled match or contest, for example Arsenal vs Chelsea on a given date, or a specific MMA bout. An event carries id, sport_id, competition_id, start_time, status (scheduled, live, finished, cancelled), an active flag (whether it is open for trading), and its participants (each with id, name, a type of team or player, and role). Events are listed via /events and fetched individually via /events/{eventId}.
Market
A bettable proposition on an event, for example match result, total goals over/under 2.5, or first-set winner. A market carries id, event_id, a segment (its classification, with code, name, and scope), an optional specifier (the line key for handicap/total variants, e.g. 2.5), a status (open, suspended, closed), and a live flag. A market's selections are returned with it via /events/{eventId}/markets.
Selection
A single outcome within a market, for example "Arsenal to win", "Over 2.5 goals", or a player-prop outcome. A selection carries id, market_id, name, an optional participant_id, a line_value (the numeric line for handicap/total selections; null for moneyline), an optional result, and its Vertex price. The price is carried over REST in a prices array that holds the single Vertex entry (pricing_provider_id, price, status). Selections are the leaf nodes of the hierarchy and the unit at which prices are carried. Price updates stream over the WebSocket market channel as market_updates, where each selection's current price is nested inside its parent market.
Participants
Teams and individual players are participants — a cross-cutting catalogue rather than a sixth level in the hierarchy. A participant carries id, sport_id, name, a type of team (e.g. Carolina Hurricanes) or player (e.g. an individual NHL player, tennis player, or fighter). The catalogue runs to tens of thousands of records, and /participants is paginated (filterable by sport; 100 records per page by default) — a single call returns one page, not the full set. Individual participants are fetched via /participants/{id}.
The same participant id is referenced elsewhere by foreign key: as the role-tagged competitors in an event's participants array, and as the participant_id on participant-scoped markets and selections — for example, a player-prop market carries the participant_id of the player the prop is about. Resolve that participant_id against GET /participants/{id} to recover the team or player name — or, if you are building a local lookup table, page through /participants with page/page_size.
Visual hierarchy
Identifiers
All identifiers in the feed are stable integers. There are no composite, prefixed, or slug-based IDs. Each entity has a plain integer id, and each level references its parent by a foreign-key field:
| Level | ID field | References parent via |
|---|---|---|
| Sport | id | n/a (top level) |
| Competition | id | sport_id |
| Event | id | competition_id |
| Market | id | event_id |
| Selection | id | market_id |
For example, lineage is recoverable purely from these integer foreign keys: a selection delivered inside a market_updates message carries its market_id, and the enclosing market carries its event_id, so a single selection resolves up to its event without any composite identifier:
{
"id": 35928,
"market_id": 8842,
"price": { "pricing_provider_id": 1, "price": 2.6747, "status": "active" }
}
Line values are carried as discrete fields (line_value on the selection and specifier on the market) and are never encoded into an identifier. Alternate lines on the same market are simply distinct selections, distinguished by their line_value.
Implications for clients
- Subscriptions are made per channel (
eventormarket), optionally filtered byevent_ids. There is no "subscribe to a whole sport or competition" option; to follow a competition, list the relevantevent_ids(discovered via REST). Omittingevent_idsis a wildcard for everything your key is entitled to. See Subscriptions. - Storage: persist by
selection_id, but index byevent_idfor fast lookup when an event lifecycle update arrives. - Reconciliation: if you receive a selection or market update and don't recognise its parent event, fetch that event's markets via REST before applying the update.