Remove all your likes on X, or only your most recent ones, without giving any app access to your account.
// Responsive Muse · Unlike All Posts on X (Twitter) · v1.1 · 2026-10-22
// Removes your likes, newest first: all of them, or only the latest LIMIT. Runs only on
// x.com/i/history/likes. It asks before changing anything, then clicks the same heart button
// you would, at a human pace. It makes no network requests of its own and never reads
// cookies, passwords or tokens.
(async () => {
const TOOL = 'Unlike all posts';
const LIMIT = 0; // only the latest this many posts (0 = all)
const DELAY = 1500; // pause between unlikes, in milliseconds. X limits fast actions: don't go lower.
if (!/^(x|twitter)\.com$/.test(location.hostname) || location.pathname !== '/i/history/likes') {
alert('Open your likes first (signed in):\nhttps://x.com/i/history/likes');
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));
const idOf = art => ((art.querySelector('a[href*="/status/"] time')?.closest('a')?.getAttribute('href') || '')
.match(/\/status\/(\d+)/) || [])[1];
const done = new Set(); // X keeps unliked posts on screen, so remember which ones are handled
// 1. Ask (X loads likes as you scroll, so the total isn't known in advance)
const scope = LIMIT ? `your latest ${LIMIT} likes` : 'all your likes, newest first';
if (!await panel.ask(`Unlike ${scope}?\nThis can't be undone. You can stop at any time.`, LIMIT ? `Unlike ${LIMIT}` : 'Unlike all')) {
panel.finish('Cancelled. Nothing was changed.');
return 0;
}
// 2. Unlike one by one, scrolling to load more
let stop = false;
panel.stoppable(() => { stop = true; });
window.scrollTo(0, 0);
await sleep(DELAY);
let count = 0, idle = 0;
while (!stop && idle < 4 && !(LIMIT && count >= LIMIT)) {
const art = [...document.querySelectorAll('article[data-testid="tweet"]')]
.find(a => a.querySelector('button[data-testid="unlike"]') && !done.has(idOf(a)));
if (!art) {
window.scrollBy(0, window.innerHeight * 0.8); // load more
await sleep(DELAY);
idle++;
continue;
}
idle = 0;
done.add(idOf(art));
art.scrollIntoView({ block: 'center' });
art.querySelector('button[data-testid="unlike"]').click();
count++;
panel.text(`Unliking… ${count}${LIMIT ? ` of ${LIMIT}` : ''}`);
await sleep(DELAY);
}
if (!count && !stop) {
panel.finish('You have no liked posts to unlike.');
return 0;
}
panel.finish(`${stop ? 'Stopped' : 'Done'}: unliked ${many(count, 'post')}.\nReload the page to see the result.`);
return count;
})();
Bookmarklet: drag the middle button to your bookmarks bar, open x.com and click the bookmark. It runs the same script with the options above.
- Sends nothing anywhere. No fetch, XHR or external scripts. The only network activity is x.com itself loading your list.
- Never touches your login. It doesn't read cookies, passwords, tokens or browser storage.
- Only runs on x.com. Anywhere else it stops before doing anything.
- Short and readable. 101 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:
d8c694d16c5457a9…
Verify it yourself in 30 seconds
- Read it: every line is above, with comments.
- Search the code for
fetch,XMLHttpRequest,cookie,localStorageoreval. None of them appear. - Open the Network tab before running it: you'll only see requests to x.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".
How to use it
- Open x.com/i/history/likes while signed in.
- Open the console: F12 or Ctrl+Shift+J (Cmd+Option+J on Mac). If Chrome asks, type
allow pastingand press Enter. - Choose “Only the latest” if you want a partial cleanup, copy the script, paste it in the console and press Enter. A small box appears on the page: click Unlike to confirm. You can stop it at any time.
A safe pace
The script clicks the heart button on each liked post, newest first, waiting 1.5 seconds between clicks. That is slower than paid tools but keeps your account clear of X’s limits on automated activity. In our test, “Only the latest 5” removed exactly the five newest likes and left every older like untouched.
FAQ
Will X block my account?
X limits accounts that act too fast. The script waits 1.5 seconds between unlikes, like a person would. For thousands of likes, run it in batches with "Only the latest".
Can I remove only recent likes?
Yes. Likes are listed newest first, so "Only the latest 50" removes your 50 most recent likes and leaves the rest.
Do people get notified?
No. Removing a like doesn't notify anyone.
