Build a tennis player profile page
Bio, form, match log, ranking chart and biggest rivalries in five parallel calls
1. From a name to an id
/players/search matches typos ("alcarez") and nicknames ("Nole"). Store the canonical_id it returns. If you ever call a route with a wrong id, the 404 lists the ids you most likely meant in error.suggestions.
curl "https://api.citoapi.com/api/v1/tennis/players/search?q=alcarez&limit=3" -H "x-api-key: YOUR_API_KEY"
# items[0].id -> "atp_207989" (Carlos Alcaraz)2. Everything for the page, in parallel
const BASE = "https://api.citoapi.com/api/v1/tennis";
const headers = { "x-api-key": process.env.CITO_API_KEY };
const get = (path) => fetch(BASE + path, { headers }).then((r) => r.json());
export async function playerPage(id) {
const [profile, stats, form, log, ranks] = await Promise.all([
get(`/players/${id}`), // bio, country, current rank, career high
get(`/players/${id}/stats`), // career record, surfaces, serve and return
get(`/players/${id}/form?limit=10`), // last 10 results with opponent ranks
get(`/players/${id}/matches?view=summary&page_size=20`), // match log, one slim row per match
get(`/rankings/history/${id}?limit=520&order=asc`), // ~10 years of weekly ranks for a chart
]);
return { profile, stats, form, log: log.items, ranks: ranks.history };
}| Section | Fields |
|---|---|
| Header | full_name, ioc, iso_alpha2 (flag), dob, hand, current_rank (null when off the list: show last_rank), career_high_rank and career_high_date, portrait_url with its credit |
| Record | career_summary (titles, Grand Slam titles, win %), surface_breakdown, by_level |
| Game style | serving_stats and return_stats (first-serve won %, break points saved %, return games won %), clutch_and_situational (tiebreaks, deciding sets, vs top 10) |
| Form strip | form.matches[]: result W/L, score, opponent_rank, surface |
| Ranking chart | ranks[] (the response's history): date, rank, points per published week, plus total_weeks_at_no_1 |
Filter the record with /players/{id}/stats?surface=Clay, level=G or year_from / year_to for surface tabs or a season selector.
3. Rivalries
One call gives a rivalry with surface, level, format, tiebreak and deciding-set splits. For a grid of a player against several rivals, use the matrix (2 to 16 players).
curl "https://api.citoapi.com/api/v1/tennis/h2h?players=atp_206173,atp_207989" -H "x-api-key: YOUR_API_KEY"
curl "https://api.citoapi.com/api/v1/tennis/h2h/matrix?player_ids=atp_206173,atp_207989,atp_100644,atp_104925" -H "x-api-key: YOUR_API_KEY"4. Match statistics on the log
To show serve stats next to each match in the log, add include=stats to the same call instead of calling /matches/{id}/stats for every row:
curl "https://api.citoapi.com/api/v1/tennis/players/atp_206173/matches?page_size=20&include=stats" -H "x-api-key: YOUR_API_KEY"Caching
- Profile, career stats and ranking history change at most weekly (rankings publish on Mondays): cache for a day.
- Form and the match log change when the player plays: cache for an hour, or refresh on a
tennis.match.completedwebhook for that player. - A cached page costs 0 calls. Five calls per uncached page view.
Find any player's id
Search is typo-tolerant and knows nicknames, so a search box can feed this page directly.
curl "https://api.citoapi.com/api/v1/tennis/players/search?q=sinner&limit=5" \
-H "x-api-key: YOUR_API_KEY"