Skip to content

Instantly share code, notes, and snippets.

@jillesvangurp
Last active February 27, 2026 06:39
Show Gist options
  • Select an option

  • Save jillesvangurp/b43cc5bbdbc0a9a29c7f0944d6cc5854 to your computer and use it in GitHub Desktop.

Select an option

Save jillesvangurp/b43cc5bbdbc0a9a29c7f0944d6cc5854 to your computer and use it in GitHub Desktop.
Beat the Algorithm - Download Youtube Subscriptions as OPML

Export Your YouTube Subscriptions to OPML (and Use Any RSS Reader)

YouTube doesn't make it easy to take your subscriptions elsewhere. But every channel still has a public RSS feed.

This guide lets you:

  • Export all your subscriptions
  • Generate a valid OPML file
  • Import them into any RSS reader
  • Beat the algorithm

No extensions. No API keys. No Google Takeout.

Step 1 - Open the Subscriptions Page

Go to:

https://www.youtube.com/feed/channels

Step 2 - Create a Bookmarklet

Create a new bookmark in your browser.

Set the URL of the bookmark to the following:

javascript:(async function(){function sleep(ms){return new Promise(function(r){setTimeout(r,ms);});}
if(location.pathname==="/feed/subscriptions"){alert("Open https://www.youtube.com/feed/channels and run again.");return;}
function esc(s){return String(s).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;");}
function pickText(t){if(!t)return"";if(typeof t==="string")return t;if(t.simpleText)return t.simpleText;if(t.runs&&t.runs.length){var out="";for(var i=0;i<t.runs.length;i++)out+=t.runs[i].text||"";return out.trim();}return"";}
function walk(node,fn){if(!node||typeof node!=="object")return;fn(node);if(Array.isArray(node)){for(var i=0;i<node.length;i++)walk(node[i],fn);}else{for(var k in node)if(Object.prototype.hasOwnProperty.call(node,k))walk(node[k],fn);}}
function fromInitialData(){var m={};try{walk(window.ytInitialData||{},function(o){if(!o||typeof o!=="object")return;var id=o.channelId;if(typeof id==="string"&&/^UC[0-9A-Za-z_-]{20,}$/.test(id)){var title=pickText(o.title)||pickText(o.channelTitle)||pickText(o.name);if(title){m[id]=title;}else if(!(id in m)){m[id]="";}}});}catch(e){}return m;}
function fromDOM(){var m={};var links=document.querySelectorAll('a[href*="/channel/"]');for(var i=0;i<links.length;i++){var a=links[i];var h=a.getAttribute("href")||"";var mm=h.match(/\/channel\/(UC[0-9A-Za-z_-]{20,})/);if(!mm)continue;var id=mm[1];var title=(a.getAttribute("title")||"").trim();if(!(id in m))m[id]=title;}return m;}
function merge(a,b){for(var id in b)if(Object.prototype.hasOwnProperty.call(b,id)){if(!(id in a)||(!a[id]&&b[id]))a[id]=b[id];}return a;}
var chans=merge(fromInitialData(),fromDOM());
var stable=0;
for(var iter=0;iter<60;iter++){
  window.scrollTo(0,document.documentElement.scrollHeight);
  await sleep(650);
  var before=Object.keys(chans).length;
  chans=merge(chans,merge(fromInitialData(),fromDOM()));
  var after=Object.keys(chans).length;
  if(after===before)stable++;else stable=0;
  if(stable>=6)break;
}
var ids=Object.keys(chans);
ids.sort(function(a,b){
  var ta=(chans[a]||a).toLowerCase(), tb=(chans[b]||b).toLowerCase();
  if(ta<tb)return -1; if(ta>tb)return 1; return 0;
});
var outlines=[];
for(var j=0;j<ids.length;j++){
  var id=ids[j];
  var title=(chans[id]||"").trim()||id;
  var feed="https://www.youtube.com/feeds/videos.xml?channel_id="+id;
  var html="https://www.youtube.com/channel/"+id;
  outlines.push('    <outline type="rss" text="'+esc(title)+'" title="'+esc(title)+'" xmlUrl="'+feed+'" htmlUrl="'+html+'" />');
}
var opml='<?xml version="1.0" encoding="UTF-8"?>\n<opml version="2.0">\n  <head>\n    <title>YouTube Subscriptions</title>\n  </head>\n  <body>\n    <outline text="YouTube Subscriptions" title="YouTube Subscriptions">\n'+outlines.join("\n")+'\n    </outline>\n  </body>\n</opml>\n';
var blob=new Blob([opml],{type:"text/xml;charset=utf-8;"});
var url=URL.createObjectURL(blob);
var a=document.createElement("a");
a.href=url;
a.download="youtube-subscriptions.opml";
document.body.appendChild(a);
a.click();
setTimeout(function(){document.body.removeChild(a);URL.revokeObjectURL(url);},1000);
alert("Downloaded youtube-subscriptions.opml ("+ids.length+" channels).");
})();

Save the bookmark.

Step 3 - Run It

  1. Go to https://www.youtube.com/feed/channels
  2. Click your new bookmark\
  3. Wait a few seconds (it scrolls automatically)
  4. A file named youtube-subscriptions.opml will download

Step 4 - Import Into Any RSS Reader

Most RSS readers support OPML import. Look for:

Settings → Import → OPML

Examples:

  • Feedly\
  • Inoreader\
  • FreshRSS\
  • Reeder\
  • NetNewsWire\
  • Tiny Tiny RSS\
  • Miniflux

What This Does

Each YouTube channel exposes an RSS feed at:

https://www.youtube.com/feeds/videos.xml?channel_id=CHANNEL_ID

The script:

  • Extracts all your subscribed channel IDs
  • Builds proper RSS feed URLs
  • Generates a valid OPML file
  • Downloads it locally

No data is sent anywhere. Everything runs inside your browser.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment