Skip to content

Instantly share code, notes, and snippets.

@sbolel
Last active November 4, 2025 12:10
Show Gist options
  • Select an option

  • Save sbolel/a2b2bfde16b3ab185fbc2e2049240abc to your computer and use it in GitHub Desktop.

Select an option

Save sbolel/a2b2bfde16b3ab185fbc2e2049240abc to your computer and use it in GitHub Desktop.
Instagram Comment Activity Deleter: Automate the deletion of all your Instagram comments from the 'Your Activity' section. Perfect for quick digital clean-up.
/**
* This script automates the process of deleting your own Instagram comments.
* It deletes comments in batches to avoid hitting rate limits or breaking the page.
*
* WARNING: This function directly manipulates the DOM and depends on the current HTML
* structure of Instagram's website to work. If Instagram implements changes to the
* activity page layout, structure, or functionality, this script may break or cause
* unexpected behavior. Use at your own risk and always review code before running it.
*
* How to use:
* 1. Navigate to the Instagram comments page by going to:
* https://www.instagram.com/your_activity/interactions/comments
* 2. Open the developer console in your web browser:
* - Chrome/Firefox: Press Ctrl+Shift+J (Windows/Linux) or Cmd+Option+J (Mac)
* - Safari: Enable the Develop menu in Safari's Advanced preferences, then press Cmd+Option+C
* 3. Copy and paste this entire script into the console and press Enter to run it.
*
* How to navigate to the comments page on instagram.com:
* 1. Log in to Instagram on a desktop browser.
* 2. Go to your profile by clicking on the profile icon at the bottom right.
* 3. Click on "Your Activity" in the menu.
* 4. Select "Interactions" and then "Comments".
* 5. Follow the usage steps above to run this script.
*/
;(async function () {
// Constants
/** @const {number} - The number of comments to delete in each batch. */
const DELETION_BATCH_SIZE = 3
/** @const {number} - The delay between actions in milliseconds. */
const DELAY_BETWEEN_ACTIONS_MS = 1000
/** @const {number} - The delay between clicking the checkboxes in milliseconds. */
const DELAY_BETWEEN_CHECKBOX_CLICKS_MS = 300
/** @const {number} - The maximum number of retries for waiting operations */
const MAX_RETRIES = 60
/**
* Utility function that delays execution for a given amount of time.
* @param {number} ms - The milliseconds to delay.
* @returns {Promise<void>} A promise that resolves after the specified delay.
*/
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
/**
* Utility function that waits for an element to appear in the DOM before resolving.
* @param {string} selector - The CSS selector of the element to wait for.
* @param {number} [timeout=30000] - The maximum time to wait in milliseconds.
* @returns {Promise<Element>} A promise that resolves with the found element.
* @throws {Error} If the element is not found within the timeout period.
*/
const waitForElement = async (selector, timeout = 30000) => {
const startTime = Date.now()
while (Date.now() - startTime < timeout) {
const element = document.querySelector(selector)
if (element) return element
await delay(100)
}
throw new Error(`Element with selector "${selector}" not found within ${timeout}ms`)
}
/**
* Utility function that clicks on a given element.
* @param {Element} element - The element to click.
* @throws {Error} If the element is not found.
*/
const clickElement = async (element) => {
if (!element) throw new Error('Element not found')
element.click()
}
/**
* Waits for the "Select" button to reappear after the page loads more comments
* following the deletion of a batch of comments when the "Select" button
* is hidden while a spinner indicates that more comments are loading.
* @returns {Promise<void>} A promise that resolves when the select button reappears.
* @throws {Error} If the select button is not found after maximum retries.
*/
const waitForSelectButton = async () => {
for (let i = 0; i < MAX_RETRIES; i++) {
const buttonCount = document.querySelectorAll('[role="button"]')?.length
if (buttonCount > 1) return
await delay(1000)
}
throw new Error('Select button not found after maximum retries')
}
/**
* Deletes the currently selected comments.
* @returns {Promise<void>} A promise that resolves when the comments are deleted.
*/
const deleteSelectedComments = async () => {
try {
const deleteButton = await waitForElement('[aria-label="Delete"]')
await clickElement(deleteButton)
await delay(DELAY_BETWEEN_ACTIONS_MS)
const confirmButton = await waitForElement('button[tabindex="0"]')
await clickElement(confirmButton)
} catch (error) {
console.error('Error during comment deletion:', error.message)
}
}
/**
* Deletes all user comments by selecting comments in batches.
* @returns {Promise<void>} A promise that resolves when all comments are deleted.
*/
const deleteActivity = async () => {
try {
while (true) {
const [, selectButton] = document.querySelectorAll('[role="button"]')
if (!selectButton) throw new Error('Select button not found')
await clickElement(selectButton)
await delay(DELAY_BETWEEN_ACTIONS_MS)
const checkboxes = document.querySelectorAll('[aria-label="Toggle checkbox"]')
if (checkboxes.length === 0) {
console.log('No more comments to delete')
break
}
for (let i = 0; i < Math.min(DELETION_BATCH_SIZE, checkboxes.length); i++) {
await clickElement(checkboxes[i])
await delay(DELAY_BETWEEN_CHECKBOX_CLICKS_MS)
}
await delay(DELAY_BETWEEN_ACTIONS_MS)
await deleteSelectedComments()
await delay(DELAY_BETWEEN_ACTIONS_MS)
await waitForSelectButton()
await delay(DELAY_BETWEEN_ACTIONS_MS)
}
} catch (error) {
console.error('Error in deleteActivity:', error.message)
}
}
// Start the deletion process
try {
await deleteActivity()
console.log('Activity deletion completed')
} catch (error) {
console.error('Fatal error:', error.message)
}
})()
@111100001
Copy link

