Channels that relied on curated YouTube playlists lose content constantly as videos are removed, age-restricted, or deleted. This tutorial tracks playlist removals daily via Scavio's YouTube playlist endpoint and emits a diff so you can replace removed videos before your viewers notice. Scavio has no playlist endpoint - YouTube coverage is search, video, transcript, comments, related, streams, shorts and channel feeds. This tutorial therefore snapshots a channel's uploads (/api/v1/youtube/channel/videos) and diffs them, which catches the same disappearances for content you own or follow.
Prerequisites
- Python 3.10+
- A Scavio API key
- A YouTube playlist URL or ID
- SQLite for daily snapshots
Walkthrough
Step 1: Fetch the channel's uploads
There is no playlist endpoint. /api/v1/youtube/channel/videos takes a channel id, @handle or URL and returns the uploads feed under data.videos.
import requests, os
API_KEY = os.environ['SCAVIO_API_KEY']
H = {'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/json'}
def uploads(channel):
r = requests.post('https://api.scavio.dev/api/v1/youtube/channel/videos',
headers=H, json={'channel_id': channel})
r.raise_for_status()
return r.json()['data'].get('videos', [])Step 2: Snapshot to SQLite
One row per video per day.
import sqlite3
conn = sqlite3.connect('pl.db')
conn.execute('CREATE TABLE IF NOT EXISTS snap (date TEXT, playlist TEXT, video_id TEXT, title TEXT, status TEXT)')
def snapshot(pid, videos):
for v in videos:
conn.execute('INSERT INTO snap VALUES (date(\'now\'), ?, ?, ?, ?)',
(pid, v['id'], v['title'], v.get('status', 'available')))
conn.commit()Step 3: Diff against yesterday
New removals = yesterday - today.
def diff(pid):
y = set(r[0] for r in conn.execute('SELECT video_id FROM snap WHERE date = date(\'now\', \'-1 day\') AND playlist = ?', (pid,)))
t = set(r[0] for r in conn.execute('SELECT video_id FROM snap WHERE date = date(\'now\') AND playlist = ?', (pid,)))
return y - tStep 4: Alert on removals
Slack or email when removals > 0.
def alert(removed):
if removed:
print(f'ALERT: {len(removed)} videos removed: {list(removed)}')Step 5: Schedule daily run
cron or GitHub Actions at 6am UTC.
0 6 * * * /usr/bin/python3 /path/to/playlist_watch.pyPython Example
import os, requests, sqlite3
API_KEY = os.environ['SCAVIO_API_KEY']
H = {'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/json'}
CHANNEL = '@MrBeast'
conn = sqlite3.connect('pl.db')
conn.execute('CREATE TABLE IF NOT EXISTS snap (date TEXT, video_id TEXT)')
r = requests.post('https://api.scavio.dev/api/v1/youtube/channel/videos',
headers=H, json={'channel_id': CHANNEL})
r.raise_for_status()
for v in r.json()['data'].get('videos', []):
conn.execute("INSERT INTO snap VALUES (date('now'), ?)", (v['video_id'],))
conn.commit()
print('snapshot saved')JavaScript Example
const API_KEY = process.env.SCAVIO_API_KEY;
const H = { Authorization: `Bearer ${API_KEY}`, 'Content-Type': 'application/json' };
export async function snap(channelId) {
const r = await fetch('https://api.scavio.dev/api/v1/youtube/channel/videos', {
method: 'POST', headers: H, body: JSON.stringify({ channel_id: channelId })
});
if (!r.ok) throw new Error('Scavio ' + r.status);
const { data } = await r.json();
return data.videos || [];
}Expected Output
Daily diff per playlist, highlighting removed video IDs and titles. Typical finding: 1-5% of a 200-video playlist goes unavailable per month.