567,911 tennis matches, results and odds.
Grand Slams, Masters, ATP/WTA tour events, Challengers and ITF. Result, score, surface, round, pre-match odds from two reference bookmakers, best price and margin-free consensus probability.
Choose your option
One purchase, one download: CSV + Parquet and a README documenting every column.
2021 → 2026
The recent seasons.
- 377,784 matches, Jan 1, 2021 → Sep 5, 2026
Download link valid 72 h, 5 downloads
Buy — €19Full history
Jan 1, 2015 → Sep 5, 2026 (2026 frozen).
- 567,911 matches, one file per season + a combined file
- Joins the Line Movements 2026 dataset on match_id
- CSV + Parquet
Download link valid 72 h, 5 downloads
Buy — €29Do favourites win as often as the odds say?
Singles matches grouped by the favourite's probability (margin-free consensus), and the real result.
Descriptive chart, for illustration: it is not a strategy.
| FAV. PROB. | MATCHES | Favourite wins | Upset | Implied |
|---|---|---|---|---|
| 50-60 % | 151,750 | 55.5% | 44.5% | 55.0% |
| 60-70 % | 138,202 | 66.4% | 33.6% | 64.8% |
| 70-80 % | 116,939 | 77.4% | 22.6% | 74.8% |
| 80-90 % | 87,573 | 89.0% | 11.0% | 85.0% |
| ≥ 90 % | 19,741 | 96.5% | 3.5% | 91.6% |
Built for modelers
p1 is always the lower player id, never the winner: no result leakage in the file layout.
Calibrate a model
Margin-free consensus probability versus the real result.
t = pd.read_parquet("tennis_results_odds_2015_2026.parquet")
s = t[t.is_doubles == 0]
s.groupby(pd.cut(s.consensus_prob_p1, 10)).result
.apply(lambda r: (r == "P1").mean())By surface
Clay, hard, grass: compare how favourites fare.
s.groupby("surface").result.value_counts(normalize=True)Best price
The better of the two prices, picked after the fact: it overstates what a bettor would really have got.
s[["best_odds_p1", "a_odds_p1", "b_odds_p1"]].describe()Two bookmakers, labelled a and b. We do not guess.
The source does not document the two bookmakers' identity: we call them a and b rather than invent a name. b is the one with the lower measured margin. These are prices recorded before the match, neither opening nor closing, without timestamps.
Totals and handicaps: bookmaker a quotes games; bookmaker b quoted games until mid-September 2022, then sets (total sets, set handicap) — the b_total_unit and b_handicap_unit columns say which, row by row. They are only present when the bookmaker offered them: the fill rate of every column is shown below.
Corrected on 22 Oct 2026: in the previous edition, over and under prices were swapped and bookmaker b's set markets were described as games. Checked since against final scores, season by season; details in the README.
Corrected on 28 Oct 2026: from March 2022, on ATP/WTA events, bookmaker a's total line no longer matched its prices (a source defect); those totals are now empty (22,982 matches), the rest is checked against the scores.
What's in the file — and what isn't
Included
- Result, score, retirements and walkovers
- Tournament, level, surface, round, format
- Pre-match odds from two bookmakers
- Best price and consensus probability (ProbWin)
Not included
- Closing line
- Odds timestamps (→ Line Movements 2026)
- Names of the two bookmakers
Every column, with its fill rate
Measured on the delivered file. 100% = no missing values.
Match
Identity, tour, date, tournament, level, surface, round, format
Players
Ids and names (p1 = lower id), doubles
Result
Winner P1/P2, score, retirement, walkover
Odds
Odds of bookmakers a and b, best price, consensus probability
Totals & handicaps
Total and handicap when offered: a in games, b in games then sets since Sept 2022 (b_total_unit, b_handicap_unit)
The file, exactly as you'll get it
Six real rows, main columns. Download 50 free rows with every column to check it fits your pipeline.
| match_date | tour | tournament_name | surface | round_name | p1_name | p2_name | result | score | a_odds_p1 | a_odds_p2 | best_odds_p1 | best_odds_p2 | consensus_prob_p1 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2025-01-05 | atp | M15 Kingston | hard | q-First | Evan Sharygin | Elias Shokry | P1 | w/o | 2.09 | 1.66 | 2.09 | 1.66 | 0.4427 |
| 2025-03-09 | wta | W75 Targu Mures | indoor_hard | q-First | Oana Georgeta Simion | Patricia Georgiana Goina | P1 | 6-2 6-1 | 1.28 | 3.32 | 1.28 | 3.32 | 0.7217 |
| 2025-04-23 | wta | W15 Wuning | hard | First | Feier Hu | Zhirui Liu | P2 | 6-4 6-2 | 1.5 | 2.41 | 1.5 | 2.57 | 0.6286 |
| 2025-05-29 | atp | M25 Bol | clay | Second | Dan Added | Admir Kalender | P1 | 6-4 6-1 | 1.168 | 4.45 | 1.168 | 4.98 | 0.8032 |
| 2025-07-09 | atp | M15 Monastir | hard | First | Amr Elsayed | Oscar Lacides | P2 | 7-6(1) 6-4 | 2.17 | 1.61 | 2.17 | 2.04 | 0.4864 |
| 2025-10-31 | wta | Hong Kong Tennis Open - Hong Kong | hard | QF | Sorana Cirstea | Leylah Annie Fernandez | P2 | 6-4 6-4 | 2.375 | 1.58 | 2.48 | 1.588 | 0.3949 |
Before you buy
What is the difference between the two offers?
Same columns, one file per season in both: 2021 → 2026, or the full history since 2015. The 2026 season is frozen on Sep 5, 2026.
Which way is the score written?
Always winner-first, not from p1's side. The result column (P1/P2) gives the winner: to read the score from p1's side, reverse each set when result = P2.
What does the tournament_level column mean?
A level code: 0 = ITF entry level (Futures until 2018, M15/W15 since 2019, plus men's M25), 1 = Challengers, WTA 125 and women's ITF W25 to W100, 2 = ATP/WTA 250 and 500 events, 3 = Masters 1000 / WTA 1000, 4 = Grand Slams (qualifying included), 5 = team events (Davis Cup, Billie Jean King Cup), 6 = other (juniors, exhibitions, wildcard play-offs).
Do you have seasons before 2015?
Yes, 2004 to 2014: by season, as a bundle or in the complete 2004 → 2026 edition, on the Results & Odds 2004–2014 page (/data-shop/tennis-results-odds-2004-2014/).
Is there more than one total or handicap line per match?
No: one total line and one handicap line per bookmaker and per match, with no alternative-line ladder.
How are walkovers and retirements handled?
The file gives the official winner, including on a walkover (walkover column) or a retirement (retirement column). Bookmakers usually void bets on a walkover and their rules on retirements vary: filter both columns before computing a return.
How do I get the file?
Right after payment a download link appears on screen and is emailed to you. The archive contains CSV and Parquet files plus a README documenting every column.
Can I use it in a commercial product?
Yes for your models, analyses and tools, commercial ones included. Reselling or redistributing the files themselves is not allowed.
A question before buying?
Email [email protected] — the founder answers, usually the same day.