Written on 13/7/2025

I've used chatgpt to update the code so it can be used at this point in time, it uses selenium so be sure to have selenium installed, run this on python:

import logging
import pathlib
import platform
import random
import sys
import time

from selenium import webdriver
from selenium.common.exceptions import NoSuchWindowException, TimeoutException, StaleElementReferenceException
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait

# Logging setup
logging.basicConfig(format="[%(levelname)s] Instagram Cleaner: %(message)s", level=logging.INFO)

# Constants
MODE = 1  # 1 = comments, 2 = likes
CHECK_EVERY = -1
LIKES_URL = "https://www.instagram.com/your_activity/interactions/likes"
COMMENTS_URL = "https://www.instagram.com/your_activity/interactions/comments"
AT_ONCE_DELETE = 50  # Maximum items to delete in one go

logging.info("Starting script...")

try:
    options = Options()
    wd = pathlib.Path().absolute()

    if platform.system() == "Windows":
        options.add_argument(f"user-data-dir={wd}\\chrome-profile")
    else:
        options.add_argument("user-data-dir=chrome-profile")

    driver = webdriver.Chrome(options=options)
    logging.info("Chrome browser launched")

    URL = COMMENTS_URL if MODE == 1 else LIKES_URL
    driver.get(URL)
    logging.info(f"Opening page: {URL}")

    # Wait for user to log in manually
    while True:
        if driver.current_url.startswith(URL):
            logging.info("Login detected")
            break
        try:
            logging.info("Waiting for login (log in from the browser and don't click anything else)...")
            wait = WebDriverWait(driver, 60)

            def is_not_now_button_present(driver):
                try:
                    div = driver.find_element(By.CSS_SELECTOR, "div[role='button']")
                    return div.text in ["Not now", "Plus tard"]
                except:
                    return False

            wait.until(is_not_now_button_present)
            driver.find_element(By.CSS_SELECTOR, "div[role='button']").send_keys(Keys.ENTER)
            logging.info("Clicked 'Not now'")
            break
        except TimeoutException:
            pass

    # Main loop
    while True:
        is_clicked_select = False
        while not is_clicked_select:
            logging.info("Waiting for elements to load...")
            time.sleep(2)
            elements = driver.find_elements(By.CSS_SELECTOR, 'span[data-bloks-name="bk.components.Text"]')
            for el in elements:
                if el.text in ["Select", "Sélectionner"]:
                    if any(txt.text in ["No results", "Aucun résultat"] for txt in elements):
                        logging.info("No items found. Finished.")
                        driver.quit()
                        sys.exit(0)
                    driver.execute_script("arguments[0].click();", el)
                    logging.info("Clicked 'Select'")
                    is_clicked_select = True
                    break

        selected_count = 0
        attempts = 0
        refreshed = False

        while selected_count == 0:
            if refreshed:
                break
            time.sleep(1)
            for icon in driver.find_elements(By.CSS_SELECTOR, 'div[data-bloks-name="ig.components.Icon"]'):
                try:
                    if icon.get_attribute("style").startswith('mask-image: url("https://i.instagram.com/static/images/bloks/icons/generated/circle__outline'):
                        driver.execute_script("arguments[0].click();", icon)
                        selected_count += 1
                        logging.info(f"Selected item (Total: {selected_count})")
                        if selected_count >= AT_ONCE_DELETE:
                            break
                except StaleElementReferenceException:
                    continue

            attempts += 1
            if attempts > 10:
                if CHECK_EVERY == -1:
                    logging.warning("Deletion limit hit. Instagram is temporarily blocking actions.")
                    while True:
                        wait_hours = input("Check again after how many hours? (default: 1) [x]: ")
                        try:
                            wait_hours = float(wait_hours or "1")
                            break
                        except ValueError:
                            pass
                    CHECK_EVERY = int(wait_hours * 3600)
                    logging.info(f"Waiting {wait_hours} hour(s) before retrying.")
                time.sleep(random.uniform(CHECK_EVERY * 0.9, CHECK_EVERY * 1.1))
                logging.info("Refreshing page...")
                driver.refresh()
                refreshed = True
                continue
        if refreshed:
            continue

        # Click Delete/Unlike
        for span in driver.find_elements(By.CSS_SELECTOR, 'span[data-bloks-name="bk.components.TextSpan"]'):
            if span.text in (["Delete", "Supprimer"] if MODE == 1 else ["Unlike", "Je n’aime plus"]):
                time.sleep(1)
                driver.execute_script("arguments[0].click();", span)
                logging.info(f"Clicked '{span.text}'")
                break

        # Confirm the deletion
        confirmed = False
        while not confirmed:
            time.sleep(1)
            for btn in driver.find_elements(By.CSS_SELECTOR, 'div[role="dialog"] button'):
                try:
                    confirmation_text = btn.find_element(By.CSS_SELECTOR, "div").text
                    if confirmation_text in ["Delete", "Unlike", "Supprimer", "Je n’aime plus"]:
                        driver.execute_script("arguments[0].click();", btn)
                        logging.info(f"Confirmed '{confirmation_text}'")
                        confirmed = True
                        break
                except StaleElementReferenceException:
                    continue

except KeyboardInterrupt:
    logging.info("Script manually interrupted.")
    driver.quit()
    sys.exit(0)
except NoSuchWindowException:
    logging.exception("Browser window was closed.")
    sys.exit(1)
except Exception as e:
    logging.exception("Unexpected error:")
    try:
        driver.quit()
    except:
        pass
    sys.exit(1)

works as of this comment's date

@FinnishArmy
Copy link

As of current, if I run the script it will select the first comment, unselect it then select the next two. It wont find the Delete button element, presumably because it was changed?
Error during comment deletion: Element with selector "button[tabindex="0"]" not found within 30000ms

@llama95
Copy link

llama95 commented Aug 11, 2025

your code does not work. delete this or fix this quit wasting our time

@ScreenY55
Copy link

Button was not found wtf?

@pawan-saxena
Copy link

pawan-saxena commented Aug 29, 2025

try below it works but it selects 20 comments at max and you need to just click on delete then confirmation dialogue comes up and you confirm.

;(async function () {
const DELETION_BATCH_SIZE = 20
const DELAY_BETWEEN_ACTIONS_MS = 3000
const DELAY_BETWEEN_CHECKBOX_CLICKS_MS = 100
const MAX_RETRIES = 100

const delay = ms => new Promise(res => setTimeout(res, ms))

async function slowScrollDown(totalDurationMs = 6000, steps = 6) {
const totalHeight = document.body.scrollHeight
const stepHeight = totalHeight / steps
const stepDelay = totalDurationMs / steps
for (let i = 0; i < steps; i++) {
window.scrollBy(0, stepHeight)
await delay(stepDelay)
}
}

async function clickElement(el) {
if (!el) throw new Error("Element not found")
el.click()
}

async function getSelectButton() {
const elems = [...document.querySelectorAll('[role="button"], span, div')]
return elems.find(el => el.innerText.trim() === "Select") || null
}

async function getDeleteButton() {
const elems = [...document.querySelectorAll("span, div, button")]
return elems.find(el => el.innerText.trim() === "Delete") || null
}

async function handleWrongMessage() {
const msgs = document.querySelectorAll("div, span, p")
for (const msg of msgs) {
if (msg.textContent.toLowerCase().includes("wrong")) {
const buttons = document.querySelectorAll("button")
for (const btn of buttons) {
if (/^(ok)$/i.test(btn.textContent.trim())) {
await clickElement(btn)
await delay(500)
window.scrollTo(0, 0)
break
}
}
break
}
}
}

async function waitForSelectButton() {
for (let i = 0; i < MAX_RETRIES; i++) {
const selectBtn = await getSelectButton()
if (selectBtn) return selectBtn
await delay(1000)
}
throw new Error("Select button not found after retries")
}

async function deleteSelectedComments() {
try {
const deleteButton = await getDeleteButton()
if (!deleteButton) throw new Error("Delete button not found")
await clickElement(deleteButton)
await delay(DELAY_BETWEEN_ACTIONS_MS)
const confirmButton = document.querySelector('button[tabindex="0"]')
if (confirmButton) await clickElement(confirmButton)
window.scrollTo(0, 0)
await handleWrongMessage()
} catch (err) {
console.error("Error during comment deletion:", err.message)
}
}

async function deleteActivity() {
while (true) {
const selectButton = await getSelectButton()
if (!selectButton) throw new Error("Select button not found")
await clickElement(selectButton)
await delay(DELAY_BETWEEN_ACTIONS_MS)
let checkboxes = document.querySelectorAll('[aria-label="Toggle checkbox"]')
let retries = 0
while (checkboxes.length < DELETION_BATCH_SIZE && retries < 10) {
await slowScrollDown(2000, 4)
await delay(2000)
checkboxes = document.querySelectorAll('[aria-label="Toggle checkbox"]')
retries++
}
if (checkboxes.length === 0) {
console.log("✅ No more comments to delete")
return
}
for (let i = 0; i < Math.min(DELETION_BATCH_SIZE, checkboxes.length); i++) {
await clickElement(checkboxes[i])
await delay(DELAY_BETWEEN_CHECKBOX_CLICKS_MS)
}
await delay(DELAY_BETWEEN_ACTIONS_MS)
await deleteSelectedComments()
await delay(DELAY_BETWEEN_ACTIONS_MS)
await waitForSelectButton()
await delay(DELAY_BETWEEN_ACTIONS_MS)
await handleWrongMessage()
}
}

try {
await deleteActivity()
console.log("🎉 Deletion finished")
} catch (err) {
console.error("Fatal error:", err.message)
}
})()

@kimonneuhoff
Copy link

kimonneuhoff commented Sep 11, 2025

Here is a revised version that also retries upon errors during deletion and is substantially faster. Make sure you set the display language to English for it to work

