Detect Deleted and Private Videos in a Playlist With the YouTube Data API
For anyone who'd rather write twenty lines than click through a UI — the exact field to check, batching, quota, and where the API stops being useful.
Updated 27 September 2026 · 8 min readplaylistItems.list with part=snippet,status and check snippet.title for the literal strings "Deleted video" or "Private video" — that's the most reliable signal the API gives you for a dead playlist entry. It costs 1 quota unit per call regardless of page size, and it can't recover the original title, since that string has already replaced it. For that last step, either build your own archive cross-reference or paste the playlist into MyFreshTools Deleted Video Finder instead.If you're maintaining a scheduled check across many playlists, or building this into a larger tool, clicking through a UI one playlist at a time doesn't scale. The YouTube Data API can tell you which entries in a playlist are dead — the trick is knowing which field to actually check, since the API doesn't expose a clean isDeleted boolean.
The field names and exact response shapes below reflect the API as documented at the time of writing — Google does revise these occasionally, so verify against the current playlistItems and videos reference before you hard-code a check into production.
The actual signal: title becomes a literal string
When a video referenced by a playlist item is deleted or made private, playlistItems.list doesn't fail or omit the item — it still returns a row, but snippet.title is replaced with the literal text "Deleted video" or "Private video", and the thumbnail set comes back empty. Checking for those two exact strings is simpler and more reliable than trying to infer status from missing fields, which can also happen for other reasons.
| snippet.title value | What it means |
|---|---|
"Deleted video" | The video was removed from YouTube entirely. |
"Private video" | The video still exists but is restricted to its owner or invited viewers. |
Any other string | The video is live and playable as far as this call can tell. |
Scanning a playlist's items
Using google-api-python-client, paginate through every item and flag the dead ones:
from googleapiclient.discovery import build
youtube = build("youtube", "v3", developerKey=API_KEY)
def scan_playlist(playlist_id):
dead = []
request = youtube.playlistItems().list(
part="snippet,status",
playlistId=playlist_id,
maxResults=50,
)
while request:
response = request.execute()
for item in response["items"]:
title = item["snippet"]["title"]
if title in ("Deleted video", "Private video"):
dead.append({
"position": item["snippet"]["position"],
"videoId": item["snippet"]["resourceId"]["videoId"],
"status": title,
})
request = youtube.playlistItems().list_next(request, response)
return deadlist_next handles the nextPageToken chase for you — a 5,000-video playlist takes about 100 calls at 50 items per page.
Cross-verifying with videos.list
A video can also be unreachable for reasons that don't produce the literal "Deleted video" string — region-locked licensing being the main one. To double-check a batch of IDs against a second signal, call videos.list and see which ones simply don't come back:
def verify_ids(video_ids):
resp = youtube.videos().list(
part="id",
id=",".join(video_ids[:50]),
).execute()
found = {item["id"] for item in resp["items"]}
return [vid for vid in video_ids if vid not in found]An ID that doesn't come back at all from videos.list is gone or blocked from your key's context — that's a useful second confirmation alongside the title-string check, not a replacement for it.
Batching and quota, so you don't burn your key
Both playlistItems.list and videos.list cost a flat 1 quota unit per call, regardless of how many items or IDs you request in that call — which is exactly why you should always batch:
videos.listaccepts up to 50 comma-separated IDs per call. Sending them one at a time turns a 1-unit job into a 50-unit one for no benefit.playlistItems.listalready batches by page — always request the maximummaxResults=50rather than a smaller page size.- Standard projects get a default daily quota (Google's console shows your current allocation, and it does change — check it rather than assuming a fixed number) shared across every call your key makes, not just this one.
Poll on a schedule, don't hammer it
What the API can't tell you
- The reason. A copyright takedown, a manual deletion and a channel termination all produce the same "Deleted video" string — there's no field that distinguishes them.
- The original title. Once it's replaced, it's gone from this response. Recovering it means cross-referencing outside records — search caches, metadata archives, anything that indexed the video while it was still live.
- A truly private video's contents. If it belongs to someone else's account, no API key or public call will ever read it — that's the platform working as intended, not a gap to work around.
How MyFreshTools helps
What the tool does
The Deleted Video Finder runs the same underlying scan described above — pagination, status detection, all of it — and adds the step the raw API can't do on its own: cross-referencing each dead entry's ID against outside records to recover the original title and channel wherever one survives.
Who it's for
- Developers who want the answer for one or two playlists without provisioning an API key and quota.
- Anyone who wrote the scan above and now needs the title-recovery step it doesn't cover.
- Teams who'd rather not maintain a scheduled job for something they check occasionally.
Requirements and limits
- A free MyFreshTools account. No card, free credits on signup.
- Free scans list every hidden entry with position and status, and reveal one recovered title as a sample; a paid plan reveals them all.
- It recovers metadata only, never the video file, and title recovery is best-effort — the same public-record dependency described above applies here too.
Frequently asked questions
No. Once a video is gone, the API returns the literal string "Deleted video" or "Private video" in place of the title — the original text isn't preserved anywhere in that response.
A plain API key is enough to call playlistItems.list on a public playlist. You only need OAuth if you're reading a private or unlisted playlist that isn't yours, or one belonging to a signed-in user.
About 100, at the maximum 50 items per page, following nextPageToken until it's empty — each call costs 1 quota unit regardless of how many items it returns.
No. It distinguishes deleted from private in the title string, but it can't tell you whether a deletion was voluntary, a copyright takedown, or a channel termination — those all look the same from the outside.
Not through the public Data API. Detecting a change means polling on a schedule and diffing against your last known state yourself.
The takeaway
The Data API gives you exactly one clean signal for a dead playlist entry — the literal title string — and nothing about why it happened or what the video used to be. That's a fine trade for a scheduled automated check; for the "what was it" question, you still need a title-recovery step layered on top, whether you build it or borrow it.