Unsubscribe from all YouTube channels at once

Start your subscriptions feed from zero. This free script unsubscribes you from every channel except the ones you choose to keep, using YouTube’s own buttons.

unsubscribe-all.js · 133 linespaste in the DevTools console on www.youtube.com
// Responsive Muse · Unsubscribe From All YouTube Channels · v1.1 · 2026-10-22
// Unsubscribes you from every channel, except the ones you list in KEEP.
// Runs only on youtube.com/feed/channels. It counts first and asks before changing anything,
// then clicks the same Unsubscribe button and confirmation you would. It makes no network
// requests of its own and never reads cookies, passwords or tokens.
(async () => {
  const TOOL = 'Unsubscribe from all channels';
  const KEEP = [];        // channels to keep, by name or @handle, e.g. ['@mkbhd', 'Veritasium']
  const LIMIT = 0;        // stop after this many channels (0 = no limit)
  const DELAY = 900;      // pause between actions, in milliseconds

  if (location.hostname !== 'www.youtube.com' || location.pathname !== '/feed/channels') {
    alert('Open your subscriptions first (signed in):\nhttps://www.youtube.com/feed/channels');
    return;
  }
  // A small box on the page shows progress and asks before changing anything,
  // so you never need to read or edit the console.
  const many = (n, word) => `${n} ${word}${n === 1 ? '' : 's'}`;   // 1 video, 2 videos
  const panel = (() => {
    const box = document.createElement('div');
    box.style.cssText = 'position:fixed;right:16px;bottom:16px;z-index:2147483647;width:340px;max-width:calc(100vw - 32px);' +
      'background:#111318;color:#e6e8ee;font:14px/1.5 system-ui,sans-serif;border-radius:12px;padding:16px;' +
      'box-shadow:0 8px 30px rgba(0,0,0,.4);text-align:left';
    const head = document.createElement('strong');
    head.textContent = TOOL;
    const msg = document.createElement('p');
    msg.style.cssText = 'margin:8px 0 12px;white-space:pre-line';
    const row = document.createElement('div');
    row.style.cssText = 'display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end';
    box.append(head, msg, row);
    document.body.append(box);
    const button = (label, primary, onClick) => {
      const b = document.createElement('button');
      b.textContent = label;
      b.style.cssText = 'font:600 14px system-ui,sans-serif;padding:8px 14px;border-radius:8px;cursor:pointer;' +
        (primary ? 'background:#0f766e;color:#fff;border:1px solid #0f766e' : 'background:none;color:#e6e8ee;border:1px solid #3a3f4b');
      b.onclick = onClick;
      row.append(b);
    };
    return {
      text(t) { msg.textContent = t; },
      ask(t, yes) {
        msg.textContent = t;
        row.replaceChildren();
        return new Promise(r => {
          button('Cancel', false, () => { row.replaceChildren(); r(false); });
          button(yes, true, () => { row.replaceChildren(); r(true); });
        });
      },
      stoppable(onStop) { row.replaceChildren(); button('Stop', false, onStop); },
      actions(t, list) {
        msg.textContent = t;
        row.replaceChildren();
        list.forEach(([label, primary, run]) => button(label, primary, run));
      },
      finish(t) { this.actions(t, [['Close', true, () => box.remove()]]); },
      close() { box.remove(); },
    };
  })();
  const sleep = ms => new Promise(r => setTimeout(r, ms));
  // Wait until something shows up on screen (menus can take a moment to open)
  const visible = e => e.getClientRects().length > 0;
  const waitFor = async (find, ms = 4000) => {
    for (let t = 0; t < ms; t += 150) {
      const hit = find();
      if (hit) return hit;
      await sleep(150);
    }
    return null;
  };
  const keep = new Set(KEEP.map(k => k.trim().toLowerCase()));
  const text = (el, sel) => (el.querySelector(sel)?.textContent || '').trim();
  const kept = el => keep.has(text(el, '#channel-title #text').toLowerCase()) || keep.has(text(el, '#subscribers').toLowerCase());
  // The button that unsubscribes is labelled "Unsubscribe from <channel>" (also in other languages)
  const UNSUB = /^(unsubscribe|cancelar (la )?suscripción|anular suscripción|cancelar inscrição|se désabonner|désabonner|abo beenden|annulla iscrizione)/i;
  const unsubButton = el => [...el.querySelectorAll('#subscribe-button button[aria-label]')]
    .find(b => UNSUB.test(b.getAttribute('aria-label')));
  const done = new Set();   // each channel is handled once, even if YouTube is slow to update the button
  const channelId = el => el.data?.channelId || text(el, '#channel-title #text');
  const subscribed = () => [...document.querySelectorAll('ytd-channel-renderer')]
    .filter(el => !kept(el) && !done.has(channelId(el)) && unsubButton(el));

  // 1. Load the whole list
  let last = -1, idle = 0;
  while (idle < 3) {
    window.scrollTo(0, document.documentElement.scrollHeight);
    await sleep(1200);
    const n = document.querySelectorAll('ytd-channel-renderer').length;
    idle = n === last ? idle + 1 : 0;
    last = n;
    panel.text(`Loading your subscriptions… ${n} channels`);
  }
  const total = LIMIT ? Math.min(LIMIT, subscribed().length) : subscribed().length;
  if (!total) {
    panel.finish(keep.size ? 'Only the channels you chose to keep are left.' : 'You are not subscribed to any channel.');
    return 0;
  }

  // 2. Ask
  const kept_ = keep.size ? `\nKeeping: ${KEEP.join(', ')}` : '';
  if (!await panel.ask(`Unsubscribe from ${many(total, 'channel')}?${kept_}\nThis can't be undone.`, `Unsubscribe ${total}`)) {
    panel.finish('Cancelled. Nothing was changed.');
    return 0;
  }

  // 3. Unsubscribe one by one: the button, then YouTube's confirmation dialog
  let stop = false;
  panel.stoppable(() => { stop = true; });
  let count = 0;
  while (!stop && count < total) {
    const el = subscribed()[0];
    if (!el) break;
    done.add(channelId(el));
    const name = text(el, '#channel-title #text');
    el.scrollIntoView({ block: 'center' });
    unsubButton(el).click();
    const dialog = await waitFor(() => [...document.querySelectorAll('yt-confirm-dialog-renderer')].find(visible));
    const confirm = dialog && [...dialog.querySelectorAll('button')].pop();
    if (!confirm) {
      panel.finish(`Unsubscribed from ${count}, then YouTube showed no confirmation for ${name}.\nYouTube may have changed its layout: please report it on responsive-muse.com.`);
      return count;
    }
    confirm.click();
    // Wait for the dialog to close, so the next channel doesn't reuse it
    await waitFor(() => !visible(dialog) || null);
    count++;
    panel.text(`Unsubscribing… ${count} of ${total}\n${name}`);
    await sleep(DELAY);
  }
  panel.finish(`${stop ? 'Stopped' : 'Done'}: unsubscribed from ${count} of ${many(total, 'channel')}.\nReload the page to see the result.`);
  return count;
})();

