Documentation โ€” Tennis API
Login Get API Key

Tennis API Documentation

Simple REST API. Pass your API key as a query parameter โ€” no headers required.

Base URL: https://live-tennis-api.com/api/v1

Authentication

Add api_key to every request URL. You can get a key by creating a free account.

https://live-tennis-api.com/api/v1/matches?api_key=YOUR_KEY
Do not expose your API key in public client-side code or public repos.

Credits

Each API call consumes credits from your balance. Credits never expire. New accounts get 100 free credits.

EndpointCost
GET /matches1 credit per call
GET /match/scores1 credit per call
GET /match/statistics1 credit per call
GET /match/h2h1 credit per call
GET /match/trackerFree โ€” no API key required
GET /rankings1 credit per call
GET /tournament/details1 credit per call
GET /tournament/bracket1 credit per call
GET /tournament/results1 credit per call
GET /player/profile1 credit per call
GET /player/matches1 credit per call
GET /player/statistics1 credit per call
GET /webhook/registerFree โ€” no API key required
GET /webhook/listFree โ€” no API key required
GET /webhook/deleteFree โ€” no API key required

Remaining balance is returned in every response under credits_remaining and the X-Credits-Remaining header.

Errors

All errors return JSON with a code and message:

{ "error": "Invalid API key.", "code": 401 }
CodeMeaning
401Missing or invalid api_key
402Insufficient credits
400Invalid parameter (e.g. bad date format)
503Upstream data source unavailable

GET /matches

Returns tennis matches for a given date. Optionally translate player and tournament names.

GET /api/v1/matches 1 credit

Parameters

ParameterRequiredDefaultDescription
api_keyYesYour API key
dateNoTodayMatch date in YYYY-MM-DD format
langNoenResponse language: en tr de ru

Example requests

# Today's matches (English) https://live-tennis-api.com/api/v1/matches?api_key=YOUR_KEY # Specific date https://live-tennis-api.com/api/v1/matches?api_key=YOUR_KEY&date=2026-07-13 # Turkish translation https://live-tennis-api.com/api/v1/matches?api_key=YOUR_KEY&lang=tr # Russian + specific date https://live-tennis-api.com/api/v1/matches?api_key=YOUR_KEY&date=2026-07-13&lang=ru
curl "https://live-tennis-api.com/api/v1/matches?api_key=YOUR_KEY&date=2026-06-29&lang=en"
const res = await fetch( 'https://live-tennis-api.com/api/v1/matches' + '?api_key=YOUR_KEY&date=2026-06-29&lang=en' ); const data = await res.json(); console.log(data.matches);
<?php $url = 'https://live-tennis-api.com/api/v1/matches' . '?api_key=YOUR_KEY&date=2026-06-29&lang=en'; $data = json_decode(file_get_contents($url), true); var_dump($data['matches']);
import requests data = requests.get( 'https://live-tennis-api.com/api/v1/matches', params={ 'api_key': 'YOUR_KEY', 'date': '2026-06-29', 'lang': 'en', } ).json() print(data['matches'])

Response

{
  "status": "ok",
  "date":   "2026-07-13",
  "count":  87,
  "matches": [
    {
      "id":              16495405,
      "start_timestamp": 1783950600,
      "status":          "finished",
      "player1": {
        "id":      244420,
        "name":    "Jesper De Jong",
        "country": "NL",
        "ranking": 73
      },
      "player2": {
        "id":      322022,
        "name":    "Vilius Gaubas",
        "country": "LT",
        "ranking": 128
      },
      "tournament": {
        "id":      2413,
        "name":    "Bastad",
        "tour":    "ATP",
        "surface": "Red clay"
      },
      "round":  "Round of 32",
      "score": {
        "sets_won": [2, 0],
        "s1":       [7, 6],
        "s1_tb":    [7, 1],
        "s2":       [7, 5]
      },
      "winner": "player1"
    }
  ],
  "credits_remaining": 49
}

Field reference

FieldTypeDescription
idintegerMatch ID
start_timestampintegerScheduled start time as a Unix timestamp (UTC)
statusstringRaw match status โ€” notstarted, inprogress, finished, postponed, cancelled, walkover, retired
player1 / player2objectPlayer id, name, country (ISO-2), ranking (integer|null), photo (proxied image URL)
tournament.idinteger|nullTournament ID
tournament.namestring|nullTournament name
tournament.tourstring|nullTour name โ€” ATP, WTA, ITF, etc.
tournament.surfacestring|nullCourt surface โ€” e.g. Red clay, Grass, Hard
roundstring|nullRound label โ€” e.g. Final, Semifinal, Round of 32
score.sets_wonarray[player1_sets, player2_sets]
score.s1โ€“s5array[player1_games, player2_games] per set; only present if the set was played
score.s1_tbโ€“s5_tbarray[player1_points, player2_points] tiebreak score; only present if a tiebreak was played in that set
winnerstring|null"player1", "player2", or null if the match is not finished
credits_remaininginteger|stringCredits left after this call. "unlimited" for subscription users

GET /match/scores

Returns the current score, set breakdown and live game score for a specific match. Cache is 30 seconds for live matches.

GET /api/v1/match/scores 1 credit

Parameters

ParameterRequiredDefaultDescription
api_keyYesYour API key
match_idYesMatch ID from GET /matches (id field)
langNoenResponse language: en tr de ru

Example requests

https://live-tennis-api.com/api/v1/match/scores?api_key=YOUR_KEY&match_id=16498578
curl "https://live-tennis-api.com/api/v1/match/scores?api_key=YOUR_KEY&match_id=16498578"
const res = await fetch( 'https://live-tennis-api.com/api/v1/match/scores' + '?api_key=YOUR_KEY&match_id=16498578' ); const data = await res.json(); console.log(data.score);
<?php $url = 'https://live-tennis-api.com/api/v1/match/scores' . '?api_key=YOUR_KEY&match_id=16498578'; $data = json_decode(file_get_contents($url), true); var_dump($data['score']);
import requests data = requests.get( 'https://live-tennis-api.com/api/v1/match/scores', params={'api_key': 'YOUR_KEY', 'match_id': 16498578} ).json() print(data['score'])

Response

{
  "status":      "ok",
  "match_id":    16498578,
  "tracker_url": "https://live-tennis-api.com/api/v1/match/tracker?match_id=16498578&lang=en",
  "match": {
    "status":          "inprogress",
    "status_detail":   "3rd set",
    "start_timestamp": 1783967700,
    "player1": { "id": 217464, "name": "Rio Noguchi",    "country": "JP", "ranking": 222, "photo": "https://live-tennis-api.com/image/players/217464.png" },
    "player2": { "id": 202181, "name": "Charles Broom", "country": "GB", "ranking": 283, "photo": "https://live-tennis-api.com/image/players/202181.png" },
    "tournament": { "id": 23140, "name": "ATP Challenger Lincoln", "tour": "Challenger", "surface": "Hardcourt outdoor" },
    "round": "Round of 32"
  },
  "score": {
    "sets_won":    [1, 1],
    "s1":          [6, 2],
    "s2":          [4, 6],
    "s3":          [6, 5],
    "current_game": { "player1": "A", "player2": "40" },
    "serving":     "player1"
  },
  "winner":            null,
  "credits_remaining": 49
}

Field reference

FieldTypeDescription
match_idintegerThe requested match ID
tracker_urlstringReady-to-embed URL for GET /match/tracker โ€” no API key required, safe to use as iframe src
match.statusstringnotstarted, inprogress, finished, postponed, cancelled
match.status_detailstring|nullHuman-readable detail โ€” e.g. "3rd set", "Ended"
match.start_timestampintegerMatch start as a Unix timestamp (UTC)
match.player1 / player2objectid, name, country (ISO-2), ranking (integer|null), photo (proxied image URL)
match.tournamentobjectid, name, tour, surface
match.roundstring|nullRound label โ€” e.g. "Final", "Round of 32"
score.sets_wonarray[player1_sets, player2_sets]
score.s1โ€“s5array[player1_games, player2_games] per set; only present if the set was played
score.s1_tbโ€“s5_tbarrayTiebreak score for that set; only present if a tiebreak was played
score.current_gameobject|โ€”Live game score {player1, player2} โ€” values: "0" "15" "30" "40" "A". Only present when status is inprogress
score.servingstring|โ€”"player1" or "player2" โ€” who is currently serving. Only present when status is inprogress
winnerstring|null"player1", "player2", or null
credits_remaininginteger|stringCredits left after this call. "unlimited" for subscription users

GET /match/statistics

Returns full match statistics broken down by period (ALL + each set). Includes service, return, points, games and miscellaneous stats for both players.

GET /api/v1/match/statistics 1 credit

Parameters

ParameterRequiredDefaultDescription
api_keyYesYour API key
match_idYesMatch ID from GET /matches
langNoenResponse language: en tr de ru

Example requests

https://live-tennis-api.com/api/v1/match/statistics?api_key=YOUR_KEY&match_id=16498578
curl "https://live-tennis-api.com/api/v1/match/statistics?api_key=YOUR_KEY&match_id=16498578"
const res = await fetch( 'https://live-tennis-api.com/api/v1/match/statistics' + '?api_key=YOUR_KEY&match_id=16498578' ); const data = await res.json(); console.log(data.statistics);
<?php $url = 'https://live-tennis-api.com/api/v1/match/statistics' . '?api_key=YOUR_KEY&match_id=16498578'; $data = json_decode(file_get_contents($url), true); var_dump($data['statistics']);
import requests data = requests.get( 'https://live-tennis-api.com/api/v1/match/statistics', params={'api_key': 'YOUR_KEY', 'match_id': 16498578} ).json() print(data['statistics'])

Response

{
  "status":   "ok",
  "match_id": 16498578,
  "statistics": [
    {
      "period": "ALL",
      "groups": [
        {
          "name": "Service",
          "stats": [
            { "key": "aces",                "label": "Aces",               "player1": "0",              "player2": "2",              "player1_value": 0,   "player2_value": 2   },
            { "key": "double_faults",       "label": "Double faults",      "player1": "5",              "player2": "4",              "player1_value": 5,   "player2_value": 4   },
            { "key": "first_serve_accuracy", "label": "First serve",        "player1": "66/105 (63%)", "player2": "47/86 (55%)", "player1_value": 66,  "player2_value": 47  },
            { "key": "break_points_saved",   "label": "Break points saved", "player1": "3/7 (42%)",    "player2": "3/9 (33%)",    "player1_value": 3,   "player2_value": 3   }
          ]
        },
        {
          "name": "Points",
          "stats": [
            { "key": "points_total",           "label": "Total",                   "player1": "103", "player2": "87", "player1_value": 103, "player2_value": 87 },
            { "key": "service_points_scored",  "label": "Service points won",  "player1": "63",  "player2": "46", "player1_value": 63,  "player2_value": 46 },
            { "key": "receiver_points_scored", "label": "Receiver points won", "player1": "40",  "player2": "42", "player1_value": 40,  "player2_value": 42 }
          ]
        }
      ]
    },
    { "period": "1ST", "groups": [ /* same structure */ ] },
    { "period": "2ND", "groups": [ /* same structure */ ] },
    { "period": "3RD", "groups": [ /* same structure */ ] }
  ],
  "credits_remaining": 49
}

Field reference

FieldTypeDescription
statisticsarrayOne entry per period โ€” ALL (full match) + one per set played (1ST, 2ND, 3RDโ€ฆ)
periodstring"ALL", "1ST", "2ND", "3RD", "4TH", "5TH"
groups[].namestringStat group โ€” Service, Points, Games, Return, Miscellaneous
stats[].keystringSnake_case stat key โ€” e.g. aces, first_serve_accuracy, break_points_saved
stats[].labelstringHuman-readable stat name in English
stats[].player1 / player2stringDisplay value โ€” plain number or fraction with % (e.g. "66/105 (63%)")
stats[].player1_value / player2_valueintegerRaw numeric value (numerator for fraction stats)
credits_remaininginteger|stringCredits left after this call. "unlimited" for subscription users

GET /match/h2h

Returns head-to-head summary, all historical meetings between the two players, and the last 5 matches for each player (recent form).

GET /api/v1/match/h2h 1 credit

Parameters

ParameterRequiredDefaultDescription
api_keyYesYour API key
match_idYesMatch ID from GET /matches
langNoenResponse language: en tr de ru

Example requests

https://live-tennis-api.com/api/v1/match/h2h?api_key=YOUR_KEY&match_id=16498578
curl "https://live-tennis-api.com/api/v1/match/h2h?api_key=YOUR_KEY&match_id=16498578"
const res = await fetch( 'https://live-tennis-api.com/api/v1/match/h2h' + '?api_key=YOUR_KEY&match_id=16498578' ); const data = await res.json(); console.log(data.meetings, data.recent_form);
<?php $url = 'https://live-tennis-api.com/api/v1/match/h2h' . '?api_key=YOUR_KEY&match_id=16498578'; $data = json_decode(file_get_contents($url), true); var_dump($data['summary']);
import requests data = requests.get( 'https://live-tennis-api.com/api/v1/match/h2h', params={'api_key': 'YOUR_KEY', 'match_id': 16498578} ).json() print(data['meetings'])

Response

{
  "status":   "ok",
  "match_id": 16498578,
  "player1": { "id": 217464, "name": "Rio Noguchi",    "country": "JP", "ranking": 222, "photo": "https://live-tennis-api.com/image/players/217464.png" },
  "player2": { "id": 202181, "name": "Charles Broom", "country": "GB", "ranking": 283, "photo": "https://live-tennis-api.com/image/players/202181.png" },
  "summary": {
    "player1_wins": 1,
    "player2_wins": 0,
    "total":        1
  },
  "meetings": [
    {
      "id":              16498578,
      "start_timestamp": 1783967700,
      "status":          "finished",
      "player1": { "id": 217464, "name": "Rio Noguchi",    "country": "JP", "ranking": 222, "photo": "..." },
      "player2": { "id": 202181, "name": "Charles Broom", "country": "GB", "ranking": 283, "photo": "..." },
      "tournament": { "id": 23140, "name": "ATP Challenger Lincoln", "tour": "Challenger", "surface": "Hardcourt outdoor" },
      "round": "Round of 32",
      "score": { "sets_won": [2, 1], "s1": [6, 2], "s2": [4, 6], "s3": [7, 5] },
      "winner": "player1",
      "result": "win"
    }
  ],
  "recent_form": {
    "player1": [ /* last 5 matches for Rio Noguchi, same match object structure */ ],
    "player2": [ /* last 5 matches for Charles Broom, same match object structure */ ]
  },
  "credits_remaining": 49
}

Field reference

FieldTypeDescription
player1 / player2objectPlayers of the requested match โ€” id, name, country, ranking, photo
summary.player1_winsintegerHead-to-head wins for player1 in all-time meetings
summary.player2_winsintegerHead-to-head wins for player2 in all-time meetings
summary.totalintegerTotal number of H2H meetings ever played
meetings[]arrayHistorical meetings between the two players, most recent first
meetings[].winnerstring|null"player1" or "player2" โ€” refers to the home/away of that specific match
meetings[].resultstring|null"win" or "loss" โ€” from player1's perspective (the home team of the current requested match)
recent_form.player1[]arrayLast 5 matches for player1, excluding the current match. Same object structure as meetings[]
recent_form.player2[]arrayLast 5 matches for player2, excluding the current match
score.sets_wonarray[player1_sets, player2_sets]
score.s1โ€“s5array[player1_games, player2_games] per set
score.s1_tbโ€“s5_tbarrayTiebreak score; only present if a tiebreak was played
credits_remaininginteger|stringCredits left after this call. "unlimited" for subscription users

GET /match/tracker

Returns a live match tracker as a fully rendered HTML page. No API key required โ€” get the tracker_url from GET /match/scores and embed it directly in an iframe. Your API key is never exposed to end users.

GET /api/v1/match/tracker Free โ€” no API key required
Tip: Call GET /match/scores server-side to get the tracker_url, then set it as the src of an iframe. This way your API key stays on your server and is never visible in page source.

Parameters

ParameterRequiredDefaultDescription
match_idYesMatch ID from /matches
langNoenResponse language: en tr de ru

Example requests

# Step 1: call match/scores with your API key to get tracker_url https://live-tennis-api.com/api/v1/match/scores?api_key=YOUR_KEY&match_id=16498578 # Step 2: embed the tracker_url from the response โ€” no API key exposed <iframe src="https://live-tennis-api.com/api/v1/match/tracker?match_id=16498578" width="800" height="600"></iframe> # Direct access also works https://live-tennis-api.com/api/v1/match/tracker?match_id=16498578&lang=de
curl "https://live-tennis-api.com/api/v1/match/tracker?match_id=16498578&lang=en" \ --output tracker.html
// Step 1: fetch scores (server-side) โ€” get tracker_url const scores = await fetch('/api/scores?match_id=16498578').then(r => r.json()); // Step 2: embed tracker_url โ€” API key never touches the browser const iframe = document.createElement('iframe'); iframe.src = scores.tracker_url; iframe.width = '800'; iframe.height = '600'; document.body.appendChild(iframe);
<?php $url = 'https://live-tennis-api.com/api/v1/match/tracker' . '?api_key=YOUR_KEY&match_id=16498578&lang=en'; $html = file_get_contents($url); echo $html;
import requests resp = requests.get( 'https://live-tennis-api.com/api/v1/match/tracker', params={ 'api_key': 'YOUR_KEY', 'match_id': 16498578, 'lang': 'en', } ) with open('tracker.html', 'w') as f: f.write(resp.text)

Response

This endpoint returns text/html โ€” a full HTML page ready to embed in an iframe. It is not a JSON response.

GET /rankings

Returns ATP, WTA, ATP Live or WTA Live player rankings. The full list of up to 500 players is cached and sliced server-side โ€” use from and limit to paginate without extra API credits.

GET /api/v1/rankings 1 credit

Parameters

ParameterRequiredDefaultDescription
api_keyYesYour API key
typeNoatpRanking list: atp, wta, atp_live, or wta_live
fromNo1Starting rank position (1-based). E.g. 51 to begin at rank 51.
limitNo100Number of players to return (default 100, max 500).

Example requests

# ATP top 100 https://live-tennis-api.com/api/v1/rankings?api_key=YOUR_KEY&type=atp&limit=100 # WTA positions 51โ€“100 https://live-tennis-api.com/api/v1/rankings?api_key=YOUR_KEY&type=wta&from=51&limit=50
curl "https://live-tennis-api.com/api/v1/rankings?api_key=YOUR_KEY&type=atp&limit=100"
const res = await fetch( 'https://live-tennis-api.com/api/v1/rankings?api_key=YOUR_KEY&type=atp&limit=100' ); const data = await res.json(); console.log(data.players);
<?php $data = json_decode(file_get_contents( 'https://live-tennis-api.com/api/v1/rankings?api_key=YOUR_KEY&type=atp&limit=100' ), true); var_dump($data['players']);
import requests data = requests.get( 'https://live-tennis-api.com/api/v1/rankings', params={'api_key': 'YOUR_KEY', 'type': 'atp', 'limit': 100} ).json()

Response

{
  "ranking_type": {
    "name":       "ATP Rankings",
    "type":       "atp",
    "gender":     "M",
    "updated_at": 1783990841,
    "total":      500
  },
  "from":     1,
  "limit":    100,
  "returned": 100,
  "has_more": true,
  "players": [
    {
      "position":           1,
      "position_change":    0,
      "previous_position":  1,
      "best_position":      1,
      "player_id":         206570,
      "name":              "Jannik Sinner",
      "short_name":        "J. Sinner",
      "country":           "IT",
      "country_name":      "Italy",
      "points":            13450,
      "previous_points":   13450,
      "tournaments_played":19,
      "photo":             "https://live-tennis-api.com/image/players/206570.png"
    },
    /* ... 99 more players */
  ],
  "credits_remaining": 49
}

Field reference

FieldTypeDescription
ranking_type.totalintegerTotal players in the full ranking (always 500)
ranking_type.updated_atintegerUnix timestamp of the last rankings update
fromintegerStarting position of this response slice
has_morebooleantrue if more players exist beyond this slice โ€” increment from by limit to paginate
position_changeintegerPositions gained (positive) or lost (negative) since the previous update
best_positionintegerCareer best ranking position
previous_pointsintegerPoints from the previous ranking update
tournaments_playedintegerTournaments counted toward the current ranking
credits_remaininginteger|stringdocs_f_credits_remaining

GET /tournament/details

Returns core tournament metadata for a given season โ€” name, surface, country, city, prize money, draw size, and dates. Also includes a full seasons array for year navigation and a raw info_boxes array with any extra fields SofaScore provides.

GET /api/v1/tournament/details 1 credit

Parameters

ParameterRequiredDefaultDescription
api_keyYesYour API key
tournament_idYesTournament ID from the SofaScore tournament URL (e.g. 23140 for ATP Challenger Lincoln)
season_idNolatestSeason ID โ€” use seasons[].id from GET /tournament/results for a specific year. Defaults to the latest season.
langNoenResponse language: en tr de ru