/**
 * This script automates the process of deleting your own Instagram comments.
 * It deletes comments in batches to avoid hitting rate limits or breaking the page.
 *
 * WARNING: This function directly manipulates the DOM and depends on the current HTML
 *  structure of Instagram's website to work. If Instagram implements changes to the
 *  activity page layout, structure, or functionality, this script may break or cause
 *  unexpected behavior. Use at your own risk and always review code before running it.
 *
 * How to use:
 * 1. Navigate to the Instagram comments page by going to:
 *    https://www.instagram.com/your_activity/interactions/comments
 * 2. Open the developer console in your web browser:
 *    - Chrome/Firefox: Press Ctrl+Shift+J (Windows/Linux) or Cmd+Option+J (Mac)
 *    - Safari: Enable the Develop menu in Safari's Advanced preferences, then press Cmd+Option+C
 * 3. Copy and paste this entire script into the console and press Enter to run it.
 *
 * How to navigate to the comments page on instagram.com:
 * 1. Log in to Instagram on a desktop browser.
 * 2. Go to your profile by clicking on the profile icon at the bottom right.
 * 3. Click on "Your Activity" in the menu.
 * 4. Select "Interactions" and then "Comments".
 * 5. Follow the usage steps above to run this script.
 */
(async function () {
  // Constants
  /** @const {number} - The number of comments to delete in each batch. */
  const DELETION_BATCH_SIZE = 20
  /** @const {number} - The delay between actions in milliseconds. */
  const DELAY_BETWEEN_ACTIONS_MS = 700
  /** @const {number} - The delay between clicking the checkboxes in milliseconds. */
  const DELAY_BETWEEN_CHECKBOX_CLICKS_MS = 100
  /** @const {number} - The maximum number of retries for waiting operations */
  const MAX_RETRIES = 60

  /**
   * Utility function that delays execution for a given amount of time.
   * @param {number} ms - The milliseconds to delay.
   * @returns {Promise<void>} A promise that resolves after the specified delay.
   */
  const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms))

  /**
   * Utility function that waits for an element to appear in the DOM before resolving.
   * @param {string} selector - The CSS selector of the element to wait for.
   * @param {number} [timeout=30000] - The maximum time to wait in milliseconds.
   * @returns {Promise<Element>} A promise that resolves with the found element.
   * @throws {Error} If the element is not found within the timeout period.
   */
  const waitForElement = async (tag, selector, timeout = 30000) => {
    console.log(`Waiting for ${tag} element with selector "${selector}"...`)

    const startTime = Date.now()
    while (Date.now() - startTime < timeout) {
      const element = Array.from(document.querySelectorAll(tag)).find((el) => el.textContent === selector)
      if (element) {
        console.log(`Found ${tag} element with selector "${selector}"`)
        return element
      }
      await delay(100)
    }
        
    throw new Error(`Element with selector "${selector}" not found within ${timeout}ms`)
  }

  /**
   * Utility function that clicks on a given element.
   * @param {Element} element - The element to click.
   * @throws {Error} If the element is not found.
   */
  const clickElement = async (element) => {
    if (!element) throw new Error('Element not found')
    element.click()
  }

  /**
   * Deletes the currently selected comments.
   * @returns {Promise<void>} A promise that resolves when the comments are deleted.
   */
  const deleteSelectedComments = async () => {
    try {
      const deleteButton = await waitForElement('span', 'Delete')
      await clickElement(deleteButton)
      await delay(DELAY_BETWEEN_ACTIONS_MS)
      const confirmButton = await waitForElement('button', 'Delete')
      await clickElement(confirmButton)
      
      await delay(5000)

      // Handle Instagram's error dialog
      const okButton = await waitForElement('button', 'OK', 10000)
      await clickElement(okButton)

      console.log('Deleted selected comments')
    } catch (error) {
      console.error('Error during comment deletion:', error.message)
    }
  }

  /**
   * Deletes all user comments by selecting comments in batches.
   * @returns {Promise<void>} A promise that resolves when all comments are deleted.
   */
  const deleteActivity = async () => {
    try {
        console.log('Starting deletion process...')
        const selectButton = await waitForElement('span', 'Select')
        if (!selectButton) throw new Error('Select button not found')

        await clickElement(selectButton)
        await delay(DELAY_BETWEEN_ACTIONS_MS)

        const checkboxes = document.querySelectorAll('[aria-label="Toggle checkbox"]')
        if (checkboxes.length === 0) {
          console.log('No comments to delete... retrying after delay')
          await delay(DELAY_BETWEEN_ACTIONS_MS)
          return deleteActivity();
        }

        for (let i = 0; i < Math.min(DELETION_BATCH_SIZE, checkboxes.length); i++) {
          await clickElement(checkboxes[i])
          await delay(DELAY_BETWEEN_CHECKBOX_CLICKS_MS)
        }

        await delay(DELAY_BETWEEN_ACTIONS_MS)
        await deleteSelectedComments()
        
        deleteActivity();
    } catch (error) {
      console.error('Error in deleteActivity:', error.message)
    }
  }

  // Start the deletion process
  try {
    deleteActivity()
  } catch (error) {
    console.error('Fatal error:', error.message)
  }
})()

@GuiGuidugli
Copy link

