Guides/Build a tennis draw and tournament page

Build a tennis draw and tournament page

Season calendar, round-by-round draws for any edition, and the tournament's matches with statistics

1. The calendar

One row per edition with dates, surface, tier, city and draw size. The year has 1,000+ editions per tour, so page with limit and page and filter on category in your code (for example grand_slam, masters_1000, atp_tour).

bash
curl "https://api.citoapi.com/api/v1/tennis/tournaments/calendar?year=2026&tour=ATP&limit=100&page=1" -H "x-api-key: YOUR_API_KEY"
# items[]: id, name, start_date, end_date, surface, category, tier ("ATP 250"), city, country_code, draw_size, indoor

curl "https://api.citoapi.com/api/v1/tennis/tournaments/upcoming?tour=WTA&limit=20" -H "x-api-key: YOUR_API_KEY"   # in progress or next

2. The draw

rounds runs from the final back to the first qualifying round. Each match carries both players with seed and entry (Q, WC, LL, PR, SE, ALT), score, winner_id, bracket_position (restarts at 1 each round) and next_match_id for drawing the lines between rounds.

javascript
const BASE = "https://api.citoapi.com/api/v1/tennis";
const headers = { "x-api-key": process.env.CITO_API_KEY };
const draw = await fetch(`${BASE}/tournaments/atp_2025_540/draw`, { headers }).then((r) => r.json());

// Main draw only, first round first: R128 -> F
const main = draw.rounds.filter((r) => !r.round_code.startsWith("Q")).reverse();
for (const round of main) {
  console.log(round.round_name);
  for (const m of [...round.matches].sort((a, b) => a.bracket_position - b.bracket_position)) {
    const p = (x) => `${x.seed ? "[" + x.seed + "] " : ""}${x.entry ? "(" + x.entry + ") " : ""}${x.name}`;
    console.log(`  ${p(m.player1)} v ${p(m.player2)}  ${m.score ?? m.status}`);
  }
}
// draw.summary: status, main_draw_matches_completed / _pending, rounds_in_progress
  • Any past edition: /tournaments/{id}/editions/{year}/draw follows the event across years, for example /tournaments/atp_2025_540/editions/1985/draw.
  • Grand Slam ids for any year: {tour}_{year}_{code} with 580 Australian Open, 520 Roland Garros, 540 Wimbledon, 560 US Open.
  • Doubles are not in the bracket; read them from /matches?tournament_id=...&match_type=doubles.
  • surface_badge gives a label and colour for the surface chip.

3. Order of play and match statistics

The schedule lists the edition's matches in date and round order. Add include=stats to show serve statistics on finished matches without one call per match.

bash
curl "https://api.citoapi.com/api/v1/tennis/tournaments/atp_2025_540/schedule?include=stats&page_size=100" -H "x-api-key: YOUR_API_KEY"

4. Keep it live during the event

Refresh the draw when a match in the event finishes instead of on a timer: subscribe to tennis.match.completed filtered to the tournament and re-read /draw on each event. For live scores inside the bracket, use the live scoreboard stream filtered by match.

Call budget

  • Calendar for a season: 10 to 11 calls per tour at 100 per page. Cache for a day.
  • A finished edition's draw never changes: cache it permanently.
  • A live edition: 1 call per finished match if you refresh on webhooks.

Open the 2025 Wimbledon draw

Every round from qualifying to the final, with seeds, entry status, scores and where each winner goes next.

bash
curl "https://api.citoapi.com/api/v1/tennis/tournaments/atp_2025_540/draw" \
  -H "x-api-key: YOUR_API_KEY"
Was this page helpful?