Example requests

https://live-tennis-api.com/api/v1/tournament/details?api_key=YOUR_KEY&tournament_id=2413
curl "https://live-tennis-api.com/api/v1/tournament/details?api_key=YOUR_KEY&tournament_id=2413"
const res = await fetch( 'https://live-tennis-api.com/api/v1/tournament/details?api_key=YOUR_KEY&tournament_id=2413' ); const data = await res.json(); console.log(data.name, data.surface, data.prize_money);
<?php $data = json_decode(file_get_contents( 'https://live-tennis-api.com/api/v1/tournament/details?api_key=YOUR_KEY&tournament_id=2413' ), true); echo $data['name'] . ' โ€” ' . $data['surface'];
import requests data = requests.get( 'https://live-tennis-api.com/api/v1/tournament/details', params={'api_key': 'YOUR_KEY', 'tournament_id': 2413} ).json()

Response

{
  "status":          "ok",
  "tournament_id":   2413,
  "season_id":       81901,
  "name":            "Bastad",
  "slug":            "bastad",
  "category":        "ATP",
  "gender":          "M",
  "surface":         "Red clay",
  "atp_points":      250,
  "number_of_sets":  3,
  "start_date":      "2026-07-07",
  "end_date":        "2026-07-13",
  "title_holder": {
    "id":      308084,
    "name":    "Luciano Darderi",
    "ranking": 18,
    "country": "IT",
    "photo":   "https://live-tennis-api.com/image/players/308084.png"
  },
  "logo":            "https://live-tennis-api.com/image/tournament/2413.png",
  "primary_color":   "#034ea2",
  "secondary_color": "#ffe37e",
  "seasons": [
    { "id": 81901, "year": "2026", "name": "ATP Bastad, Sweden Men Singles 2026" },
    { "id": 67378, "year": "2025", "name": "ATP Bastad, Sweden Men Singles 2025" }
  ],
  "credits_remaining": 49
}

Field reference

FieldTypeDescription
namestringOfficial tournament name
slugstringURL-friendly tournament identifier
categorystringTour category (ATP, WTA, Challenger, etc.)
genderstringM (men) or F (women)
surfacestringCourt surface, translated if lang is set
atp_pointsinteger|nullRanking points awarded for winning this tournament
number_of_setsinteger|nullSets per match (typically 3 for ATP/WTA, 5 for Grand Slams)
start_date / end_datestring|nullTournament dates in YYYY-MM-DD format
title_holderobject|nullDefending champion โ€” id, name, ranking, country, photo
logostringTournament logo image URL
primary_color / secondary_colorstringTournament brand colors as hex values
seasonsarrayAll available seasons โ€” id, year, name
credits_remaininginteger|stringdocs_f_credits_remaining

GET /tournament/bracket

Returns the full knockout draw for a tournament season โ€” main draw and qualifying bracket. Each round lists all matches with players, rankings, and the winner flag. Use season_id from GET /tournament/results for year selection.

GET /api/v1/tournament/bracket 1 credit

Parameters

ParameterRequiredDefaultDescription
api_keyYesYour API key
tournament_idYesTournament ID from the SofaScore tournament URL (e.g. 23140 for ATP Challenger Lincoln)
season_idNolatestSeason ID โ€” use seasons[].id from GET /tournament/results for a specific year. Defaults to the latest season.

Example requests

# Latest season (auto-detected) https://live-tennis-api.com/api/v1/tournament/bracket?api_key=YOUR_KEY&tournament_id=23140 # Specific season https://live-tennis-api.com/api/v1/tournament/bracket?api_key=YOUR_KEY&tournament_id=23140&season_id=89928
curl "https://live-tennis-api.com/api/v1/tournament/bracket?api_key=YOUR_KEY&tournament_id=23140&season_id=89928"
const res = await fetch( 'https://live-tennis-api.com/api/v1/tournament/bracket' + '?api_key=YOUR_KEY&tournament_id=23140&season_id=89928' ); const data = await res.json(); console.log(data.draws);
<?php $url = 'https://live-tennis-api.com/api/v1/tournament/bracket' . '?api_key=YOUR_KEY&tournament_id=23140&season_id=89928'; $data = json_decode(file_get_contents($url), true); var_dump($data['draws']);
import requests data = requests.get( 'https://live-tennis-api.com/api/v1/tournament/bracket', params={'api_key': 'YOUR_KEY', 'tournament_id': 23140, 'season_id': 89928} ).json() print(data['draws'])

Response

{
  "status":        "ok",
  "tournament_id": 23140,
  "season_id":     89928,
  "seasons": [
    { "id": 89928, "year": "2026", "name": "ATP Challenger Lincoln 2026" },
    { "id": 80594, "year": "2025", "name": "ATP Challenger Lincoln 2025" }
  ],
  "draws": [
    {
      "name":   "2026 Lincoln, USA",
      "type":   "main",
      "rounds": [
        {
          "name":    "Round of 32",
          "order":   1,
          "matches": [
            {
              "block_id": 2612048,
              "event_id": 16514881,
              "finished": false,
              "player1":  { "id": 489715, "name": "Matthew Forbes",   "ranking": 939, "photo": "https://live-tennis-api.com/image/players/489715.png", "winner": false },
              "player2":  { "id": 408344, "name": "Sebastian Gorzny", "ranking": 966, "photo": "https://live-tennis-api.com/image/players/408344.png", "winner": false }
            }
          ]
        },
        { "name": "Round of 16", "order": 2, "matches": [/* 8 matches */] },
        { "name": "Quarterfinal",  "order": 3, "matches": [/* 4 matches */] },
        { "name": "Semifinal",     "order": 4, "matches": [/* 2 matches */] },
        { "name": "Final",         "order": 5, "matches": [/* 1 match  */] }
      ]
    },
    { "name": "2026 Lincoln, USA, Qualifying", "type": "qualifying", "rounds": [/* same structure */] }
  ],
  "credits_remaining": 49
}

Field reference

FieldTypeDescription
seasonsarrayAll available seasons โ€” id, year, name. Use season_id param to switch year.
draws[].typestring"main" or "qualifying"
draws[].rounds[].namestringRound label โ€” e.g. "Round of 32", "Quarterfinal", "Final"
draws[].rounds[].orderintegerRound order starting from 1 (1 = earliest round)
matches[].event_idinteger|nullMatch ID โ€” use with GET /match/scores; null if not yet scheduled
matches[].finishedbooleanWhether the match has been played
matches[].player1 / player2object|nullid, name, ranking, photo, winner (boolean); null if slot is empty (BYE or not yet filled)
credits_remaininginteger|stringCredits left after this call

GET /tournament/results

Returns completed and upcoming matches for a tournament season, plus a full list of all available seasons for year selection. Includes player seeding when available.

GET /api/v1/tournament/results 1 credit

Parameters

ParameterRequiredDefaultDescription
api_keyYesYour API key
tournament_idYesTournament ID from the SofaScore tournament URL (e.g. 23140 for ATP Challenger Lincoln)
season_idNolatestSeason ID โ€” use seasons[].id from GET /tournament/results for a specific year. Defaults to the latest season.
langNoenResponse language: en tr de ru

Example requests

# Current season results (auto-detected) https://live-tennis-api.com/api/v1/tournament/results?api_key=YOUR_KEY&tournament_id=23140 # Specific season + language https://live-tennis-api.com/api/v1/tournament/results?api_key=YOUR_KEY&tournament_id=23140&season_id=89928&lang=tr
curl "https://live-tennis-api.com/api/v1/tournament/results?api_key=YOUR_KEY&tournament_id=23140&season_id=89928&lang=en"
const res = await fetch( 'https://live-tennis-api.com/api/v1/tournament/results' + '?api_key=YOUR_KEY&tournament_id=23140&season_id=89928&lang=en' ); const data = await res.json(); console.log(data.seasons, data.past_matches, data.upcoming_matches);
<?php $url = 'https://live-tennis-api.com/api/v1/tournament/results' . '?api_key=YOUR_KEY&tournament_id=23140&season_id=89928&lang=en'; $data = json_decode(file_get_contents($url), true); var_dump($data['past_matches']);
import requests data = requests.get( 'https://live-tennis-api.com/api/v1/tournament/results', params={'api_key': 'YOUR_KEY', 'tournament_id': 23140, 'season_id': 89928, 'lang': 'en'} ).json() print(data['seasons'])

Response

{
  "status":        "ok",
  "tournament_id": 23140,
  "season_id":     89928,
  "seasons": [
    { "id": 89928, "year": "2026", "name": "ATP Challenger Lincoln (NE), USA Men Singles 2026" },
    { "id": 80594, "year": "2025", "name": "ATP Challenger Lincoln (NE), USA Men Singles 2025" },
    { "id": 64754, "year": "2024", "name": "ATP Challenger Rome, USA Men Singles 2024" }
  ],
  "past_matches": [
    {
      "id":              16498578,
      "start_timestamp": 1783967700,
      "status":          "finished",
      "player1": { "id": 217464, "name": "Rio Noguchi", "country": "JP", "ranking": 222, "seed": null, "photo": "https://live-tennis-api.com/image/players/217464.png" },
      "player2": { "id": 202181, "name": "Charles Broom", "country": "GB", "ranking": 283, "seed": null, "photo": "https://live-tennis-api.com/image/players/202181.png" },
      "round":  "Round of 32",
      "surface": "Hardcourt outdoor",
      "score":  { "sets_won": [2, 1], "s1": [6, 2], "s2": [4, 6], "s3": [7, 5] },
      "winner": "player1"
    }
  ],
  "upcoming_matches": [ /* same structure, score will be empty */ ],
  "credits_remaining": 49
}

Field reference

FieldTypeDescription
seasonsarrayAll available seasons for this tournament โ€” use id as season_id for year selection
seasons[].yearstringYear string โ€” e.g. "2026", "2025"
past_matchesarrayCompleted matches, most recent first
upcoming_matchesarrayScheduled but not yet played matches
player1/2.seedinteger|nullPlayer seeding in this tournament; null for unseeded players
roundstring|nullRound label โ€” e.g. "Round of 32", "Final"
surfacestring|nullCourt surface
credits_remaininginteger|stringCredits left after this call

GET /player/profile

Returns full player bio โ€” ranking, playing hand, height, weight, birthdate, prize money and photo.

GET /api/v1/player/profile 1 credit

Parameters

ParameterRequiredDefaultDescription
api_keyYesYour API key
player_idYesPlayer ID from the SofaScore player URL (e.g. 457262 for Nicolas Arseneault)
langNoenResponse language: en tr de ru

Example requests

https://live-tennis-api.com/api/v1/player/profile?api_key=YOUR_KEY&player_id=457262
curl "https://live-tennis-api.com/api/v1/player/profile?api_key=YOUR_KEY&player_id=457262"
const res = await fetch( 'https://live-tennis-api.com/api/v1/player/profile?api_key=YOUR_KEY&player_id=457262' ); const data = await res.json(); console.log(data.player);
<?php $data = json_decode(file_get_contents( 'https://live-tennis-api.com/api/v1/player/profile?api_key=YOUR_KEY&player_id=457262' ), true);
import requests data = requests.get( 'https://live-tennis-api.com/api/v1/player/profile', params={'api_key': 'YOUR_KEY', 'player_id': 457262} ).json()

Response

{
  "status": "ok",
  "player": {
    "id":             457262,
    "name":           "Nicolas Arseneault",
    "short_name":     "N. Arseneault",
    "country":        { "code": "CA", "name": "Canada" },
    "gender":         "M",
    "ranking":        768,
    "hand":           "left-handed",
    "height":         1.83,
    "weight":         77,
    "birthdate":      "2007-01-18",
    "age":            19,
    "birthplace":     "Richmond Hill, Canada",
    "prize_current":  1505,
    "prize_total":    66695,
    "photo":          "https://live-tennis-api.com/image/players/457262.png"
  },
  "credits_remaining": 49
}

Field reference

FieldTypeDescription
player.rankinginteger|nullCurrent ATP/WTA ranking
player.handstring|nullPlaying hand โ€” translated with lang param
player.heightfloat|nullHeight in metres
player.weightinteger|nullWeight in kilograms
player.prize_currentinteger|nullPrize money earned this season (EUR)
player.prize_totalinteger|nullCareer prize money total (EUR)
credits_remaininginteger|stringdocs_f_credits_remaining

GET /player/matches

Returns a player's recent and upcoming matches across all tournaments, paginated. Filter by type=singles or type=doubles.

GET /api/v1/player/matches 1 credit

Parameters

ParameterRequiredDefaultDescription
api_keyYesYour API key
player_idYesPlayer ID from the SofaScore player URL (e.g. 457262 for Nicolas Arseneault)
pageNo0Page of results โ€” 0 = most recent 30 matches, 1 = previous 30, etc.
typeNoallFilter by match type: all (default), singles, or doubles
langNoenResponse language: en tr de ru

Response

{
  "status":     "ok",
  "player_id":  457262,
  "page":          0,
  "has_next_page": true,
  "next_page":     1,
  "past_matches": [
    {
      "id":              14876875,
      "start_timestamp": 1760379000,
      "type":            "singles",
      "status":          "finished",
      "result":          "W",
      "player":   { "id": 457262, "name": "Nicolas Arseneault", "ranking": 780, "country": "CA", "photo": "https://live-tennis-api.com/image/players/457262.png" },
      "opponent": { "id": 95259,  "name": "Strong Kirchheimer",   "ranking": 660,  "country": "US", "photo": "https://live-tennis-api.com/image/players/95259.png" },
      "tournament": { "id": 23140, "name": "ATP Challenger Lincoln (NE)", "category": "Challenger" },
      "season":     { "id": 80594, "year": "2025" },
      "round":      "Qualification Final",
      "surface":    "Hardcourt outdoor",
      "score": { "sets": [2, 0], "s1": [6, 3], "s2": [7, 5] }
    }
  ],
  "upcoming_matches": [],
  "credits_remaining": 49
}

Field reference

FieldTypeDescription
pageintegerCurrent page โ€” 30 matches per page
has_next_pagebooleantrue if a next page exists; false when you've reached the last page
next_pageinteger|nullPage number to pass for the next request; null on the last page
typestringall (default), singles, or doubles
resultstring|null"W" (win) or "L" (loss); null for upcoming matches
opponent.membersarray|nullDoubles only โ€” lists both players in the opponent pair
surfacestring|nullTranslated with lang param
roundstring|nullTranslated with lang param
upcoming_matchesarrayOnly populated on page 0
credits_remaininginteger|stringdocs_f_credits_remaining

GET /player/statistics

Returns win/loss statistics for a player for a given season year โ€” overall and broken down by surface. Covers up to 60 most recent matches.

GET /api/v1/player/statistics 1 credit

Parameters

ParameterRequiredDefaultDescription
api_keyYesYour API key
player_idYesPlayer ID from the SofaScore player URL (e.g. 457262 for Nicolas Arseneault)
season_yearNocurrent yearYear to compute statistics for (e.g. 2026). Defaults to the current year.
langNoenResponse language: en tr de ru

Response

{
  "status":      "ok",
  "player": { /* same as GET /player/profile */ },
  "season_year": "2026",
  "singles": {
    "played":   18,
    "wins":     11,
    "losses":   7,
    "win_rate": 61.1,
    "by_surface": {
      "hardcourt": { "played": 12, "wins": 8, "losses": 4 },
      "clay":      { "played": 4,  "wins": 2, "losses": 2 },
      "grass":     { "played": 2,  "wins": 1, "losses": 1 }
    }
  },
  "doubles": { "played": 6, "wins": 3, "losses": 3, "win_rate": 50.0 },
  "recent_form": ["W", "W", "L", "W", "L", "W", "W", "W", "L", "W"],
  "credits_remaining": 49
}

Field reference

