Skip to content

Instantly share code, notes, and snippets.

@omarkurt
Created December 7, 2025 07:40
Show Gist options
  • Select an option

  • Save omarkurt/ff9c44e3d7731504c610f854b3a5a482 to your computer and use it in GitHub Desktop.

Select an option

Save omarkurt/ff9c44e3d7731504c610f854b3a5a482 to your computer and use it in GitHub Desktop.
GitHub Following to OPML Converter - Export GitHub following list as RSS/Atom feeds for feed readers
#!/usr/bin/env python3
"""
GitHub Following to OPML Converter
Exports your GitHub following list as an OPML file containing Atom feeds
for each user. Import into your favorite feed reader (Feedly, Inoreader, etc.)
to track activity from developers you follow.
Usage:
python github_following_to_opml.py <username>
python github_following_to_opml.py omarkurt
Output:
Creates <username>_following.opml in the current directory
Requirements:
pip install requests
uv pip install requests
"""
import requests
import xml.etree.ElementTree as ET
from xml.dom import minidom
import sys
def generate_opml(username):
"""Fetch GitHub following list and generate OPML file with Atom feeds."""
base_url = f"https://api.github.com/users/{username}/following"
following_list = []
page = 1
print(f"Fetching following list for '{username}', please wait...")
while True:
try:
response = requests.get(f"{base_url}?per_page=100&page={page}")
if response.status_code != 200:
print(f"Error: {response.status_code}")
break
data = response.json()
if not data:
break
for user in data:
following_list.append(user['login'])
page += 1
except Exception as e:
print(f"An error occurred: {e}")
break
# Build OPML structure
opml = ET.Element('opml', version="1.0")
head = ET.SubElement(opml, 'head')
ET.SubElement(head, 'title').text = f"{username} GitHub Following List"
body = ET.SubElement(opml, 'body')
main_outline = ET.SubElement(
body, 'outline',
text=f"Following by {username}",
title=f"{username} Following"
)
for user in following_list:
feed_url = f"https://github.com/{user}.atom"
profile_url = f"https://github.com/{user}"
ET.SubElement(
main_outline, 'outline',
type="rss",
text=user,
title=user,
xmlUrl=feed_url,
htmlUrl=profile_url
)
# Pretty print XML
xmlstr = minidom.parseString(ET.tostring(opml)).toprettyxml(indent=" ")
filename = f"{username}_following.opml"
with open(filename, "w", encoding="utf-8") as f:
f.write(xmlstr)
print(f"\nSuccess! Found {len(following_list)} users.")
print(f"File created: {filename}")
if __name__ == "__main__":
if len(sys.argv) > 1:
target_user = sys.argv[1]
else:
print("No username provided, using default...")
target_user = 'infosec-au'
generate_opml(target_user)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment