Skip to content

Instantly share code, notes, and snippets.

@aalhour
Forked from benwillkommen/_readme.md
Last active January 3, 2026 14:51
Show Gist options
  • Select an option

  • Save aalhour/4faa89f72748d784030bf48263ee6f15 to your computer and use it in GitHub Desktop.

Select an option

Save aalhour/4faa89f72748d784030bf48263ee6f15 to your computer and use it in GitHub Desktop.
Unsubscribe from all watched repositories in a particular organization

GitHub Unsubscribe API

Manage your GitHub repository subscriptions from the command line. List organizations you're subscribed to, view repos within an organization, and bulk unsubscribe.

Why?

This script was created when I took a new job, and my notification settings inadvertently subscribed me to all 600+ of their repos. I wanted a better signal to noise ratio in my notifications, but I didn't want to click the "Unwatch All" button, which would remove my subscriptions to repos outside my new employer's org.

Installation

  1. Create a Personal Access Token with the notifications scope

  2. Copy code and build a local venv for requests:

cd /path/to/script_dir/
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
  1. Export your token:
export GITHUB_PERSONAL_ACCESS_TOKEN=<your-token-here>

Usage

$ ./main.py -h
usage: main.py [-h] [--token TOKEN] {list,repos,unsubscribe} ...

Manage GitHub repository subscriptions

positional arguments:
  {list,repos,unsubscribe}
    list                List all organizations you are subscribed to
    repos               List all repos you are subscribed to in an organization
    unsubscribe         Unsubscribe from all repos in an organization

options:
  -h, --help            show this help message and exit
  --token, -t TOKEN     GitHub Personal Access Token (default: GITHUB_PERSONAL_ACCESS_TOKEN env var)

List all organizations

$ ./main.py list
GET https://api.github.com/user/subscriptions?per_page=100&page=1

You are subscribed to repos from 5 organizations/users:

  my-company: 42 repo(s)
  some-org: 8 repo(s)
  another-org: 3 repo(s)
  cool-project: 2 repo(s)
  random-user: 1 repo(s)

Total: 56 subscriptions

List repos in an organization

$ ./main.py repos some-org
GET https://api.github.com/user/subscriptions?per_page=100&page=1

Subscribed to 3 repos in 'some-org':

  - some-org/api-service
  - some-org/frontend
  - some-org/docs

Unsubscribe from an organization

$ ./main.py unsubscribe some-org
GET https://api.github.com/user/subscriptions?per_page=100&page=1

Found 3 subscribed repos in 'some-org':
  - some-org/api-service
  - some-org/frontend
  - some-org/docs

Unsubscribe from the above 3 repositories? (y/n): y
DELETE https://api.github.com/repos/some-org/api-service/subscription
204
DELETE https://api.github.com/repos/some-org/frontend/subscription
204
DELETE https://api.github.com/repos/some-org/docs/subscription
204

Use --yes or -y to skip the confirmation prompt.

Credits

Note

Forked from: https://gist.github.com/benwillkommen/b7549414883087923ad574d1e846c5f9
Updated code and dependencies to be compatible with Python 3.14 and added a bit more support to list organizations and repos

