The problem
Whether you're rebranding, cleaning up an old account, or just starting fresh, deleting thousands of tweets one by one is not an option. X provides no bulk delete feature. Services like TweetDelete work β but they charge β¬6β15 per month for something that should be free.
The free solution
We built a JavaScript script that runs directly in your browser console. It scrolls your profile automatically, identifies your own tweets and reposts, and deletes them one by one. No installation, no API key, no external service β just your browser.
We used it ourselves to clean an old X account before relaunching @Sublime_Arts_ β it deleted hundreds of posts without a single issue.
How to use it β 3 steps
What it does
Progress is logged in real time to the console. Stops automatically after 8 empty scrolls (end of feed). Re-run if X lazy-loads more posts.
The script
const sleep = ms => new Promise(r => setTimeout(r, ms));
async function cleanupXProfile() {
let repostsRemoved = 0, tweetsDeleted = 0, emptyScrolls = 0;
const getCol = () =>
document.querySelector('[data-testid="primaryColumn"]') ||
document.querySelector('main') || document;
const closeMenu = async () => {
document.dispatchEvent(new KeyboardEvent('keydown',
{ key: 'Escape', keyCode: 27, bubbles: true }));
await sleep(250);
const h = getCol().querySelector('h1, h2');
if (h) h.click(); await sleep(200);
};
console.log('π§Ή X Profile Cleanup started...');
while (emptyScrolls < 8) {
let actions = 0; const col = getCol();
for (const btn of [...col.querySelectorAll('button')]
.filter(b => b.innerHTML.includes('M4.75 3.79l4.603'))) {
try {
btn.click(); await sleep(700);
const c = document.querySelector('[data-testid="unretweetConfirm"]');
if (c) { c.click(); repostsRemoved++; actions++;
console.log(`β©οΈ Repost removed #${repostsRemoved}`); await sleep(900);
} else await closeMenu();
} catch { await closeMenu(); }
}
for (const btn of [...col.querySelectorAll('button')]
.filter(b => b.innerHTML.includes('M3 12c0-1.1.9-2 2-2s2'))) {
await closeMenu(); await sleep(300);
try {
btn.click(); await sleep(800);
const del = [...document.querySelectorAll('[role="menuitem"]')]
.find(i => i.textContent.trim()
.match(/^(Delete|Elimina|Supprimer|LΓΆschen|Eliminar|Π£Π΄Π°Π»ΠΈΡΡ|ει€|μμ )$/));
if (del) {
del.click(); await sleep(700);
const ok = document.querySelector('[data-testid="confirmationSheetConfirm"]');
if (ok) { ok.click(); tweetsDeleted++; actions++;
console.log(`ποΈ Tweet deleted #${tweetsDeleted}`); await sleep(1200);
} else await closeMenu();
} else await closeMenu();
} catch { await closeMenu(); }
}
if (actions === 0) { emptyScrolls++;
console.log(`Scrolling... (${emptyScrolls}/8)`);
} else emptyScrolls = 0;
window.scrollBy(0, 900); await sleep(2000);
}
console.log(`\nβ
Done! Reposts: ${repostsRemoved} | Tweets: ${tweetsDeleted}`);
}
cleanupXProfile();About
Patrick Chen β indie developer behind Sublimearts.io. Built this while cleaning up my own X account before the relaunch.