FieldTypeDescription
season_yearstringThe year the statistics are filtered to (defaults to current year)
singles.by_surfaceobjectW/L breakdown per surface โ€” keys: hardcourt, clay, grass, carpet
recent_formstring[]Last 10 singles match results, newest first โ€” "W" or "L"
credits_remaininginteger|stringdocs_f_credits_remaining

GET /webhook/register

Registers a new webhook endpoint. When a subscribed event fires, your URL receives a POST request with a JSON payload. Up to 10 active webhooks per account. Free โ€” does not consume credits.

GET /api/v1/webhook/register Free โ€” no API key required

Parameters

ParameterRequiredDefaultDescription
api_keyYesYour API key
urlYesA publicly reachable http or https URL that will receive POST requests
eventsNoallComma-separated list of event types to subscribe to (e.g. match.start,score.update). Leave empty to subscribe to all events.
secretNoOptional secret string โ€” when set, each request includes an X-Webhook-Signature: sha256=<hmac> header for verification

Available events

EventDescription
match.startMatch begins (status changed from notstarted to inprogress)
match.finishMatch is complete with final score
match.postponedMatch has been postponed
match.cancelledMatch has been cancelled
score.updateSet score changed, or current game score updated (15 / 30 / 40 / A). Includes current_game field when a point is in progress.
period.startA new set has started
period.endA set has ended

Example requests

https://live-tennis-api.com/api/v1/webhook/register?api_key=YOUR_KEY&url=https://yourapp.com/hook&events=match.start,match.finish,score.update
curl "https://live-tennis-api.com/api/v1/webhook/register?api_key=YOUR_KEY&url=https://yourapp.com/hook&events=match.start,match.finish"
const res = await fetch( 'https://live-tennis-api.com/api/v1/webhook/register?api_key=YOUR_KEY' + '&url=https://yourapp.com/hook&events=match.start,match.finish' ); const data = await res.json(); console.log(data.webhook_id);
<?php $data = json_decode(file_get_contents( 'https://live-tennis-api.com/api/v1/webhook/register?api_key=YOUR_KEY' . '&url=https://yourapp.com/hook&events=match.start,match.finish' ), true); echo $data['webhook_id'];
import requests data = requests.get( 'https://live-tennis-api.com/api/v1/webhook/register', params={'api_key': 'YOUR_KEY', 'url': 'https://yourapp.com/hook', 'events': 'match.start,match.finish'} ).json()

Response

{
  "status":     "ok",
  "webhook_id": 12,
  "url":        "https://yourapp.com/hook",
  "events":     ["match.start", "match.finish", "score.update"],
  "secret":     null,
  "credits_remaining": 49
}

Webhook payload

Your endpoint receives a POST request with Content-Type: application/json and the following body:

Every event follows the same envelope: event, timestamp, a match object (always present), and an event-specific data object. Click an event below to see its example response.

โš ๏ธ Important Security Notice Webhook notifications are sent from our system via the IP address 45.94.4.69. For added security, you can restrict incoming requests to your webhook endpoint on your server (at the firewall level) to only allow this IP address.

GET /webhook/list

Returns all active webhooks registered to your API key. Free โ€” does not consume credits.

GET /api/v1/webhook/list Free โ€” no API key required

Parameters

ParameterRequiredDefaultDescription
api_keyYesYour API key

Example requests

https://live-tennis-api.com/api/v1/webhook/list?api_key=YOUR_KEY
curl "https://live-tennis-api.com/api/v1/webhook/list?api_key=YOUR_KEY"
const data = await (await fetch('https://live-tennis-api.com/api/v1/webhook/list?api_key=YOUR_KEY')).json();
<?php $data = json_decode(file_get_contents('https://live-tennis-api.com/api/v1/webhook/list?api_key=YOUR_KEY'), true);
import requests data = requests.get('https://live-tennis-api.com/api/v1/webhook/list', params={'api_key': 'YOUR_KEY'}).json()

Response

{
  "status":   "ok",
  "count":    2,
  "webhooks": [
    {
      "webhook_id": 12,
      "url":        "https://yourapp.com/hook",
      "secret":     null,
      "events":     ["match.start", "score.update"],
      "created_at": "2026-07-14 10:32:00"
    }
  ],
  "credits_remaining": 49
}

GET /webhook/delete

Deactivates a webhook by its ID. Free โ€” does not consume credits.

GET /api/v1/webhook/delete Free โ€” no API key required

Parameters

ParameterRequiredDefaultDescription
api_keyYesYour API key
webhook_idYesWebhook ID returned by GET /webhook/register or GET /webhook/list

Example requests

https://live-tennis-api.com/api/v1/webhook/delete?api_key=YOUR_KEY&webhook_id=12
curl "https://live-tennis-api.com/api/v1/webhook/delete?api_key=YOUR_KEY&webhook_id=12"
const data = await (await fetch('https://live-tennis-api.com/api/v1/webhook/delete?api_key=YOUR_KEY&webhook_id=12')).json();
<?php $data = json_decode(file_get_contents('https://live-tennis-api.com/api/v1/webhook/delete?api_key=YOUR_KEY&webhook_id=12'), true);
import requests data = requests.get('https://live-tennis-api.com/api/v1/webhook/delete', params={'api_key': 'YOUR_KEY', 'webhook_id': 12}).json()

Response

{
  "status":     "ok",
  "webhook_id": 12,
  "deleted":    true,
  "credits_remaining": 49
}