GET /markets
Match-winner market(s) for a match (PRO)
Parameters
| Name | In | Type | Required | Notes |
|---|---|---|---|---|
match_id | query | integer | yes |
Responses
| Status | Meaning |
|---|---|
200 | Markets |
400 | Bad query parameter |
401 | Missing, unknown, or disabled credentials |
403 | Your tier doesn't unlock this endpoint |
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. |
Response fields
| Field | Type | Description |
|---|---|---|
data | array of object | |
meta | object |
Example
curl https://api.livetennisapi.com/api/public/v1/markets \
-H "Authorization: Bearer twjp_..."
GET /markets/{matchId}/prices
Market + recent price ticks per side, newest first (PRO)
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.
Parameters
| Name | In | Type | Required | Notes |
|---|---|---|---|---|
matchId | path | integer | yes | The match id. Every match route shares ONE id space, so the same value works everywhere matchId appears.
Get it from any match list, where it is the id field of each match object:
GET /matches?status=live (in progress), GET /matches?status=upcoming or GET /fixtures (scheduled), GET /matches?status=completed or GET /history/matches (finished).
Ids are stable for the life of a match, so one captured before it starts still resolves after it finishes.
|
limit | query | integer | no | Default 50. |
Responses
| Status | Meaning |
|---|---|
200 | Market with prices |
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. |
Response fields
| Field | Type | Description |
|---|---|---|
id | integer | |
question | string or null | |
status | string or null (active, resolved, closed, null) | |
volume | number or null | |
liquidity | number or null | |
end_date | string or null | |
prices | array of object | Prices endpoint / match detail only; newest first |
Example
curl https://api.livetennisapi.com/api/public/v1/markets/18953/prices \
-H "Authorization: Bearer twjp_..."
GET /matches/{matchId}/prices
Bare price ticks of the mapped match-winner market, newest first (PRO)
Recent ticks only (no market wrapper). limit caps at 500; minutes bounds the lookback window. 404 when the match has no mapped market.
Ticks are kept for 30 days and then deleted, so a match older than that answers an empty data while its market stays mapped — that is retention, not a fault. Past 500 ticks, page with the cursor: read a page, and while meta.has_more is true pass meta.next_cursor back as ?cursor= for the next (older) page. Pages never overlap or skip a tick (added 2026-09-09).
Parameters
| Name | In | Type | Required | Notes |
|---|---|---|---|---|
matchId | path | integer | yes | The match id. Every match route shares ONE id space, so the same value works everywhere matchId appears.
Get it from any match list, where it is the id field of each match object:
GET /matches?status=live (in progress), GET /matches?status=upcoming or GET /fixtures (scheduled), GET /matches?status=completed or GET /history/matches (finished).
Ids are stable for the life of a match, so one captured before it starts still resolves after it finishes.
|
limit | query | integer | no | Default 100. |
minutes | query | integer | no | |
cursor | query | string | no | Keyset cursor for the next (older) page — the meta.next_cursor of the previous page (added 2026-09-09). Opaque; anything else is 400 bad_cursor. |
Responses
| Status | Meaning |
|---|---|
200 | Price ticks |
400 | Bad query parameter |
401 | Missing, unknown, or disabled credentials |
403 | Your tier doesn't unlock this endpoint |
404 | No such resource, or no data yet |
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. |
Response fields
| Field | Type | Description |
|---|---|---|
data | array of object | |
meta | object |
Example
curl https://api.livetennisapi.com/api/public/v1/matches/18953/prices \
-H "Authorization: Bearer twjp_..."