I recently came across a common problem which we all face, Subscribing non-active channels on Youtube.
I realized I was subscribed to hundreds of YouTube channels, many of which hadn’t posted a video in months….sometimes years! They were just cluttering my feed and wasting my attention.
The Goal: Find and automatically unsubscribe from any channel that hasn’t posted in the last 90 days.
This is exactly the kind of repetitive, data-heavy task that a general-purpose language like Python absolutely dominates. I put my love and respect for Python to work, building a simple automation script.
The High-Level Solution:
- Authentication: The script first used a secure Google API key to log in and get permission to view and manage my subscriptions.
- Data Fetching: It then made calls to the YouTube API to pull my entire subscription list.
- The Logic: For every single channel, the script performed a date check to see when their last video was published.
- Human Veto: Before making any permanent changes, it printed a clean list of all “inactive” channels and asked for a final “YES” confirmation from me.
- Execution: After my approval, it used the API one last time to systematically delete the inactive subscriptions.
The result? A streamlined YouTube feed and a little more peace of mind. It’s truly amazing what you can, accomplish with a little bit of code! Here is the code, which truly eased my life.
import os
from datetime import datetime, timedelta, timezone
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
# Import HttpError specifically for the fix
from googleapiclient.errors import HttpError
# --- CONFIGURATION ---
DAYS_INACTIVE_THRESHOLD = 90
SCOPES = ["https://www.googleapis.com/auth/youtube"]
CLIENT_SECRET_FILE = "client_secret.json"
# ---------------------
def get_authenticated_service():
"""Authenticates the user and returns the YouTube Data API service object."""
# The first time this runs, it will open your browser for authentication
flow = InstalledAppFlow.from_client_secrets_file(CLIENT_SECRET_FILE, SCOPES)
credentials = flow.run_local_server(port=0)
return build("youtube", "v3", credentials=credentials)
def list_my_subscriptions(youtube):
"""Retrieves all subscriptions for the authenticated user."""
print("-> Fetching all current subscriptions...")
subscriptions = []
# Use 'pages' to handle large subscription lists
next_page_token = None
while True:
request = youtube.subscriptions().list(
part="snippet",
mine=True,
maxResults=50,
pageToken=next_page_token
)
response = request.execute()
for item in response.get("items", []):
subscriptions.append(item)
next_page_token = response.get("nextPageToken")
if not next_page_token:
break
print(f"-> Found {len(subscriptions)} total subscriptions.")
return subscriptions
def get_channel_activity(youtube, channel_id, channel_title):
"""
Fetches the last upload date for a given channel.
Includes robust error handling for missing channel data or playlists.
"""
# 1. Fetch channel details to get the 'uploads' playlist ID
request = youtube.channels().list(
part="contentDetails",
id=channel_id
)
response = request.execute()
if not response.get("items"):
print(f"Checking: {channel_title}... **Skipped (No Channel Data)**.")
return None
try:
# Get the ID of the playlist that holds the channel's uploads
uploads_playlist_id = response["items"][0]["contentDetails"]["relatedPlaylists"]["uploads"]
except KeyError:
print(f"Checking: {channel_title}... **Skipped (No Uploads Playlist ID)**.")
return None
# 2. Fetch the most recent video from the uploads playlist
try:
request = youtube.playlistItems().list(
part="snippet",
playlistId=uploads_playlist_id,
maxResults=1 # We only need the latest video
)
response = request.execute()
# CRITICAL FIX: Handle the 404 error when the playlist cannot be found
except HttpError as e:
if e.resp.status == 404:
# This handles channels where the 'uploads' playlist is inaccessible or not found
print(f"Checking: {channel_title}... **Skipped (Uploads Playlist Not Found)**.")
return None
else:
# Re-raise any other unexpected HTTP error
raise e
if response.get("items"):
# The publishedAt field is the date the video was uploaded
published_at_str = response["items"][0]["snippet"]["publishedAt"]
# Convert the string to a datetime object
return datetime.strptime(published_at_str, '%Y-%m-%dT%H:%M:%S%z')
print(f"Checking: {channel_title}... **Skipped (No Videos Found in Playlist)**.")
return None # No videos found in the playlist
def delete_subscription(youtube, subscription_id):
"""Deletes a single subscription by its ID."""
youtube.subscriptions().delete(id=subscription_id).execute()
def main():
if not os.path.exists(CLIENT_SECRET_FILE):
print(f"ERROR: Client secret file '{CLIENT_SECRET_FILE}' not found.")
print("Please follow the instructions to create and download the file.")
return
youtube = get_authenticated_service()
all_subscriptions = list_my_subscriptions(youtube)
# Calculate the cutoff date (90 days ago)
cutoff_date = datetime.now(timezone.utc) - timedelta(days=DAYS_INACTIVE_THRESHOLD)
inactive_subscriptions = []
print("\n--- Checking Channel Activity ---")
for sub in all_subscriptions:
# Extract necessary IDs and title from the subscription object
channel_id = sub["snippet"]["resourceId"]["channelId"]
channel_title = sub["snippet"]["title"]
subscription_id = sub["id"]
# Pass the title to the function for better error logging
last_activity = get_channel_activity(youtube, channel_id, channel_title)
# Only proceed if we actually got a last activity date
if last_activity is not None:
if last_activity < cutoff_date:
inactive_subscriptions.append({
"title": channel_title,
"sub_id": subscription_id,
"last_active": last_activity.date()
})
print(f"Checking: {channel_title}... INACTIVE. Last post: {last_activity.date()}")
else:
print(f"Checking: {channel_title}... Active. Last post: {last_activity.date()}")
print("\n" + "="*50)
print(f"SUMMARY: {len(inactive_subscriptions)} channels found inactive for > {DAYS_INACTIVE_THRESHOLD} days.")
print("="*50)
if not inactive_subscriptions:
print("No inactive channels to remove. Exiting.")
return
# --- HUMAN REVIEW STEP ---
print("\nChannels to be UN-SUBSCRIBED:")
for i, item in enumerate(inactive_subscriptions):
print(f" {i+1}. **{item['title']}** (Last active: {item['last_active']})")
confirmation = input("\nDo you want to DELETE these subscriptions? (Type 'YES' in all caps to proceed): ")
if confirmation.upper() == 'YES':
print("\n--- Deleting Subscriptions ---")
for item in inactive_subscriptions:
delete_subscription(youtube, item["sub_id"])
print(f"Successfully deleted: {item['title']}")
print("\n**Cleanup complete!**")
else:
print("Deletion cancelled by user. Exiting.")
if __name__ == "__main__":
main()
- Are you organized? Show me your download folder… - October 7, 2025
- Decluttering My Digital Life - October 7, 2025
- The Power of Deep Research - February 5, 2025
