Live Tennis Scores & Match Tracker Widget | API Guide โ€” Tennis API
Login Get API Key
Tutorials

How to Get Live Tennis Scores and Embed the Match Tracker

Illustration of a live tennis match tracker showing set and point scores

Learn how to fetch live tennis scores โ€” sets, games, and points โ€” and embed the ready-made match tracker widget using Live Tennis API.

On this page

Tennis scoring has more layers than most sports โ€” sets, games within a set, and points within a game, plus tiebreaks that need their own score. This guide covers fetching that full picture with Live Tennis API, and embedding the ready-made match tracker widget so you don't have to build a live scoreboard UI from scratch.

Two Endpoints for Two Different Views

  • /matches โ€” a list of matches for a given day, with set and game scores, good for a schedule/results page
  • /match/scores โ€” the live detail for one specific match, including the current game's point score and who's serving

Fetching Today's Matches

import requests

response = requests.get(
    'https://live-tennis-api.com/api/v1/matches',
    params={'api_key': 'YOUR_KEY', 'date': '2026-07-13', 'lang': 'en'}
).json()

for match in response['matches']:
    p1 = match['player1']['name']
    p2 = match['player2']['name']
    sets = match['score']['sets_won']
    print(f"{p1} {sets[0]}-{sets[1]} {p2} ({match['status']})")

Understanding the Score Structure

Each match's score object breaks down as follows:

  • sets_won โ€” [player1_sets, player2_sets], the overall match tally
  • s1, s2, s3... โ€” games won per set, e.g. [7, 6] for a 7-6 first set
  • s1_tb, s2_tb... โ€” tiebreak point score, only present if that set went to a tiebreak
function formatSetScore(match) {
  const sets = ['s1', 's2', 's3', 's4', 's5'];
  return sets
    .filter(key => match.score[key])
    .map(key => {
      const [g1, g2] = match.score[key];
      const tb = match.score[`${key}_tb`];
      return tb ? `${g1}-${g2}(${tb[0]}-${tb[1]})` : `${g1}-${g2}`;
    })
    .join(', ');
}

// "7-6(7-1), 7-5" for a match with a first-set tiebreak

Fetching Live Detail for One Match

Once a user taps into a specific match, /match/scores gives you the live point-by-point state:

const res = await fetch(
  'https://live-tennis-api.com/api/v1/match/scores' +
  `?api_key=YOUR_KEY&match_id=${matchId}`
);
const data = await res.json();

if (data.match.status === 'inprogress') {
  const { player1, player2 } = data.score.current_game;
  const server = data.score.serving === 'player1' ? data.match.player1.name : data.match.player2.name;
  console.log(`${player1} - ${player2} (serving: ${server})`);
}

current_game and serving are only present while the match is actually in progress โ€” always check match.status before reading them.

Building a Live Score Display

function LiveScoreCard({ data }) {
  const { match, score } = data;
  const isLive = match.status === 'inprogress';

  return (
    <div className="score-card">
      <div className="players">
        <span>{match.player1.name}</span>
        <span>{match.player2.name}</span>
      </div>

      <div className="sets">
        {formatSetScore({ score })}
      </div>

      {isLive && (
        <div className="current-game">
          {score.current_game.player1} - {score.current_game.player2}
          <span className="serving-indicator">
            {score.serving === 'player1' ? 'โ— ' : ''}{match.player1.name}
            {score.serving === 'player2' ? ' โ—' : ''}{match.player2.name}
          </span>
        </div>
      )}
    </div>
  );
}

Embedding the Match Tracker Widget (Without Exposing Your API Key)

Rather than building your own live scoreboard UI, /match/scores returns a ready-to-use tracker_url pointing to a fully rendered HTML widget โ€” and critically, that URL requires no API key to load, so it's safe to embed directly in a public page.

The correct pattern is a two-step fetch: get tracker_url server-side (where your key is safe), then use that URL client-side:

# Server-side: fetch with your API key, extract the public tracker_url
import requests

response = requests.get(
    'https://live-tennis-api.com/api/v1/match/scores',
    params={'api_key': 'YOUR_KEY', 'match_id': match_id}
).json()

tracker_url = response['tracker_url']
# Pass tracker_url to your frontend template โ€” it needs no key
<!-- Client-side: embed the key-free tracker_url directly -->
<iframe
  src="{{ tracker_url }}"
  width="800"
  height="600"
  frameborder="0"
></iframe>

This matters because embedding a URL that includes api_key=YOUR_KEY directly in client-side HTML would expose your key in the page source โ€” the tracker_url field exists specifically to avoid that.

Frequently Asked Questions

Does current_game show numeric points like 15 or 30?

Yes, values are "0", "15", "30", "40", or "A" (advantage) as strings, matching standard tennis scoring โ€” not raw point counts.

Can I call /match/tracker directly instead of going through /match/scores first?

Yes, /match/tracker works with just a match_id and no API key at all โ€” but fetching tracker_url from /match/scores first is the documented, recommended pattern since it keeps your key handling server-side by default.

How often should I poll /match/scores for a live match?

The endpoint's cache is 30 seconds for live matches, so polling more frequently than that won't get you fresher data โ€” 30 seconds is a sensible polling interval to match.

What does status_detail like "3rd set" tell me that status doesn't?

The status field is a fixed enum (inprogress, finished, etc.), while status_detail gives a human-readable phase within that status โ€” useful for displaying directly in a UI without building your own "which set are we in" logic.

Share this article:
โ† Back to blog