Skip to content

Playlists ​

A playlist is an ordered list of clips. Once you create one, you can:

There are two ways to create a playlist:

MethodUse it when
POST /create_playlist/ctg_idsYou have marking IDs from the data feed
POST /create_playlist/csvYou have games and times from another source

From CTG Marking IDs ​

POST /create_playlist/ctg_ids

Creates one clip for each data feed marking ID. Each clip starts a few seconds before the marking. For markings with a duration, such as chances, the clip runs through the end of the marking. For moments, such as shots, it runs a few seconds past it.

Clips are in the order you send the IDs. You can mix marking types in one request.

Supported IDs ​

ID prefixExample data feed fields
chance_chance_id_ctg
possession_possession_id_ctg
shot_shot_id_ctg, linked_shot_id_ctg
pass_pass_id_ctg
possession_touch_possession_touch_id_ctg
dribble_dribble_id_ctg
drive_drive_id_ctg
cut_cut_id_ctg
closeout_closeout_id_ctg
ball_screen_ball_screen_id_ctg
off_ball_screen_off_ball_screen_id_ctg
1v1_advantage_creation_1v1_id_ctg
rebound_rebound_id_ctg
turnover_turnover_id_ctg
foul_foul_id_ctg, linked_foul_id_ctg
free_throw_free_throw_id_ctg
violation_violation_id_ctg
jump_ball_jump_ball_id_ctg
double_team_double_team_id_ctg
contested_rebound_touch_contested_rebound_touch_id_ctg
deflection_deflection_id_ctg
jump_jump_id_ctg
fall_fall_id_ctg

Challenges and shot contests are not yet supported. To see one of those, use a related ID on the same row, such as the shot or chance ID. Violations without a violation_wall_clock, such as delay of game and lane violations, are returned in skipped_ids.

Request ​

Send JSON with one field, ids_string: the IDs separated by newlines (\n).

bash
curl --request POST \
  --url 'https://api.cleaningtheglass.com/video/create_playlist/ctg_ids' \
  --header 'X-API-KEY: YOUR_VIDEO_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{"ids_string": "shot_8c0ae4ab89d51034a17dcf2cde9a179f\nchance_43b2166610037e91ab6fa6654fcdb0b8\nball_screen_7245fc8f21cbe594c94a4c29875a76fa"}'

Response ​

FieldTypeDescription
resultstringSuccess or an error message
playlist_idUUIDID of the new playlist
num_clipsintegerNumber of clips created
playlist_clipslistThe clips, in the same format as Read a Playlist
skipped_idslistOnly present when some IDs were skipped

An ID is skipped when it has an unsupported prefix, does not exist, or its game or period has no video. The other IDs still become clips.

From a CSV ​

POST /create_playlist/csv

Creates one clip for each row of a CSV with these columns:

game_id,period,start_time,start_padding,end_padding
ColumnDescription
game_idNBA game ID (game_id_nba, such as 0022400121) or video game ID from /available_game_periods. See Game IDs
periodPeriod number
start_timeGame clock as seconds left (581) or mm:ss (9:41), or a timestamp in epoch milliseconds
start_paddingSeconds of video before the start time
end_paddingSeconds of video after the start time

Values of 3600 or less are read as a game clock. Larger values are read as a timestamp. You can mix both in one CSV, and mix NBA game IDs and video game IDs across rows. A header row is optional; its first column can be game_id or game_id_nba.

When the period's wall_clock_timing_available is true in /available_game_periods, game clocks and timestamps are converted to each other, so clips play correctly either way. Otherwise, use game clocks.

Request ​

Send JSON with one field, csv_string: the rows separated by newlines (\n).

bash
curl --request POST \
  --url 'https://api.cleaningtheglass.com/video/create_playlist/csv' \
  --header 'X-API-KEY: YOUR_VIDEO_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{"csv_string": "00124539-08e3-4174-bd7f-db5e96a904fb,1,9:41,10,5\n00124539-08e3-4174-bd7f-db5e96a904fb,1,1730330093050,10,5"}'

Both rows show the same moment, once by game clock and once by timestamp.

Response ​

FieldTypeDescription
resultstringSuccess or an error message
playlist_idUUIDID of the new playlist
num_clipsintegerNumber of clips created
playlist_clipslistThe clips, in the same format as Read a Playlist

Each row's clip_number is its position in the CSV. Rows whose game and period have no video are skipped, which leaves a gap in the clip numbers. If any row is for a league your key cannot access, no playlist is created and the response is 403.

Save to Users While Creating ​

Both create methods can also save the new playlist to your users in the same request. Add these fields to the request body:

FieldTypeDescription
user_emailslistUp to 50 email addresses. Required if you send any of these fields
playlist_namestringThe saved playlist's name, up to 120 characters. Required if you send any of these fields
collaborativebooleanOptional. Defaults to false. See Save a Playlist to Users

The users and the copies work as described in Save a Playlist to Users.

bash
curl --request POST \
  --url 'https://api.cleaningtheglass.com/video/create_playlist/csv' \
  --header 'X-API-KEY: YOUR_VIDEO_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{"csv_string": "00124539-08e3-4174-bd7f-db5e96a904fb,1,9:41,10,5", "playlist_name": "Week 3 turnovers", "user_emails": ["analyst@example.com", "coach@example.com"]}'

Your users are checked before the playlist is created. If any email is not an active CTG Pro user in your organization, or the user cannot receive saved playlists, the response is 400 and no playlist is created. Its results list the emails that failed, in the format below.