(async function () {
  // Constants
  /** @const {number} - The number of comments to delete in each batch. */
  const DELETION_BATCH_SIZE = 20
  /** @const {number} - The delay between actions in milliseconds. */
  const DELAY_BETWEEN_ACTIONS_MS = 700
  /** @const {number} - The delay between clicking the checkboxes in milliseconds. */
  const DELAY_BETWEEN_CHECKBOX_CLICKS_MS = 100
  /** @const {number} - The maximum number of retries for waiting operations */
  const MAX_RETRIES = 60

  /**
   * Utility function that delays execution for a given amount of time.
   * @param {number} ms - The milliseconds to delay.
   * @returns {Promise<void>} A promise that resolves after the specified delay.
   */
  const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms))

  /**
   * Utility function that waits for an element to appear in the DOM before resolving.
   * @param {string} selector - The CSS selector of the element to wait for.
   * @param {number} [timeout=30000] - The maximum time to wait in milliseconds.
   * @returns {Promise<Element>} A promise that resolves with the found element.
   * @throws {Error} If the element is not found within the timeout period.
   */
  const waitForElement = async (tag, selector, timeout = 30000) => {
    console.log(`Waiting for ${tag} element with selector "${selector}"...`)

    const startTime = Date.now()
    while (Date.now() - startTime < timeout) {
      const element = Array.from(document.querySelectorAll(tag)).find((el) => el.textContent === selector)
      if (element) {
        console.log(`Found ${tag} element with selector "${selector}"`)
        return element
      }
      await delay(100)
    }
        
    throw new Error(`Element with selector "${selector}" not found within ${timeout}ms`)
  }

  /**
   * Utility function that clicks on a given element.
   * @param {Element} element - The element to click.
   * @throws {Error} If the element is not found.
   */
  const clickElement = async (element) => {
    if (!element) throw new Error('Element not found')
    element.click()
  }

  /**
   * Deletes the currently selected comments.
   * @returns {Promise<void>} A promise that resolves when the comments are deleted.
   */
  const deleteSelectedComments = async () => {
    try {
      const deleteButton = await waitForElement('span', 'Delete')
      await clickElement(deleteButton)
      await delay(DELAY_BETWEEN_ACTIONS_MS)
      const confirmButton = await waitForElement('button', 'Delete')
      await clickElement(confirmButton)
      
      await delay(5000)

      // Handle Instagram's error dialog
      const okButton = await waitForElement('button', 'OK', 10000)
      await clickElement(okButton)

      console.log('Deleted selected comments')
    } catch (error) {
      console.error('Error during comment deletion:', error.message)
    }
  }

  /**
   * Deletes all user comments by selecting comments in batches.
   * @returns {Promise<void>} A promise that resolves when all comments are deleted.
   */
  const deleteActivity = async () => {
    try {
        console.log('Starting deletion process...')
        const selectButton = await waitForElement('span', 'Select')
        if (!selectButton) throw new Error('Select button not found')

        await clickElement(selectButton)
        await delay(DELAY_BETWEEN_ACTIONS_MS)

        const checkboxes = document.querySelectorAll('[aria-label="Toggle checkbox"]')
        if (checkboxes.length === 0) {
          console.log('No comments to delete... retrying after delay')
          await delay(DELAY_BETWEEN_ACTIONS_MS)
          return deleteActivity();
        }

        for (let i = 0; i < Math.min(DELETION_BATCH_SIZE, checkboxes.length); i++) {
          await clickElement(checkboxes[i])
          await delay(DELAY_BETWEEN_CHECKBOX_CLICKS_MS)
        }

        await delay(DELAY_BETWEEN_ACTIONS_MS)
        await deleteSelectedComments()
        
        deleteActivity();
    } catch (error) {
      console.error('Error in deleteActivity:', error.message)
    }
  }

  // Start the deletion process
  try {
    deleteActivity()
  } catch (error) {
    console.error('Fatal error:', error.message)
  }
})()

This is working so far. Will let you know if it works on one go.

@O1O1O1O
Copy link

O1O1O1O commented Sep 23, 2025

I just used the script from @kimonneuhoff and it worked great with no errors or timeouts. Also by changing "Delete" to "Unlike" it worked for deleting my Like history. I also used the trick of opening two tabs and sorting the second by Oldest First and starting another delete script. That allowed me to delete twice as fast. Thanks everyone who has contributed to keeping this script working!

@drHyperion451
Copy link

drHyperion451 commented Sep 29, 2025

Thank you for the script it works perfectly. I would love to see a "Whitelist" implementation as well, so the script will skip some comments made on some post made by some users.

Also I think the optimal whay to handle this is to create a normal github repo, at least we could maintain this better.

@O1O1O1O
Copy link

O1O1O1O commented Sep 29, 2025

Yeah that would be a useful feature which I was also thinking about as I watched to delete all my comments on my personal friend's posts. But too late for me now - I just erased years worth of history but ultimately I don't think anyone cares. It's just Instagram after all.

@GuiGuidugli
Copy link

GuiGuidugli commented Oct 6, 2025

Forgot to update but yes, it did work on one go. Thank you, @kimonneuhoff!

@skipshean
Copy link

@GuiGuidugli 's script worked for me on about 7 years of comments in one go. Thx...

@GuiGuidugli
Copy link

@GuiGuidugli 's script worked for me on about 7 years of comments in one go. Thx...

It's actually @kimonneuhoff's script!

@ajohnclark
Copy link

Thank you so very much.

@AtomicRobotMan0101
Copy link

This is the version to unlike everything

