v1 · base url https://api.tennis-model.io
Introduction
The tennis-model API returns live win probabilities for in-progress ATP & WTA singles matches. Probabilities come from a point-by-point Markov simulation re-solved from the current score every 30 seconds — surface-specific serve stats, pressure score-state adjustments, fatigue, and momentum included.
Every response is plain JSON. There's no SDK and nothing to install — authenticate with a key in the query string and make a GET request. The API is CORS-open, so you can call it directly from the browser.
Authentication
Pass your key as the key query parameter on every request. Keys are free and scoped to the free tier by default.
tm_live_••••••••••••••••••••••••
Generate free key
Keep keys server-side when you can. Need a higher limit? Reply to your signup email — we lift limits for non-commercial and research use.
Rate limits
| Tier | Requests / minute | Price |
|---|---|---|
| Free | 60 | $0 |
| Research | unlimited | $0 · on request |
Every response includes X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset (Unix timestamp) headers. The model recomputes every 30 seconds — polling more than once or twice a minute returns cached values.
GET /v1/live
Returns an array of every match currently in progress, each with both players' live win probability.
Query parameters
| Param | Type | Description |
|---|---|---|
| key | string · required | Your API key. |
| tour | string | Filter by atp or wta. Omit for both. |
| surface | string | Filter by clay, grass, or hard. |
GET /v1/match/:id
Fetch a single match by its stable match_id — useful when you've stored an id and want to refresh just that match.
Returns the same object shape as a single element of /v1/live. Returns 404 once a match has finished.
GET /v1/players/{name}
Look up the serve parameters the model uses for a player on a given surface. Useful for debugging model inputs or building your own pre-match probability tool.
Query parameters
| Param | Type | Description |
|---|---|---|
| surface | string | clay, grass, or hard. Defaults to hard. |
Response
| Field | Type | Description |
|---|---|---|
| name | string | Canonical display name used by the model. |
| found | bool | true if the player is in the model's roster. false returns the tour-average serve rate. |
| surface | string | The surface the stats are for. |
| p_serve | float | P(server wins a point on serve) for this surface, 0–1. |
Response schema
Each match object in the live feed:
| Field | Type | Description |
|---|---|---|
| match_id | string | Stable id — safe to join on across polls for the duration of a match. |
| player1 / player2 | string | Display names in API feed order. |
| tour | string | atp or wta. |
| surface | string | One of clay, grass, hard. |
| tournament | string | Event name, e.g. Roland Garros. |
| score | object | { sets, games, points, serving, best_of } — full live score state. serving is "player1" or "player2". |
| p1_win_prob | float | Player 1 match-win probability, 0–1. Sums to 1 with p2_win_prob. |
| p2_win_prob | float | Player 2 match-win probability, 0–1. |
| p1_found / p2_found | bool | true if the player is in the model's roster. false means the probability is based on tour-average serve stats. |
| confidence | string | Data quality of the WP estimate. full — both players in roster. partial — one player known. score_only — neither player in roster; WP is driven by live score state and tour-average serve stats only. Filter on confidence !== "score_only" before using WP as a trading signal. |
| calibrated_wp_p1 / calibrated_wp_p2 | float | Temperature-scaled win probability. Applies a fitted calibration factor (temperature T) to compress overconfident raw WPs toward 50%. Use these instead of p1_win_prob for any quantitative sizing or edge calculation. |
| calibration_t | float | The temperature value used. T=1.0 is identity (no scaling). Higher values compress toward 50%. Updated periodically as settled match data accumulates. |
| is_live | bool | Always true in the live feed. |
Surfaces
The model carries independent serve and return distributions per surface. The same player can have very different hold rates on clay versus grass — that difference is the whole point.
Errors
Errors return a JSON body with an error string and the standard HTTP status.
| Status | Meaning |
|---|---|
| 401 | Missing or invalid key. |
| 404 | Unknown match_id (match finished or never existed). |
| 429 | Rate limit exceeded — check X-RateLimit-Reset. |
| 503 | Poller temporarily behind — retry in a few seconds. |