When the request succeeds, the response is the usual create response plus a results entry for each email, in the format described under Save a Playlist to Users.

If something unexpected goes wrong while saving, the response is 500. The playlist exists, so the response still has its playlist_id, and each entry in results says which users got a copy. Call /save_playlist_for_users with that playlist_id and only the emails that failed. If the request produces no clips, the playlist is created but not saved, and the response is 400.

Read a Playlist ​

GET /playlist/<playlist_id>

Returns a playlist and its clips.

Response ​

FieldTypeDescription
playlist.idUUIDPlaylist ID
playlist.date_createddateDate the playlist was created
clipslistThe clips, in playlist order
available_3d_game_ids_nbalistNBA game IDs in the playlist that have CTG 3D animations

Each clip has these fields:

FieldTypeDescription
clip_numberintegerClip number within the playlist. Use it for downloads
game_idUUIDVideo game ID
game_id_nbastring or nullNBA game ID
game_infoobjectid, game_id_nba, league, game_date, home_team, away_team
leaguestringNBA, GLEAGUE, or NBASL
periodintegerPeriod number
start_game_clocknumberSeconds left in the period at the start time
start_timestampinteger or nullStart time in epoch milliseconds. null when the video has no timestamps
start_paddingnumberSeconds before the start time
end_paddingnumberSeconds after the start time

Pass a clip's league, game_id (or game_id_nba), period, start time, and padding to /clip to play it in your own player.

Example ​

json
{
  "playlist": {
    "id": "9fbe7116-240d-4715-8019-e1b0fccb644d",
    "date_created": "2026-10-13"
  },
  "clips": [
    {
      "clip_number": 1,
      "game_id": "00124539-08e3-4174-bd7f-db5e96a904fb",
      "game_id_nba": "0022400121",
      "game_info": {
        "away_team": "ATL",
        "game_date": "2024-10-30",
        "game_id_nba": "0022400121",
        "home_team": "WAS",
        "id": "00124539-08e3-4174-bd7f-db5e96a904fb",
        "league": "NBA"
      },
      "league": "NBA",
      "period": 1,
      "start_game_clock": 581,
      "start_timestamp": 1730330093050,
      "start_padding": 10,
      "end_padding": 5
    }
  ],
  "available_3d_game_ids_nba": ["0022400121"]
}

Save a Playlist to Users ​

POST /save_playlist_for_users

Saves a copy of a playlist to the saved playlists of each user you list, as if they had opened it in the CTG video player and saved it themselves. Each user gets their own copy, so changes one user makes do not affect anyone else's. Users appear with the playlist the next time they open CTG Pro.

You can only save to active CTG Pro users in your organization. Your key also needs access to every league in the playlist.

Request ​

Send JSON with these fields:

FieldTypeDescription
playlist_idUUIDRequired. The ID of a playlist you created or read
playlist_namestringRequired. The saved playlist's name, up to 120 characters. If a user already has a playlist with this name, their copy gets a number, such as Week 3 turnovers (1)
user_emailslistRequired. Up to 50 email addresses
collaborativebooleanWhether other users in your organization who open a copy can edit it. Defaults to false, so they can open it but not edit it
bash
curl --request POST \
  --url 'https://api.cleaningtheglass.com/video/save_playlist_for_users' \
  --header 'X-API-KEY: YOUR_VIDEO_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{"playlist_id": "9fbe7116-240d-4715-8019-e1b0fccb644d", "playlist_name": "Week 3 turnovers", "user_emails": ["analyst@example.com", "coach@example.com"]}'

Response ​

FieldTypeDescription
resultstringSuccess when every user got a copy, Partial when only some did, or an error message
resultslistOne entry per email, in the order you sent them

Each entry in results has these fields:

FieldTypeDescription
emailstringThe email address, as you sent it
statusstringsaved or error
namestringThe name the copy was saved under. Only when status is saved
saved_playlist_idUUIDThe Film Room saved playlist ID for that user's copy. Open it at https://pro.cleaningtheglass.com/video/saved/<saved_playlist_id>. Only when status is saved
errorstringWhy no copy was saved. Only when status is error
json
{
  "result": "Partial",
  "results": [
    {
      "email": "analyst@example.com",
      "status": "saved",
      "name": "Week 3 turnovers",
      "saved_playlist_id": "318e7de6-7242-44da-b05a-d2ce902c331b"
    },
    {
      "email": "coach@example.com",
      "status": "error",
      "error": "No active CTG Pro user with this email in your organization"
    }
  ]
}

A user's entry is an error when:

  • The email is not an active CTG Pro user in your organization. The message is the same whether or not an account exists elsewhere.
  • The user does not have the access needed to save this playlist.
  • More than one account uses the email. Contact CTG to resolve it.

One user's error does not stop the others from being saved. The response is 200 when at least one copy was saved. It is 400 when none was or the request is invalid. It is 403 when your key is not linked to a customer organization, or when the playlist has clips from a league your key cannot access. It is 404 when the playlist does not exist or has no clips, and 500 when no copy could be saved because of a server error, in which case you can retry the same request.

CTG Video Player ​

Open any playlist in the CTG video player:

https://pro.cleaningtheglass.com/video/<playlist_id>

The player does not require a login, so you can send playlist links to anyone. Viewers can select clips from the list next to the video or use these keyboard shortcuts:

KeyAction
SpacePlay or pause
↑ / ↓Previous or next clip
← / →Back or forward 2 seconds
sCycle speed: 0.25x, 0.5x, 1x
lLoop the current clip instead of moving to the next one
fToggle full screen
cCopy a link to the current clip