Skip to content

Instantly share code, notes, and snippets.

@aymericbeaumet
Last active September 29, 2024 19:47
Show Gist options
  • Save aymericbeaumet/d1d6799a1b765c3c8bc0b675b1a1547d to your computer and use it in GitHub Desktop.
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)
@chrisheninger
Copy link

image

Checking the network request's response shows rate-limit info.

If you're running the command you'll quickly find out you can only unlike ~500 tweets in a ~15 minute period (as of September 2023)

You'll need to wait for the limit to reset– you can copy/paste the timestamp into https://www.epochconverter.com/ to figure out when it will reset.

@jbreckmckye
Copy link

jbreckmckye commented Sep 18, 2023

Good find @chrisheninger! I guess then any script should take that into account

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()
  }
  if (next) {
    console.log('Finished early to prevent rate-limiting')
  } else {
    console.log('Finished, count =', count)
  }
}

removeAll() 

@Eggroley
Copy link

Eggroley commented Nov 3, 2023

Anyone have something that works for the old twitter layout? I currently can't see any past likes at all using the new twitter layout. Old one still shows them. Right now I'm using an extension to get the old look.

@xhapless
Copy link

Anyone have something that works for the old twitter layout? I currently can't see any past likes at all using the new twitter layout. Old one still shows them. Right now I'm using an extension to get the old look.

If you are using dimdenGD extension, you can use the below updated code. I just updated the querySelector .

function nextUnlike() {
  return document.querySelector('.tweet-interact-favorite.tweet-interact-favorited')
}

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()
  }
  if (next) {
    console.log('Finished early to prevent rate-limiting')
  } else {
    console.log('Finished, count =', count)
  }
}

removeAll()

@Eggroley
Copy link

Anyone have something that works for the old twitter layout? I currently can't see any past likes at all using the new twitter layout. Old one still shows them. Right now I'm using an extension to get the old look.

If you are using dimdenGD extension, you can use the below updated code. I just updated the querySelector .

function nextUnlike() {
  return document.querySelector('.tweet-interact-favorite.tweet-interact-favorited')
}

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()
  }
  if (next) {
    console.log('Finished early to prevent rate-limiting')
  } else {
    console.log('Finished, count =', count)
  }
}

removeAll()

Thanks! Works well

Unfortunately, now I'm at the point where posts don't even show likes. I have to like and unlike to remove them :/

Twitter is lame

@TheAbdusalam
Copy link

Thank you @aymericbeaumet appreciate it

@schmutzie
Copy link

Anyone have something that works for the old twitter layout? I currently can't see any past likes at all using the new twitter layout. Old one still shows them. Right now I'm using an extension to get the old look.

If you are using dimdenGD extension, you can use the below updated code. I just updated the querySelector .

function nextUnlike() {
  return document.querySelector('.tweet-interact-favorite.tweet-interact-favorited')
}

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()
  }
  if (next) {
    console.log('Finished early to prevent rate-limiting')
  } else {
    console.log('Finished, count =', count)
  }
}

removeAll()

Is there a way to have this first like, and then unlike tweets on the Likes page? The first script here left me with over 121K formerly liked tweets still counted and sitting under the Likes section. Only liking again and then unliking again fully removes them.

@trinlol
Copy link

trinlol commented Jan 1, 2024

Here is a version for likes, but you can specify tweets from a specific user to delete.

setInterval(() => {
  function removePostsByAuthor(authorHandle) {
    let posts = Array.from(document.querySelectorAll('.tweet'));
    posts.forEach(post => {
      let author = post.querySelector('.tweet-header-handle').textContent;
      if (author === authorHandle) {
        let likeButton = post.querySelector('.tweet-button.tweet-interact-favorite.tweet-interact-favorited');
        if (likeButton) {
          likeButton.click();
        }
      }
    });
  }

  // Call the function with the author's handle
  removePostsByAuthor('@USERNAME'); // Case sensitive (must match exactly)
  window.scrollTo(0, document.body.scrollHeight)
}, 1000)

Here is a version for retweets, but you can specify tweets from a specific user to delete.

setInterval(() => {
function removePostsByAuthor(authorHandle) {
  let posts = Array.from(document.querySelectorAll('.tweet'));
  posts.forEach(post => {
    let author = post.querySelector('.tweet-header-handle').textContent;
    if (author === authorHandle) {
      let tweetButton = post.querySelector('.tweet-button.tweet-interact-retweet.tweet-interact-retweeted');
      if (tweetButton) {
        tweetButton.click();
        let retweetMenu = tweetButton.parentElement.querySelector(".tweet-interact-retweet-menu-retweet");
        if (retweetMenu) {
          retweetMenu.click();
        }
      }
    }
  });
}

// Call the function with the author's handle
removePostsByAuthor('@USERNAME'); // Case sensitive (must match exactly)
  window.scrollTo(0, document.body.scrollHeight)
}, 1000)

Probably a better way to do this, but it works.

Must be using the old twitter extension as the above post's OldTwitter - Easier to get element IDs.

@danielwagn3r
Copy link

