---
title: "Market + recent price ticks per side, newest first (PRO)"
method: GET
path: "/markets/{matchId}/prices"
---

# Market + recent price ticks per side, newest first (PRO)

`GET /markets/{matchId}/prices`

## Request

- Base URL: `https://api.livetennisapi.com/api/public/v1`
- URL: `https://api.livetennisapi.com/api/public/v1/markets/{matchId}/prices`
- Auth: one of:
  - HTTP bearer
  - API key in header `X-API-Key`

The mapped match-winner market for one match, with its recent price ticks per side, newest first.

404 with `error: no_market` (since 2026-09-02; `match_id` and `coverage: "none"` alongside) when no match-winner market is mapped to the match — a market is mapped only where one is published for that fixture, so a client walking every live match will meet this. A bare `not_found` means the match id itself does not exist. Every row of `GET /matches` carries `has_market`, so a slate can be filtered before any call here. `GET /matches/{matchId}` answers `200` for the same match with `market` and `market_price` set to `null` instead, and carries the live score in the same response, so a per-match poll built on the detail route never spends a call on a 404.

Ticks are kept for 30 days and then deleted: a market older than that answers with an empty `prices` array while staying mapped — retention, not a fault. For paging past 500 ticks use `/matches/{matchId}/prices` with its cursor.

## Path parameters

- `matchId` integer, required

## Query parameters

- `limit` integer

## Response `200`

Market with `prices`

- Market
  - `id` integer
  - `question` string, nullable
  - `status` 'active' | 'resolved' | 'closed' | 'null', nullable
  - `volume` number, nullable
  - `liquidity` number, nullable
  - `end_date` string, date-time, nullable
  - `prices` Price[]: Prices endpoint / match detail only; newest first
    - `side` integer, nullable: 1 = players.p1's outcome, 2 = players.p2's - decided by our name-verified match-market mapping, never by the venue's own display order (the venue lists roughly half of all pairings the other way round). Fixed 2026-09-09: ticks served before that date followed the venue's display order, so on those markets side was reversed; the mapping is stored, so re-reading any tick now returns the correct side.
    - `bid` number, nullable
    - `ask` number, nullable
    - `mid` number, nullable: The venue's observed midpoint at the instant we polled or streamed it - never a model estimate. Only mid is always real; see synthetic.
    - `spread` number, nullable
    - `price_source` string, nullable: Feed category, e.g. prediction_market
    - `synthetic` boolean, nullable: true = bid/ask are estimated from mid (mid +/- 0.005, not a live order book) - mid itself is the venue's observed midpoint, never a model estimate; false = real top-of-book from the live order-book stream; null = unknown (older ticks). The live stream attaches when a match goes live, so pre-match ticks are synthetic: true by design.
    - `timestamp` string, date-time, nullable: When we observed this quote (UTC) - our observation clock, not a venue publication time.

## Other responses

- `401`: Missing, unknown, or disabled credentials
- `403`: Your tier doesn't unlock this endpoint
- `404`: `error: not_found` — no such match id. `error: no_market` (with `match_id`, `coverage: "none"`, `detail`) — the match exists and no match-winner market is mapped to it; `has_market` on the match list says so without a probe.
- `410`: The match id existed and was merged into another match record (since 2026-09-05). The body is `MatchMerged`, not `Error`: it carries a forwarding address, which is the whole point of answering 410 rather than 404.
- `429`: Rate limit exceeded (Retry-After header present). Three body shapes, told apart by `error` and `scope`: the per-MINUTE limit (`rate_limited`, with `upgrade_url`, `tier` and `price` naming the next tier up); the per-DAY quota (`rate_limited` with `scope: "day"`, `limit_per_day`, and `resets_at` — the absolute ISO instant the daily window resets); and `abuse_throttled` with `retry_at_epoch` — a 24-hour block applied to clients that keep hammering far past their cap, which a well-behaved retry loop never sees. Fix the loop rather than retrying through it.

---

[API](https://skmtc.dev/livetennisapi/apis/live-tennis-api.md) · [All operations](https://skmtc.dev/livetennisapi/apis/live-tennis-api/llms.txt) · [OpenAPI document](https://skmtc.dev/livetennisapi/apis/live-tennis-api/revisions/7881947e8e43?raw)
