Skip to content

Instantly share code, notes, and snippets.

@aymericbeaumet
Last active December 4, 2025 00:45
Show Gist options
  • Select an option

  • Save aymericbeaumet/d1d6799a1b765c3c8bc0b675b1a1547d to your computer and use it in GitHub Desktop.

Select an option

Save aymericbeaumet/d1d6799a1b765c3c8bc0b675b1a1547d to your computer and use it in GitHub Desktop.
[Recipe] Delete all your likes/favorites from Twitter

Ever wanted to delete all your likes/favorites from Twitter but only found broken/expensive tools? You are in the right place.

  1. Go to: https://twitter.com/{username}/likes
  2. Open the console and run the following JavaScript code:
setInterval(() => {
  for (const d of document.querySelectorAll('div[data-testid="unlike"]')) {
    d.click()
  }
  window.scrollTo(0, document.body.scrollHeight)
}, 1000)
@BrianxTu
Copy link

BrianxTu commented Aug 6, 2024

setInterval(() => {
  const d = document.querySelector('button[data-testid="unlike"]')
  if(d) {
    d.click()
  } else {
    window.scrollTo(0, document.body.scrollHeight)
  }
}, 1000)

worked for me.

@owquresh
Copy link

owquresh commented Aug 7, 2024

What is the limit on this what if you have thousands of likes? I want to start from 0.

@jcoding09
Copy link

To speed up this code while avoiding rate-limiting, consider decreasing the wait time incrementally. One approach is to reduce the time between unlikes initially and increase it if we start approaching Twitter's rate limits. We can also remove the additional wait every 50 tweets. However, this comes with a higher chance of hitting rate limits, so it’s important to adjust the timing carefully.

Below code with shorter delays that increase only if a rate-limiting error is detected. This setup reduces the wait to 3 seconds between actions and increases it only if an error occurs.

function nextUnlike() {
  return document.querySelector('[data-testid="unlike"]');
}

function wait(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function removeAll() {
  let count = 0;
  let next = nextUnlike();
  let waitTime = 3000; // Start with a shorter wait time of 3 seconds

  while (next && count < 1200) {
    try {
      next.focus();
      next.click();
      console.log(`Unliked ${++count} tweets`);
      await wait(waitTime); // Initial wait time of 3 seconds
      next = nextUnlike();

      // If no unlike button is found, scroll to load more
      if (!next && count < 1200) {
        window.scrollTo(0, document.body.scrollHeight); 
        await wait(5000); // Shorter wait for loading more tweets
        next = nextUnlike();
      }
    } catch (error) {
      console.error('An error occurred:', error);
      waitTime = Math.min(waitTime * 2, 60000); // Exponentially increase wait time if an error occurs
      console.log(`Rate limit hit? Increasing wait time to ${waitTime / 1000} seconds.`);
      await wait(waitTime); // Wait before retrying
    }
  }

  if (next) {
    console.log('Finished early to prevent rate-limiting');
  } else {
    console.log('Finished, count =', count);
  }
}

removeAll();

Explanation:

  1. Shorter Initial Wait Time: The initial wait time is set to 3 seconds instead of 10.
  2. Dynamic Wait Time: The wait time doubles each time an error occurs, up to a maximum of 1 minute. This approach helps slow down only if there’s a rate-limiting risk.
  3. Reduced Scroll Wait: The scroll wait is reduced to 5 seconds, making it quicker to load additional tweets.

@woodenpil1ow
Copy link

Whenever i am on twitter it says i am following the three people i clicked the follow button on but whenever i click it it says to "find people to follow"
1 is that connected to me using this to delete my likes
2 if so how can i fix it and if not..... still how do i fix it

@hamzah1P16
Copy link

hamzah1P16 commented Jan 25, 2025

This script is for handling the case where tweets appear in your "Likes" list but do not have the red heart symbol (i.e., they appear unliked). It ensures these tweets are properly unliked by first liking them and then unliking them.


 function nextLike() {
  return document.querySelector('[data-testid="like"]');
}

function nextUnlike() {
  return document.querySelector('[data-testid="unlike"]');
}

function wait(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function removeAll() {
  let count = 0;
  while (count < 500) {
    try {
      // Find the next tweet with a "like" button (appears unliked but is in liked tweets list)
      const likeButton = nextLike();
      if (likeButton) {
        // Scroll the tweet into view to ensure it's interactable
        likeButton.scrollIntoView({ behavior: 'smooth', block: 'center' });
        await wait(1000); // Wait for the tweet to be in view

        // Like the tweet first to reset its state
        likeButton.focus();
        likeButton.click();
        console.log(`Liked tweet to reset state`);
        await wait(1000); // Wait 1 second for the like action to complete

        // Now find the "unlike" button and unlike the tweet
        const unlikeButton = nextUnlike();
        if (unlikeButton) {
          unlikeButton.focus();
          unlikeButton.click();
          console.log(`Unliked ${++count} tweets`);
          await wait(1000); // Wait 1 second between unlikes
        }

        // Remove the processed tweet from the DOM to avoid reprocessing it
        const tweetElement = likeButton.closest('[data-testid="tweet"]');
        if (tweetElement) {
          tweetElement.remove();
        }
      } else {
        // If no more "like" buttons are found, scroll to load more tweets
        window.scrollTo(0, document.body.scrollHeight);
        await wait(2000); // Wait 2 seconds for more tweets to load
      }
    } catch (error) {
      console.error('An error occurred:', error);
      break;
    }
  }
  console.log('Finished, count =', count);
}

removeAll();

@tahakara
Copy link

I think the fastest way is to block the CDN domains that I will provide in the Ublock Origin > My FILTERS section and then use the script below with an interval of 600 ms because the current rate limit is 500 req/5min, so 600ms is suitable for this situation.

CDN domains

abs-0.twimg.com/*
video.twimg.com/*
pbs.twimg.com/*

(Don't forget to delete the filters afterwards.)

setInterval(() => {
  const d = document.querySelector('div[data-testid="unlike"]')
  if(d) {
    d.click()
  } else {
    window.scrollTo(0, document.body.scrollHeight)
  }
}, 1000)

@btywoniuk
Copy link

setInterval(() => {
  for (const d of document.querySelectorAll('button[data-testid="unlike"]')) {
    d.click()
  }
  window.scrollTo(0, document.body.scrollHeight)
}, 1000)

@cerohazuri
Copy link

cerohazuri commented Jul 11, 2025 via email

@iamxeeshankhan
Copy link

Try this one 👇 in case the above methods don't work

setInterval(() => {
  for (const button of document.querySelectorAll(
    'button[data-testid="unlike"], button[aria-label*="Liked"]'
  )) {
    button.click();
  }
  window.scrollTo(0, document.body.scrollHeight);
}, 1000);

@levifasten
Copy link

I want to delete likes from a suspended account, and also from an account that is private (likes don't show up on my likes page, but count as number of likes I have, BTW I can see the links to those tweets in my likes.js of my archive data).

Will any script delete those??

@jconnnewby
Copy link

Is there a code to get rid of all bookmarks?

@jconnnewby
Copy link

I am aware that this is turning into the programmer's version of playing Stairway to Heaven.

But here's mine

  • rather than estimating a scroll offset, it simply uses focus() to scroll the next item into view
  • has a backoff every 50 unlikes to prevent HTTP 429: Too Many Requests
function nextUnlike() {
  return document.querySelector('[data-testid="unlike"]')
}

function wait(ms) {
  return new Promise(resolve => setTimeout(resolve, ms))
}

async function removeAll() {
  let count = 0
  let next = nextUnlike()
  while (next && count < 500) {
    next.focus()
    next.click()
    console.log(`Unliked ${++count} tweets`)
    await wait(count % 50 === 0 ? 30000 : 2000)
    next = nextUnlike()
  }
  console.log('Out of unlikes, count =', count)
}

removeAll() 

At time of writing this is printing Unliked 1506 tweets

stops at 10 tweets

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