By all the gods I despise Instagram.

      (async function () {
        const UNLIKE_BATCH_SIZE = 10;
        const DELAY_BETWEEN_ACTIONS_MS = 800;
        const DELAY_BETWEEN_CHECKBOX_CLICKS_MS = 120;
        const MAX_RETRIES = 60;
      
        const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
      
        const waitForElement = async (tag, text, timeout = 30000) => {
          const start = Date.now();
          while (Date.now() - start < timeout) {
            const el = Array.from(document.querySelectorAll(tag)).find(
              (x) => x.textContent.trim() === text
            );
            if (el) return el;
            await delay(200);
          }
          throw new Error(`Element "${text}" not found`);
        };
      
        const clickElement = async (el) => {
          if (!el) throw new Error("Element not found");
          el.scrollIntoView({ behavior: "smooth", block: "center" });
          el.click();
          await delay(DELAY_BETWEEN_ACTIONS_MS);
        };
      
        const unlikeSelectedPosts = async () => {
          try {
            const unlikeButton = await waitForElement("span", "Unlike");
            await clickElement(unlikeButton);
            const confirmButton = await waitForElement("button", "Unlike");
            await clickElement(confirmButton);
            console.log("Batch unliked.");
          } catch (err) {
            console.error("Unlike error:", err.message);
          }
        };
      
        const unlikeActivity = async () => {
          try {
            console.log("Starting unlike process...");
            const selectButton = await waitForElement("span", "Select");
            await clickElement(selectButton);
            await delay(DELAY_BETWEEN_ACTIONS_MS);
      
            const checkboxes = document.querySelectorAll('[aria-label="Toggle checkbox"]');
            if (checkboxes.length === 0) {
              console.log("No more liked posts found.");
              return;
            }
      
            for (let i = 0; i < Math.min(UNLIKE_BATCH_SIZE, checkboxes.length); i++) {
              await clickElement(checkboxes[i]);
              await delay(DELAY_BETWEEN_CHECKBOX_CLICKS_MS);
            }
      
            await delay(DELAY_BETWEEN_ACTIONS_MS);
            await unlikeSelectedPosts();
      
            await delay(5000); // Wait before continuing
            await unlikeActivity();
          } catch (err) {
            console.error("unlikeActivity error:", err.message);
          }
        };
      
        try {
          await unlikeActivity();
        } catch (fatal) {
          console.error("Fatal:", fatal.message);
        }
      })();

@Afura33
Copy link

Afura33 commented Oct 28, 2025