Bookmarklet: drag the middle button to your bookmarks bar, open www.youtube.com and click the bookmark. It runs the same script with the options above.

Why it's safe to runAutomated check passed Oct 23, 2026 · tested on a real account Oct 22, 2026
  • Sends nothing anywhere. No fetch, XHR or external scripts. The only network activity is www.youtube.com itself loading your list.
  • Never touches your login. It doesn't read cookies, passwords, tokens or browser storage.
  • Only runs on www.youtube.com. Anywhere else it stops before doing anything.
  • Short and readable. 133 commented lines, not minified or obfuscated.
  • Asks before it changes anything. A box on the page shows what it found and waits for your OK. You can stop it at any time.
  • Same code for everyone. SHA-256 of the default version: 4d700ec3ff975250…
Verify it yourself in 30 seconds
  1. Read it: every line is above, with comments.
  2. Search the code for fetch, XMLHttpRequest, cookie, localStorage or eval. None of them appear.
  3. Open the Network tab before running it: you'll only see requests to www.youtube.com.

Good to know: this can't be undone in bulk. The script always shows the count and asks before it starts. Leave the tab open until the box says "Done".

Did it work for you?

How to use it

  1. Open youtube.com/feed/channels while signed in.
  2. Open the console: F12 or Ctrl+Shift+J (Cmd+Option+J on Mac). If Chrome asks, type allow pasting and press Enter.
  3. Type the channels you want to keep (optional), copy the script, paste it in the console and press Enter. A small box shows how many channels it will unsubscribe from: click Unsubscribe to confirm, or Cancel.
  4. Each channel is confirmed in YouTube’s own dialog, automatically. You can stop it at any time.

Why use a script for this?

YouTube has no “unsubscribe from all” option, so with hundreds of channels it means hundreds of clicks and confirmations. Many old snippets online break on the 2026 layout: after unsubscribing, YouTube keeps showing a “Subscribed” bell button, and those scripts click it again and re-subscribe you. This one checks the real button state and handles each channel only once.

Save a copy first with the subscriptions exporter.

FAQ

Will the channels know I unsubscribed?

No. YouTube doesn't notify creators when someone unsubscribes.

Can I keep some channels?

Yes. Type their names or @handles in "Channels to keep" and the script skips them.

Should I export my subscriptions first?

It's a good idea. Use the subscriptions exporter to save a CSV or OPML copy before you start.

More YouTube tools