Made a little modification, which adds a page scroll to load futher likes in case you've got really many of them

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) {
    try {
      next.focus();
      next.click();
      console.log(`Unliked ${++count} tweets`);
      await wait(count % 50 === 0 ? 60000 : 10000); // Increased wait times
      next = nextUnlike();
      if (!next && count < 500) {
        window.scrollTo(0, document.body.scrollHeight); // Scroll to the bottom of the page
        await wait(10000); // Increased wait time for more tweets to load
        next = nextUnlike(); // Get the next unlike button
      }
    } catch (error) {
      console.error('An error occurred:', error);
      break;
    }
  }
  if (next) {
    console.log('Finished early to prevent rate-limiting');
  } else {
    console.log('Finished, count =', count);
  }
}

removeAll();

@captainhook
Copy link

Is there a way to have this first like, and then unlike tweets on the Likes page? The first script here left me with over 121K formerly liked tweets still counted and sitting under the Likes section. Only liking again and then unliking again fully removes them.

I'm facing this issue now too. You could possibly run the script to first go and like a few hundred tweets and then after that a script to unlike them again. Or you'd have to create a counter around document.querySelector and loop through that.

@celine1OF1
Copy link

Screenshot 2024-01-10 091055
why arent the rest of my likes showing?

@captainhook
Copy link

Plenty of people above having the same issue with no definitive reasoning. Could be rate limiting, could be free tier limits, could be API bug, could be something else.

@h0jeZvgoxFepBQ2C
Copy link

This works fine for me:

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

@Valx01P
Copy link

Valx01P commented Jan 29, 2024

Damn twitter API, these rate limits suck, how am I supposed to delete my socially incriminating twitter history if it only let's me remove 20 likes using a script before it stops letting me, twitter needs to implement an API endpoint to just remove all likes, tweets, retweets, etc. There is no way I'm gonna be able to remove thousands of likes manually lmao

@Fodules
Copy link

Fodules commented Feb 9, 2024

This works fine for me:

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

works for me as well

@nikolaydubina
Copy link

this is incredible 👍

@nikolaydubina
Copy link

nikolaydubina commented Feb 17, 2024

I added more scripts for https://github.com/nikolaydubina/twitter-remover

  • removing tweets
  • removing retweets
  • removing replies
  • removing followers

@skibideetoilet
Copy link

is there one that likes and unlikes the tweets in your likes that for some reason are unliked but haven't been removed?

@schmutzie
Copy link

schmutzie commented Feb 22, 2024 via email

@LetyEspinosa19
Copy link

It does not work for me, I also tried this script:

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

And didn't worked also ):

Copy link

ghost commented Mar 13, 2024

Is there anyone who deletes DMs chat?

@NewWestSon
Copy link

It didn't work for me, so I tried waiting 100 milliseconds and it seemed to work, odd.
setInterval(() => {
for (const d of document.querySelectorAll('div[data-testid="unlike"]')) {
d.click()
}
window.scrollTo(0, document.body.scrollHeight)
setTimeout(pass, 100);
}, 1000)

@Anon14j1
Copy link

Anon14j1 commented Apr 5, 2024

function getAllTweetButtons() { return Array.from(document.querySelectorAll('.tweet-interact-favorite')); }

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

async function processTweet(tweetButton) { tweetButton.focus(); tweetButton.click(); // Click to like (if not liked) or unlike (if liked) await wait(1000); // Reduced wait time

// Check if the tweet is still liked, if so, click again to unlike if (tweetButton.classList.contains('tweet-interact-favorited')) { tweetButton.click(); // Click again to ensure it's unliked await wait(500); // Reduced wait time console.log('Ensured tweet is unliked'); } else { console.log('Processed tweet'); } }

async function removeAll() { let count = 0; let tweetButtons = getAllTweetButtons();

while (count < 500 && tweetButtons.length > 0) { for (let i = 0; i < tweetButtons.length && count < 500; i++) { await processTweet(tweetButtons[i]); count++; if (count % 50 === 0) { console.log('Waiting to prevent rate-limiting'); await wait(15000); // Wait to prevent rate-limiting } }

// Scroll to the bottom of the page to load more tweets
window.scrollTo(0, document.body.scrollHeight);
await wait(3000); // Wait for more tweets to load
tweetButtons = getAllTweetButtons(); // Refresh the list of tweet buttons

}

console.log('Finished, count =', count); }

removeAll();

Fix for shadow likes on feed Works with Old Twitter layouts Modify the rate limiting waiting as neccessary

Im getting a Promise error when i try this
"promise {: undefined}"

@nafal9aadi
Copy link

In my account, I have 53 unavailable posts that exist only as a number, and as for likes, they are 66 unavailable likes, and sometimes they are 99 likes. What is the solution to make the same number 0 in the posts and likes tab? I have tried all available solutions and have been suffering from this issue for more than a year and have not found a solution?!
Screenshot_٢٠٢٤٠٥١٤-٠٢١٧٠٥_X
Screenshot_٢٠٢٤٠٥١٤-٠٢١٦٥٧_X

@PrinOrange
Copy link

It takes new frontend version and use this code please

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

@softyoda
Copy link

Hi, is there only ways to delete them, not download them somewhere ? I just want to ctrl+f among my 10000+ liked tweet, but it is impossible, the infinite scrool broke after 200 loaded tweets, and I think it's by design.

@nafal9aadi
Copy link

It takes new frontend version and use this code please

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

What did you mean by "new version of the interface"?

@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.

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