Stopped working for me :( , I am using @kimonneuhoff script which I found in the comments. I am either stuck in an endless loading deleting screen or it says that it deleted the comments but it actually did not delete them the comments reappear a couple of minutes later. Does someone have an updated script that works? I tried different browsers and changed language to english but still doesn't fix it. Thank youuuu

@AtomicRobotMan0101
Copy link

Sometimes one must force-refresh to update the page occasionally. Instagram seems to be onto us! (I suspect its just caching and our actions are not entirely expected by their mechanisms)

Ensure you are on this page: https://www.instagram.com/your_activity/interactions/comments

Also ensure you do it SLOWLY. I was most successful with the settings below. I had a LLOOTT of comments and likes to remove.

const DELETION_BATCH_SIZE = 20
const DELAY_BETWEEN_ACTIONS_MS = 700
const DELAY_BETWEEN_CHECKBOX_CLICKS_MS = 100
const MAX_RETRIES = 60

As an aside, I can confirm my trivial mod to the base script to remove likes also works as expected. But do take it easy - I found I had to be quite slow (10 selects, with a decent pause).... just let it percolate. Again, I did find I needed to force-refresh the page occasionally or take a break periodically for 24 hours (again, I suspect caching).

@DigitalCafeTech
Copy link

The code from Sep 13 by @kimonneuhoff worked like a charm for me in Chrome—many thanks! 🙏 I kept an eye on it every so often, occasionally had to click when IG threw an error, but it saved me hours of manual labor!

@Afura33
Copy link

Afura33 commented Oct 29, 2025

@AtomicRobotMan0101 I think it is rather an instagram issue than a script issue though. Today the script was working again, but after running it for an hour the loading screen bug occured again, as you presumed I think that instagram is blocking it, cause even manually deleting the comments doesn't work anymore after the loading screen bug appeared. I have tried everything from changing browsers, refreshing page, deleting caches and cookies, loging out, and so on, nothing seems to fix it. Seems like I have to wait 24 hours everytime this happens. However that's better than nothing, manually deleting all my comments would take ages.

I was using 20 deletion batch size so far, I will try it with 10 or 15 next time.

@AtomicRobotMan0101
Copy link

@Afura33 I believe its simply the way Instagram displays the pages. They are essentially generated and left static for their caches. They don't expect users to come along and obliterate vast hunks of their history.... so I speculate that when we delete many hundreds of comments or likes, we are simply hitting the bottom of their caches and their solutions simply seize up.

Give it overnight and it will work again with refreshed/repopulated data.

I experienced what you have many times during my deletion. Just wait a bit. Be patient and fire it up again the next day.

@Afura33
Copy link

Afura33 commented Oct 30, 2025

@AtomicRobotMan0101 I think you are right, it's probably because of their caches, I reduced the batch size and the time it takes to delete the comments, but the issue still occured. I am just surprised that it works this way, I mean instagram is all about posting pictures and comments and then they make it so hard to delete your comments. I don't understand why they don't do it like facebook since they are both owned by the same company, on facebook you can easily manually delete in bulks of 250 messages ore more which goes pretty fast.

Anyway, I will just run the script every day for 1 hour until everything is deleted, that's still better than doing it manually to be honest.

@AtomicRobotMan0101
Copy link

@Afura33 they do it very deliberately. There is NOTHING that they do which is within your interests.

Their making it difficult for us is a wilful act of bastardry. The reason one can do it the way you'd expect on FB is because a few years ago it blew up into a full-blown worldwide political crisis. FB/meta "offered" this "solution" as a means of "allowing" people to re-take control of their privacy. They, of course, did not make this easy - only available. Notice how many screens you need to jump through and how carefully its hidden (at bottom below the scroll/off-screen, using non-obvious terms, under a sub-menu).... but available...

Facebook/Meta employ these dark patterns across all their sites.

Notice how one can follow something in a single obvious step, but to unfollow takes 2 (or more, with confirmation, depending on where you are). Also notice that the follow/subscribe takes milliseconds with a single click and leaves you right where you were, but the unfollow takes you off-screen, waits, then pauses, then asks for confirmation before further pausing and waiting... then refreshes the damned screen... (more waiting).

Its all deliberate, its all designed that way. Its designed to wear you down.

This is even more reason to remove all likes and comments, if not then close/delete the accounts.

When AI kicks in, expect your every comment to be used in a manner you'll find deeply... discomforting.

@Afura33
Copy link

Afura33 commented Oct 31, 2025

@AtomicRobotMan0101 I wouldn't be surprised about that to be honest, it's weird to think about that a company worth billions can't add a more convinient deletion feature than this, I mean it's really badly made. True, it's a pain in the butt to find where to delete your facebook comments, that goes for a lot of other stuff on facebook too like setting up privacy, it's so badly structured and confusing, it can't be that you have to use google to find basic things on your account...
Yes unfollowing button is pretty well hidden on their page, good luck finding it lol.
I agree, social media started as a nice idea, but now it's just something most people rather wanna get rid off, it turned into a huge marketing data hoarding mess.

@AtomicRobotMan0101
Copy link

@AtomicRobotMan0101 ... it's weird to think about that a company worth billions can't add a more convenient deletion feature than this, I mean it's really badly made.

Again, its deliberate. Their continuous bastardry is well documented.

Meta deliberately makes user control difficult and designs friction into certain actions: https://dl.acm.org/doi/10.1145/3290605.3300838

Privacy interface improvements were rolled out after the Cambridge Analytica scandal (2018) under regulatory pressure, not voluntarily: https://www.theguardian.com/technology/2018/apr/04/facebook-privacy-settings-update-cambridge-analytica

UX timing and confirmation friction are intentional to retain engagement. Documented as “confirmshaming” or “resistance patterns.”: https://dl.acm.org/doi/10.1145/3313831.3376321

Everything you have come up against is continuously tested and refined to be as difficult as possible. They employ psychologists to ensure your actions are influenced and controlled - considerable effort and vast sums are spent to ensure your compliance and control.

You are better off being free of them - there is no such thing as a "casual drug addict" and their systems are deliberately designed to hit your basic-animal dopamine and behaviour-control mechanisms (e.g. Doom Scrolling)

Dark Patterns are ubiquitous on large US websites (I say US, as they are very much the very worst). DP's are a profound evil and once you know what they are, you will see them everywhere.

@phases78
Copy link

phases78 commented Nov 3, 2025

Just used @kimonneuhoff version successfully. Took about 2 hours, I had to do a lot of clicking "ok" through periodic "we failed to delete" errors and sometimes re-paste in the code and rerun but it worked! 7 Years worth. Amazing. Thank you all who have contributed. As one other mentioned, you can easily modify and do likes too. Burning through all those now. Edit: works with story replies too xD

@theshubzworld
Copy link

theshubzworld commented Nov 4, 2025

📌 Instagram Comment Bulk Deletion Script (Python + Selenium)

Description:

Automates the deletion of your Instagram comments (or likes) in batches.

Features interactive prompts for:

- How many selections per batch

- Delay between each selection (sec)

- Delay after each batch deletion (sec)

- Customizable rate-limit retry timer

Progress and batch info shown in the terminal.

import logging
import pathlib
import platform
import random
import sys
import time

from selenium import webdriver
from selenium.common.exceptions import (
NoSuchWindowException,
TimeoutException,
StaleElementReferenceException,
)
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait

logging.basicConfig(format="[%(levelname)s] Instagram Cleaner: %(message)s", level=logging.INFO)

def get_int_input(prompt, default):
while True:
try:
inp = input(f"{prompt} (default: {default}) [x]: ")
if not inp or inp.lower() == "x":
return default
value = int(inp)
if value <= 0:
print("Please enter a positive integer.")
continue
return value
except ValueError:
print("Invalid input. Please enter a number.")

def get_float_input(prompt, default):
while True:
try:
inp = input(f"{prompt} (default: {default}) [x]: ")
if not inp or inp.lower() == "x":
return default
value = float(inp)
if value <= 0:
print("Please enter a positive number.")
continue
return value
except ValueError:
print("Invalid input. Please enter a number.")

print("---- Instagram Bulk Comment Cleaner ----")
SELECTIONS_AT_ONCE = get_int_input("How many comments to select (per batch)?", 5)
DELAY_BETWEEN_SELECTS = get_float_input("Delay (seconds) between each selection?", 2.0)
DELAY_AFTER_BATCH_DELETE = get_float_input("Delay (seconds) after each batch deletion?", 10.0)
CHECK_EVERY = get_float_input("If rate limit hit, wait how many hours before retry? ", 1.0) * 3600
MODE = 1 # 1 = comments, 2 = likes
print("----------------------------------------")

LIKES_URL = "https://www.instagram.com/your_activity/interactions/likes"
COMMENTS_URL = "https://www.instagram.com/your_activity/interactions/comments"

logging.info("Starting script...")

try:
options = Options()
wd = pathlib.Path().absolute()

if platform.system() == "Windows":
    options.add_argument(f"user-data-dir={wd}\\chrome-profile")
else:
    options.add_argument("user-data-dir=chrome-profile")

driver = webdriver.Chrome(options=options)
logging.info("Chrome browser launched")

URL = COMMENTS_URL if MODE == 1 else LIKES_URL
driver.get(URL)
logging.info(f"Opening page: {URL}")

# Wait for user to log in manually
while True:
    if driver.current_url.startswith(URL):
        logging.info("Login detected")
        break
    try:
        logging.info("Waiting for login (log in from the browser and don't click anything else)...")
        wait = WebDriverWait(driver, 60)

        def is_not_now_button_present(driver):
            try:
                div = driver.find_element(By.CSS_SELECTOR, "div[role='button']")
                return div.text in ["Not now", "Plus tard"]
            except:
                return False

        wait.until(is_not_now_button_present)
        driver.find_element(By.CSS_SELECTOR, "div[role='button']").send_keys(Keys.ENTER)
        logging.info("Clicked 'Not now'")
        break
    except TimeoutException:
        pass

# Main loop
total_deleted = 0
batch_number = 1
while True:
    is_clicked_select = False
    while not is_clicked_select:
        logging.info("Waiting for elements to load...")
        time.sleep(2)
        elements = driver.find_elements(By.CSS_SELECTOR, 'span[data-bloks-name="bk.components.Text"]')
        for el in elements:
            if el.text in ["Select", "Sélectionner"]:
                if any(txt.text in ["No results", "Aucun résultat"] for txt in elements):
                    logging.info("No items found. Finished.")
                    driver.quit()
                    sys.exit(0)
                driver.execute_script("arguments[0].click();", el)
                logging.info("Clicked 'Select'")
                is_clicked_select = True
                break

    selected_count = 0
    attempts = 0
    refreshed = False

    while selected_count < SELECTIONS_AT_ONCE:
        if refreshed:
            break
        time.sleep(DELAY_BETWEEN_SELECTS)
        for checkbox in driver.find_elements(By.CSS_SELECTOR, '[aria-label="Toggle checkbox"]'):
            try:
                if not checkbox.is_selected():
                    driver.execute_script("arguments[0].click();", checkbox)
                    selected_count += 1
                    logging.info(f"Selected item (Total: {selected_count})")
                    if selected_count >= SELECTIONS_AT_ONCE:
                        break
            except StaleElementReferenceException:
                continue

        attempts += 1
        if attempts > 10:
            logging.warning("Possible UI issue or limit. Retrying...")
            time.sleep(2)
            attempts = 0

    # Click Delete/Unlike
    deleted_this_batch = False
    for span in driver.find_elements(By.CSS_SELECTOR, 'span[data-bloks-name="bk.components.TextSpan"]'):
        if span.text in (["Delete", "Supprimer"] if MODE == 1 else ["Unlike", "Je n’aime plus"]):
            time.sleep(1)
            driver.execute_script("arguments[0].click();", span)
            logging.info(f"Clicked '{span.text}' to confirm deletion (Batch #{batch_number})")
            deleted_this_batch = True
            break

    # Confirm the deletion
    confirmed = False
    while not confirmed and deleted_this_batch:
        time.sleep(1)
        for btn in driver.find_elements(By.CSS_SELECTOR, 'div[role="dialog"] button'):
            try:
                confirmation_text = btn.find_element(By.CSS_SELECTOR, "div").text
                if confirmation_text in ["Delete", "Unlike", "Supprimer", "Je n’aime plus"]:
                    driver.execute_script("arguments[0].click();", btn)
                    logging.info(f"Confirmed '{confirmation_text}'")
                    total_deleted += selected_count
                    logging.info(f"Deleted {selected_count} comments in batch #{batch_number}. Total deleted: {total_deleted}")
                    batch_number += 1
                    confirmed = True
                    break
            except StaleElementReferenceException:
                continue
        if not confirmed:
            time.sleep(1)

    logging.info(f"Waiting {DELAY_AFTER_BATCH_DELETE} seconds before next batch...")
    time.sleep(DELAY_AFTER_BATCH_DELETE)  # Extra delay for safety

except KeyboardInterrupt:
logging.info("Script manually interrupted.")
driver.quit()
sys.exit(0)
except NoSuchWindowException:
logging.exception("Browser window was closed.")
sys.exit(1)
except Exception as e:
logging.exception("Unexpected error:")
try:
driver.quit()
except:
pass
sys.exit(1)

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