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.

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

https://mcp.api-tennis.com/mcp

Header

HeaderValue
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

ParameterTypeRequiredDescription
event_typestringNo*Event type/category name or partial match, e.g. "Atp Singles"
tournament_namestringNo*Tournament name or partial match, e.g. "Wimbledon"
limitintegerNoMax 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

ParameterTypeRequiredDescription
date_fromstringYes, unless tournament_id givenStart date, YYYY-MM-DD
date_tostringYes, unless tournament_id givenEnd date (max 15 days after date_from)
tournament_idstringNoFrom search_tennis_tournaments
event_type_idstringNoNarrows by tour/draw category
player_idstringNoFrom another fixture's player1_id/player2_id
timezonestringNoIANA timezone, e.g. Europe/Bucharest. Defaults to UTC.
limitintegerNoMax 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_live_tennis_matches

Matches currently in progress, optionally filtered by tournament, event type or player. Same response shape as get_tennis_fixtures.

Parameters

ParameterTypeRequiredDescription
tournament_idstringNoFrom search_tennis_tournaments
event_type_idstringNoNarrows by tour/draw category
player_idstringNo 
timezonestringNoDefaults to UTC
limitintegerNoMax 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

ParameterTypeRequiredDescription
tourstringYes"ATP" or "WTA"
limitintegerNoMax 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

ParameterTypeRequiredDescription
match_idstringYesFrom get_tennis_fixtures / get_live_tennis_matches
includearrayNoAny 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

ParameterTypeRequiredDescription
player1_idstringYesFrom get_tennis_fixtures, get_live_tennis_matches or get_tennis_standings
player2_idstringYes 

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

ParameterTypeRequiredDescription
match_idstringStrongly recommendedFrom get_tennis_fixtures/get_live_tennis_matches
date_fromstringYes, unless match_id givenStart date, YYYY-MM-DD
date_tostringYes, unless match_id givenMax 7 days after date_from
event_type_idstringNoNarrows by tour/draw category
tournament_idstringNoFrom search_tennis_tournaments
limitintegerNoMax 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

ParameterTypeRequiredDescription
match_idstringRecommendedFrom get_live_tennis_matches
tournament_idstringNo 
event_type_idstringNo 
player_idstringNo 
limitintegerNoMax 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

ParameterTypeRequiredDescription
player_idstringNo*From get_tennis_fixtures, get_live_tennis_matches or get_tennis_standings
tournament_idstringNo*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

ParameterTypeRequiredDescription
date_fromstringYesOnly articles published on/after this date
date_tostringYesOnly articles published on/before this date
player_idstringNoNews about this player, including match reports they're in
tournament_idstringNoNews about this tournament, including its match reports
match_idstringNoThe article about this specific match, if one exists
limitintegerNoMax 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.

MessageMeaning
Wrong login credentialsThe API key is invalid.
...you must have the Ultra planYour account isn't on the Ultra plan — required for MCP access and for get_tennis_news specifically.
...you must have the Business planget_live_tennis_odds requires Business plan or higher.