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

TierRequests / minutePrice
Free60$0
Researchunlimited$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.

GET /v1/live?key=YOUR_KEY

Query parameters

ParamTypeDescription
keystring · requiredYour API key.
tourstringFilter by atp or wta. Omit for both.
surfacestringFilter 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.

GET /v1/match/{match_id}?key=YOUR_KEY

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.

GET /v1/players/{name}?surface=clay&key=YOUR_KEY

Query parameters

ParamTypeDescription
surfacestringclay, grass, or hard. Defaults to hard.

Response

FieldTypeDescription
namestringCanonical display name used by the model.
foundbooltrue if the player is in the model's roster. false returns the tour-average serve rate.
surfacestringThe surface the stats are for.
p_servefloatP(server wins a point on serve) for this surface, 0–1.

Response schema

Each match object in the live feed:

FieldTypeDescription
match_idstringStable id — safe to join on across polls for the duration of a match.
player1 / player2stringDisplay names in API feed order.
tourstringatp or wta.
surfacestringOne of clay, grass, hard.
tournamentstringEvent name, e.g. Roland Garros.
scoreobject{ sets, games, points, serving, best_of } — full live score state. serving is "player1" or "player2".
p1_win_probfloatPlayer 1 match-win probability, 0–1. Sums to 1 with p2_win_prob.
p2_win_probfloatPlayer 2 match-win probability, 0–1.
p1_found / p2_foundbooltrue if the player is in the model's roster. false means the probability is based on tour-average serve stats.
confidencestringData 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_p2floatTemperature-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_tfloatThe temperature value used. T=1.0 is identity (no scaling). Higher values compress toward 50%. Updated periodically as settled match data accumulates.
is_liveboolAlways 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.

clay grass hard

Errors

Errors return a JSON body with an error string and the standard HTTP status.

StatusMeaning
401Missing or invalid key.
404Unknown match_id (match finished or never existed).
429Rate limit exceeded — check X-RateLimit-Reset.
503Poller temporarily behind — retry in a few seconds.