Skip to content

Clips ​

GET /clip

Generates a clip and returns a URL that you can stream in a video player or download. Clips are generated on demand, fast enough to request one when a user presses play.

Clips are cached. If a clip with the same parameters already exists, its URL is returned without generating it again.

Do not store clip URLs

Clip URLs are not permanent, because generated clips are deleted from time to time. Always request clips through this endpoint each time and use the returned URL instead of saving a prior URL. A clip that still exists is returned from the cache, so this shouldn't create a problem but will ensure the URL does not disappear on you.

Parameters ​

ParameterRequiredTypeDescription
leagueYesstringNBA, GLEAGUE, or NBASL
game_id_nbaOne of these twostringNBA game ID, as in the data feed
game_idOne of these twoUUIDVideo game ID from /available_game_periods. See Game IDs
periodYesintegerPeriod number
start_game_clockOne of these twonumberSeconds left in the period, such as 581 for 9:41
start_timestampOne of these twointegerWall clock time in epoch milliseconds
start_paddingNointegerSeconds of video before the start time. Default 0
end_paddingNointegerSeconds of video after the start time. Default 5
audioNotrue or falseInclude broadcast audio. Default false
regenerateNotrue or falseSkip the cache and generate the clip again. Default false

A clip can be up to 5 minutes long: start_padding plus end_padding cannot exceed 300.

A padding of 10 seconds before and 5 seconds after usually shows what leads up to an event.

Game Clock or Timestamp ​

Check wall_clock_timing_available in the period's video_metrics from /available_game_periods:

  • true: use either. A timestamp points to one exact moment, so it is the better choice when you have one, such as a *_wall_clock value from the data feed. This covers nearly all NBA games, and G League and Summer League games for which we have tracking data.
  • false: use start_game_clock. A request with only start_timestamp returns a 400 error.

NOTE

When using the start_game_clock method, remember that a game clock can repeat during a stoppage. Suppose there is a foul at 9:41, then an inbound with 9:41 still on the clock. When wall_clock_timing_available is true, passing 581 starts at the first moment 9:41 appears and shows the foul. To show the inbound, pass 580 with a larger start_padding. When it is false, a repeated clock time can start the clip at the end of the stoppage instead, showing the inbound. To show the foul, pass 582 with a smaller start_padding.

Response ​

FieldTypeDescription
resultstringSuccess, Imputed, or an error message
clip_urlstring or nullURL of the MP4 clip
closest_prev_timenumberOnly when result is Imputed (see below)
closest_next_timenumberOnly when result is Imputed (see below)

Example ​

bash
curl --request GET \
  --url 'https://api.cleaningtheglass.com/video/clip?league=NBA&game_id=00124539-08e3-4174-bd7f-db5e96a904fb&period=1&start_game_clock=581&start_padding=10&end_padding=5' \
  --header 'X-API-KEY: YOUR_VIDEO_API_KEY'
json
{
  "result": "Success",
  "clip_url": "https://..."
}

Imputed Clips ​

Older G League and Summer League video that does not have tracking data may not have a match for every game clock time. In that case the clip is estimated from the nearest matched times before and after the requested time, and result is Imputed:

json
{
  "result": "Imputed",
  "clip_url": "https://...",
  "closest_prev_time": 573.0,
  "closest_next_time": 568.0
}

The estimate is accurate while the clock is running. If the clock was stopped, the clip may not show what you expect. You can request one of the two closest times instead.

Building Your Own Player ​

This page plays a clip in a standard HTML video element. Save it as an HTML file, replace YOUR_VIDEO_API_KEY, open it in a browser, and press Play Clip.

Keep your key on the server

This example calls the API from the browser, which exposes your key to anyone who can open the page. In production, call /clip from your server and pass only clip_url to the browser.

html
<!DOCTYPE html>
<html lang="en">
  <body>
    <video id="video-player" controls width="1280"></video>

    <button
      id="play-clip"
      data-league="NBA"
      data-game="00124539-08e3-4174-bd7f-db5e96a904fb"
      data-period="1"
      data-start="581"
      data-start-padding="10"
      data-end-padding="5"
    >
      Play Clip
    </button>

    <script>
      const API_KEY = "YOUR_VIDEO_API_KEY";

      document.getElementById("play-clip").addEventListener("click", async (event) => {
        const clip = event.target.dataset;
        const params = new URLSearchParams({
          league: clip.league,
          game_id: clip.game,
          period: clip.period,
          start_game_clock: clip.start,
          start_padding: clip.startPadding,
          end_padding: clip.endPadding,
        });

        const response = await fetch(
          "https://api.cleaningtheglass.com/video/clip?" + params,
          { headers: { "X-API-KEY": API_KEY } }
        );
        const data = await response.json();

        if (data.result === "Success" || data.result === "Imputed") {
          const player = document.getElementById("video-player");
          player.src = data.clip_url;
          player.play();
        } else {
          console.error("Could not get the clip:", data.result);
        }
      });
    </script>
  </body>
</html>

Change data-start, data-start-padding, and data-end-padding to see how the clip changes.

If you would rather not build a player, create a playlist and open it in the CTG video player.