#!/usr/bin/env python3
"""
Manage GitHub repository subscriptions.
List organizations, view repos, and bulk unsubscribe from the command line.
Run with -h for usage details.
"""
import argparse
import os
import requests
def get_all_subscribed_organizations(personal_access_token):
"""
Get all organizations/owners you are subscribed to repos from.
"""
url = "https://api.github.com/user/subscriptions?per_page=100&page=1"
organizations = {} # org_name -> repo_count
while url is not None:
print(f"GET {url}")
response = requests.get(url,
headers={'Authorization': f'token {personal_access_token}'})
repos = response.json()
for repo in repos:
owner = repo['owner']['login']
organizations[owner] = organizations.get(owner, 0) + 1
try:
url = response.links['next']['url']
except KeyError:
url = None
return organizations
def get_subscribed_repos_in_organization(organization, personal_access_token):
"""
Get all repos you are subscribed to in a specific organization.
"""
url = "https://api.github.com/user/subscriptions?per_page=100&page=1"
while url is not None:
print(f"GET {url}")
response = requests.get(url,
headers={'Authorization': f'token {personal_access_token}'})
repos = response.json()
repos_from_response = [repo['full_name'] for repo in repos if repo['owner']['login'] == organization]
for repo_name in repos_from_response:
yield repo_name
try:
url = response.links['next']['url']
except KeyError:
url = None
def yes_or_no(question):
"""
Prompt user for yes/no confirmation.
"""
reply = str(input(question + ' (y/n): ')).lower().strip()
if reply[0] == 'y':
return True
if reply[0] == 'n':
return False
else:
return yes_or_no("Please choose")
def cmd_list(args):
"""
Handle the 'list' subcommand.
"""
organizations = get_all_subscribed_organizations(args.token)
if not organizations:
print("\nYou are not subscribed to any repositories.")
else:
print(f"\nYou are subscribed to repos from {len(organizations)} organizations/users:\n")
# Sort by repo count (descending)
for org, count in sorted(organizations.items(), key=lambda x: x[1], reverse=True):
print(f" {org}: {count} repo(s)")
print(f"\nTotal: {sum(organizations.values())} subscriptions")
def cmd_repos(args):
"""
Handle the 'repos' subcommand.
"""
repos_in_organization = list(get_subscribed_repos_in_organization(args.organization, args.token))
if not repos_in_organization:
print(f"\nNo subscriptions found for organization: {args.organization}")
else:
print(f"\nSubscribed to {len(repos_in_organization)} repos in '{args.organization}':\n")
for repo in repos_in_organization:
print(f" - {repo}")
def cmd_unsubscribe(args):
"""
Handle the 'unsubscribe' subcommand.
"""
repos_in_organization = list(get_subscribed_repos_in_organization(args.organization, args.token))
if not repos_in_organization:
print(f"\nNo subscriptions found for organization: {args.organization}")
return
print(f"\nFound {len(repos_in_organization)} subscribed repos in '{args.organization}':")
for repo in repos_in_organization:
print(f" - {repo}")
if args.yes or yes_or_no(f"\nUnsubscribe from the above {len(repos_in_organization)} repositories?"):
for repo_full_name in repos_in_organization:
url = f"https://api.github.com/repos/{repo_full_name}/subscription"
print(f"DELETE {url}")
response = requests.delete(url,
headers={'Authorization': f'token {args.token}'})
print(response.status_code)
def main():
parser = argparse.ArgumentParser(
description='Manage GitHub repository subscriptions',
epilog='''Examples:
%(prog)s list List all organizations you're subscribed to
%(prog)s repos <org> List repos you're subscribed to in <org>
%(prog)s unsubscribe <org> Unsubscribe from all repos in <org>
%(prog)s unsubscribe <org> -y Skip confirmation prompt''',
formatter_class=argparse.RawDescriptionHelpFormatter
)
# Add token argument at parser level (available to all subcommands)
parser.add_argument(
'--token', '-t',
default=os.environ.get('GITHUB_PERSONAL_ACCESS_TOKEN'),
help='GitHub Personal Access Token (default: GITHUB_PERSONAL_ACCESS_TOKEN env var)'
)
subparsers = parser.add_subparsers(dest='command', required=True)
# 'list' subcommand
list_parser = subparsers.add_parser(
'list',
help='List all organizations you are subscribed to'
)
list_parser.set_defaults(func=cmd_list)
# 'repos' subcommand
repos_parser = subparsers.add_parser(
'repos',
help='List all repos you are subscribed to in an organization'
)
repos_parser.add_argument(
'organization',
help='Name of the organization/user'
)
repos_parser.set_defaults(func=cmd_repos)
# 'unsubscribe' subcommand
unsubscribe_parser = subparsers.add_parser(
'unsubscribe',
help='Unsubscribe from all repos in an organization'
)
unsubscribe_parser.add_argument(
'organization',
help='Name of the organization/user to unsubscribe from'
)
unsubscribe_parser.add_argument(
'--yes', '-y',
action='store_true',
help='Skip confirmation prompt'
)
unsubscribe_parser.set_defaults(func=cmd_unsubscribe)
args = parser.parse_args()
if not args.token:
parser.error('GitHub token is required. Set GITHUB_PERSONAL_ACCESS_TOKEN or use --token')
args.func(args)
if __name__ == '__main__':
main()
requests>=2.31.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment