MCP Server Documentation (Ultra plan)
The API Tennis MCP server exposes our tennis data as tools that an AI agent can call directly, using the open Model Context Protocol (MCP) standard. It sits in front of the same data that powers our REST API, so your existing Ultra plan API key works unchanged — the MCP server is a thin adapter, not a separate product.
count/truncated wrapper on list results) — it is not
the same shape as the raw api.api-tennis.com REST response documented on the
REST API docs page.
Authentication
Authentication uses your existing Ultra plan API Tennis API key as an OAuth-style bearer token — no separate MCP account or credentials.
Request URL
Header
| Header | Value |
|---|---|
| Authorization | Bearer YOUR_ULTRA_APITENNIS_API_KEY — the same key you use for APIkey= on REST calls. |
Your plan's existing limits (requests, coverage, historical range) apply exactly as they do on the REST API. There is no separate MCP quota — only the Ultra-plan requirement itself.
Connecting Your AI Client
Most MCP clients (Claude Desktop, Claude Code, Cursor) use the same JSON configuration shape:
{
"mcpServers": {
"api-tennis": {
"url": "https://mcp.api-tennis.com/mcp",
"headers": {
"Authorization": "Bearer YOUR_ULTRA_APITENNIS_API_KEY"
}
}
}
}
Once added, restart your AI client and the tools listed in the sidebar become available — you can just ask tennis questions in natural language and the client will call the right tool automatically.
search_tennis_tournaments
Search tennis tournaments by event type and/or name, to resolve a tournament_id before calling other tools. Always call this first if you don't already have one — never guess it. event_type describes the tour/draw category — e.g. "Atp Singles", "Wta Doubles", "Challenger Men Singles" — not a geographic country.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| event_type | string | No* | Event type/category name or partial match, e.g. "Atp Singles" |
| tournament_name | string | No* | Tournament name or partial match, e.g. "Wimbledon" |
| limit | integer | No | Max results, 1–100 |
* At least one of event_type or tournament_name is required.
Example call
{ "event_type": "Atp Singles", "tournament_name": "Wimbledon" }
Example response
{
"count": 1,
"tournaments": [
{ "tournament_id": 2053, "name": "Wimbledon", "event_type": "Atp Singles", "event_type_id": 265, "surface": "Grass" }
]
}
get_tennis_fixtures
Fixtures and results, optionally filtered by tournament, event type or player. Provide date_from/date_to (max 15 days apart), OR a tournament_id on its own (no dates required — returns the whole tournament's matches).
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| date_from | string | Yes, unless tournament_id given | Start date, YYYY-MM-DD |
| date_to | string | Yes, unless tournament_id given | End date (max 15 days after date_from) |
| tournament_id | string | No | From search_tennis_tournaments |
| event_type_id | string | No | Narrows by tour/draw category |
| player_id | string | No | From another fixture's player1_id/player2_id |
| timezone | string | No | IANA timezone, e.g. Europe/Bucharest. Defaults to UTC. |
| limit | integer | No | Max results, 1–100 |
Example call
{ "tournament_id": "2053", "limit": 1 }
Example response
{
"count": 1,
"truncated": true,
"note": "1453 matches matched this query but only the first 1 are shown. Narrow with tournament_id, event_type_id or player_id (or a smaller date range).",
"matches": [
{
"match_id": 32388,
"date": "2021-06-22",
"time": "10:10",
"status": "Finished",
"event_type": "Atp Singles",
"tournament_id": 2053,
"tournament": "Wimbledon",
"tournament_round": "Wimbledon - Quarter-finals",
"tournament_season": "2021",
"player1": "A. Matusevich",
"player1_id": 6886,
"player2": "W. Jansen",
"player2_id": 2237,
"final_result": "2 - 1",
"game_result": "-",
"serving": null,
"winner": "First Player",
"live": false
}
]
}
get_tennis_match_details with include for those.
get_live_tennis_matches
Matches currently in progress, optionally filtered by tournament, event type or player. Same response shape as get_tennis_fixtures.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| tournament_id | string | No | From search_tennis_tournaments |
| event_type_id | string | No | Narrows by tour/draw category |
| player_id | string | No | |
| timezone | string | No | Defaults to UTC |
| limit | integer | No | Max results, 1–100 |
Example call
{}
Example response
{
"count": 1,
"truncated": false,
"matches": [
{
"match_id": 12148992,
"date": "2026-07-24",
"time": "07:10",
"status": "Set 1",
"event_type": "Itf Men Singles",
"tournament_id": 4479,
"tournament": "M25+H Bacau",
"tournament_round": "M25+H Bacau - Quarter-finals",
"tournament_season": "2026",
"player1": "R. D. Turcanu",
"player1_id": 63402,
"player2": "T. Batin",
"player2_id": 58949,
"final_result": "0 - 0",
"game_result": "15 - 30",
"serving": "First Player",
"winner": null,
"live": true
}
]
}
get_tennis_standings
Returns the current top-level ATP or WTA tour ranking (not a per-tournament standing — tennis tournaments are single-elimination brackets, not league tables).
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| tour | string | Yes | "ATP" or "WTA" |
| limit | integer | No | Max results, 1–100 |
Example call
{ "tour": "ATP", "limit": 1 }
Example response
{
"count": 1,
"truncated": true,
"note": "2277 ranked players; only the first 1 are shown.",
"standings": [
{ "rank": 1, "player": "Jannik Sinner", "player_id": 2072, "tour": "ATP", "movement": "same", "country": "Italy", "points": "13450" }
]
}
get_tennis_match_details
Details for a single match by id: players, score, status, and optionally set-by-set scores/statistics/point-by-point via include. All of this data comes from a single underlying call, so asking for more include values has no extra latency cost. Unlike some sports APIs, match_id alone is enough here — no date range required.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| match_id | string | Yes | From get_tennis_fixtures / get_live_tennis_matches |
| include | array | No | Any of: scores, statistics, pointbypoint |
Example call
{ "match_id": "32387", "include": ["scores"] }
Example response
{
"match_id": 32387,
"date": "2021-06-22",
"time": "12:10",
"status": "Finished",
"event_type": "Atp Singles",
"tournament_id": 2053,
"tournament": "Wimbledon",
"tournament_round": "Wimbledon - Quarter-finals",
"tournament_season": "2021",
"player1": "A. Vukic",
"player1_id": 360,
"player2": "B. Nakashima",
"player2_id": 2838,
"final_result": "0 - 2",
"game_result": "-",
"serving": null,
"winner": "Second Player",
"live": false,
"qualification": false,
"scores": [
{ "score_first": "1", "score_second": "6", "score_set": "1" },
{ "score_first": "1", "score_second": "6", "score_set": "2" }
]
}
get_tennis_head_to_head
Historical only: already-completed direct meetings between two players (capped at the last 10), plus each player's own recent finished results. A match scheduled for today or the future will NOT appear here — use get_tennis_fixtures or get_live_tennis_matches instead. Only accepts numeric player ids, not names.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| player1_id | string | Yes | From get_tennis_fixtures, get_live_tennis_matches or get_tennis_standings |
| player2_id | string | Yes |
Example call
{ "player1_id": "2072", "player2_id": "1980" }
Example response
{
"head_to_head": [
{
"match_id": 12145144, "date": "2026-07-12", "status": "Finished", "event_type": "Atp Singles",
"tournament": "Wimbledon", "tournament_round": "ATP Wimbledon - Final",
"player1": "J. Sinner", "player1_id": 2072, "player2": "A. Zverev", "player2_id": 1980,
"final_result": "3 - 1", "winner": "First Player", "live": false
}
],
"player1_recent_form": [ "...compact matches..." ],
"player2_recent_form": [ "...compact matches..." ]
}
get_tennis_odds
Real bookmaker pre-match odds, grouped by match then by market (e.g. "Home/Away", "Correct Score 1st Half") then by bookmaker. Markets vary a lot by match — read the markets object's own keys rather than assuming specific ones exist. Provide match_id (recommended) for one match, or a date range (max 7 days — an api-tennis-enforced limit) for a whole range.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| match_id | string | Strongly recommended | From get_tennis_fixtures/get_live_tennis_matches |
| date_from | string | Yes, unless match_id given | Start date, YYYY-MM-DD |
| date_to | string | Yes, unless match_id given | Max 7 days after date_from |
| event_type_id | string | No | Narrows by tour/draw category |
| tournament_id | string | No | From search_tennis_tournaments |
| limit | integer | No | Max results, 1–100 |
Example call
{ "match_id": "12148225" }
Example response
{
"count": 1,
"truncated": false,
"odds": [
{
"match_id": "12148225",
"markets": {
"Home/Away": { "Home": { "bet365": "2.50" }, "Away": { "bet365": "1.50" } }
}
}
]
}
get_live_tennis_odds
In-play betting odds (market name, value, handicap, suspended status) for matches currently live. Unlike some sports APIs, this has no accompanying text commentary field. Narrow with match_id — without it, returns every live match worldwide with live odds.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| match_id | string | Recommended | From get_live_tennis_matches |
| tournament_id | string | No | |
| event_type_id | string | No | |
| player_id | string | No | |
| limit | integer | No | Max results, 1–100 |
Example call
{ "match_id": "12148993" }
Example response
{
"count": 1,
"truncated": false,
"matches": [
{
"match_id": 12148993,
"date": "2026-07-24",
"time": "09:10",
"status": "Set 1",
"event_type": "Itf Men Singles",
"tournament": "M25+H Bacau",
"tournament_id": 4479,
"player1_id": 15768,
"player2_id": 38014,
"game_result": "15 - 15",
"serving": "Second Player",
"live_odds": [
{ "odd_name": "Total Games in Match", "suspended": "No", "type": "Over", "value": "1.833", "handicap": "24.5", "upd": "2026-07-24 09:47:33" }
]
}
]
}
get_tennis_player
A player's profile (name, country, birthday) plus season-by-season stats (rank, titles, matches won/lost by surface) and tournament history. Provide player_id, or tournament_id to list every player who competed in that tournament's most recent season instead.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| player_id | string | No* | From get_tennis_fixtures, get_live_tennis_matches or get_tennis_standings |
| tournament_id | string | No* | From search_tennis_tournaments — lists that tournament's players instead |
* Provide at least one.
Example call
{ "player_id": "2072" }
Example response
{
"count": 1,
"players": [
{
"player_id": 2072,
"name": "J. Sinner",
"country": "Italy",
"birthday": "16.08.2001",
"stats": [ { "season": "2026", "type": "singles", "rank": "1", "titles": "5", "matches_won": "58", "matches_lost": "6" } ],
"tournaments": [ { "name": "Wimbledon", "season": "2026", "type": "singles", "surface": "Grass", "prize": "3,000,000" } ]
}
]
}
get_tennis_news
AI-synthesized news articles about players, tournaments and matches, with source publications listed per article. An article can be linked to a player, a tournament and a match all at once — any of player_id/tournament_id/match_id on a given article can independently be null. date_from/date_to filter by publish date and are required.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| date_from | string | Yes | Only articles published on/after this date |
| date_to | string | Yes | Only articles published on/before this date |
| player_id | string | No | News about this player, including match reports they're in |
| tournament_id | string | No | News about this tournament, including its match reports |
| match_id | string | No | The article about this specific match, if one exists |
| limit | integer | No | Max results, 1–100 |
Example call
{ "date_from": "2026-07-01", "date_to": "2026-07-24", "limit": 1 }
Example response
{
"count": 1,
"truncated": true,
"note": "393 articles matched; only the first 1 are shown. Narrow the date range or add a player/tournament/match filter.",
"articles": [
{
"news_id": "443",
"title": "Sabalenka Gives Back to Minsk with New Tennis Club Bearing Her Name",
"content": "World No. 1 Aryna Sabalenka has added a new title to her résumé...",
"published_at": "2026-07-24 06:00:29.235",
"player_id": null,
"tournament_id": null,
"match_id": null,
"sources": ["ubitennis"]
}
]
}
Errors
Tool errors come back as a normal MCP tool result with isError: true, so your AI client can read the message and decide what to do next.
Example: plan-gated method
{
"content": [
{ "type": "text", "text": "api-tennis API error (method=get_news): You do not have access to this method. To have access to this method, you must have the Ultra plan" }
],
"isError": true
}
The whole MCP connection itself is also gated: an initialize call with a non-Ultra or invalid key is rejected with HTTP 403 before any session is created — you won't see individual tool errors in that case, the connection simply won't establish.
| Message | Meaning |
|---|---|
Wrong login credentials | The API key is invalid. |
...you must have the Ultra plan | Your account isn't on the Ultra plan — required for MCP access and for get_tennis_news specifically. |
...you must have the Business plan | get_live_tennis_odds requires Business plan or higher. |