Skip to content

Instantly share code, notes, and snippets.

@chaodonghu
Last active September 28, 2024 17:12
Show Gist options
  • Save chaodonghu/c942b6ca8f8c247ccacd3b0123ff3580 to your computer and use it in GitHub Desktop.
Save chaodonghu/c942b6ca8f8c247ccacd3b0123ff3580 to your computer and use it in GitHub Desktop.
Google Chrome script that allows user to mass unfollow instagram users on user's profile
// Run GOOGLE CHROME - WORKING AS OF DEC 26 2023
// Please @ me in the comments if this stops working, I will try to get it working again within the month
// INSTRUCTIONS
// 1. Open Instagram in Chrome
// 2. Click on "FOLLOWING" on your Instagram profile
// 3. Open developer tools by right clicking on the page and clicking "INSPECT"
// 4. Copy the code below and paste in the developer tools console and press enter to run
// 5. Script will not run if tab is navigated away from, minimized of unfocused (It is recommended to open a new chrome window or push tab to the side and let script run in background)
const unfollowEveryone = (async () => {
// Modify these variables to your liking
const UNFOLLOW_LIMIT = 800;
const BREAK_DURATION = 5 * 60 * 1000; // 5 minutes break
const TOTAL_DURATION = 10 * 60 * 1000; // 10 minutes duration - Timeout after 10 minutes
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
// Find target button
const findButton = (txt) =>
[...document.querySelectorAll("button").entries()]
.map(([pos, btn]) => btn)
.filter((btn) => btn.innerText === txt)[0];
console.log("Start unfollowing script...");
let startTime = new Date().getTime();
while (new Date().getTime() - startTime < TOTAL_DURATION) {
for (let i = 0; i < UNFOLLOW_LIMIT; i++) {
const followingButton = findButton("Following");
if (!followingButton) {
continue;
}
followingButton.scrollIntoViewIfNeeded();
followingButton.click();
await delay(100);
const confirmUnfollowButton = findButton("Unfollow");
if (confirmUnfollowButton) {
await confirmUnfollowButton.click(); // Wait for the unfollow to complete
}
// Increase UNFOLLOW_INTERVAL if you are getting rate limited
// Set this to 0 unfollow as quickly as possible - not recommended
// Random unfollow interval for each follow to avoid rate limiting
const UNFOLLOW_INTERVAL = Math.floor(Math.random() * 10 + 1) * 5000;
console.log(`Wait ${UNFOLLOW_INTERVAL} milliseconds`);
await delay(UNFOLLOW_INTERVAL);
console.log(`Unfollowed #${i}`);
}
console.log(`Taking a break for ${BREAK_DURATION / 1000} seconds...`);
await delay(BREAK_DURATION); // Take a break to avoid rate limiting
startTime = new Date().getTime(); // Reset start time for the next cycle
}
console.log("Unfollow script complete!");
})();
@Ironyte
Copy link

Ironyte commented Sep 15, 2021

Why does the followers number just pop back up to some random number which is still less than what used to be...

@Ironyte can you post a screenshot of the problem or elaborate, i'm not understanding the issue. Are you referring to once the script is run even though you follow no one the instagram interface still shows you follow users?

Can you take a screenshot of the console to see if there are any errors that appear.

Thats exactly what happens

@Ironyte
Copy link

Ironyte commented Sep 15, 2021

Why does the followers number just pop back up to some random number which is still less than what used to be...

@Ironyte can you post a screenshot of the problem or elaborate, i'm not understanding the issue. Are you referring to once the script is run even though you follow no one the instagram interface still shows you follow users?
Can you take a screenshot of the console to see if there are any errors that appear.

Thats exactly what happens

Also gotta say I set random timeout to near-zero cause some hacker had added 3k accounts in my following list and I needed my account back to normal as fast as possible

@quincynyan
Copy link

Why does the followers number just pop back up to some random number which is still less than what used to be...

@Ironyte can you post a screenshot of the problem or elaborate, i'm not understanding the issue. Are you referring to once the script is run even though you follow no one the instagram interface still shows you follow users?
Can you take a screenshot of the console to see if there are any errors that appear.

Thats exactly what happens

Also gotta say I set random timeout to near-zero cause some hacker had added 3k accounts in my following list and I needed my account back to normal as fast as possible

@Ironyte If you're sending too many requests (AKA unfollows) in a short time, you will get rate limited (or sometimes get banned if you send too many requests in a very very short time). Also, follower numbers increasing means the hacker is still in your account still following more people, so please change your passwords and clean your devices.

@arimal199
Copy link

image

@shev72
Copy link

shev72 commented Aug 31, 2023

minor improvrments to the script which allow it work in Aug 2023, better handles the time between unfollows, and scrolls down to handle the next batch received by AJAX.

// click on unfollow
// open the chrome console
// copy the script into the console

const FOLLOWING_BUTTON_TEXT = 'Following' // CHANGE "FOLLOWING" TO LOCALIZED LANGUAGE AS DISPLAYED ON INSTAGRAM
const UNFOLLOW_BUTTON_TEXT = 'Unfollow' // CHANGE "UNFOLLOW" TO LOCALIZED LANGUAGE AS DISPLAYED ON INSTAGRAM
const MAX_ATTEMPTS_PER_UNFOLLOW = 3 // MAXIMUM # OF ATTEMPTS
const TEXT_XPATH='.//div//text()'
const TEXT_XPATH_UNFOLLOW='text()'
const timeout = (ms) => new Promise(resolve => setTimeout(resolve, ms))
const unfollowSomebody = async () => {
    const followingButton = document
        .evaluate(`//button[${TEXT_XPATH}="${FOLLOWING_BUTTON_TEXT}"]`, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null)
        .singleNodeValue
    if (followingButton) {
        console.log('Found following button. Clicking ...')
        followingButton.click()
        console.log('Clicked following button.')
		let unfollowButton=null
        let attempts = 1
		 await timeout(2000)
        while (attempts < MAX_ATTEMPTS_PER_UNFOLLOW && !unfollowButton) {
		
            console.log(`Attempted to find unfollowButton after delay, attmpe  #${attempts++}`)
            unfollowButton = document.evaluate(`//button[${TEXT_XPATH_UNFOLLOW}="${UNFOLLOW_BUTTON_TEXT}"]`, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue
			attempts=attempts+1;
        }
		if(unfollowButton){
            console.log('Found unfollow button. scrolling to force reload next users and  clicking ...')
			//unfollowButton.scrollIntoView(true)
			   unfollowButton.click()
			document.querySelector('[class="_aano"]').scrollBy(0,30)
         
		}else{
			  console.log('NO unfollow button.  exiting early ...')
		return true  // no unfollow button found
		}
        return false
    }else{
	 console.log('WARNING: NO FOLLOW BUTTON FOUND');
	}
    return true  //stop something wrong no following button was found
}



// INCREASE TIMEOUT IF NECESSARY TO AVOID RESTRICTIONS/LIMITS PER DAY  max 1 per minute=>60000 + some random increment
const randomTimeout = () => (Math.floor((Math.random() * 10) + 1) * 1000) + 60000

const unfollowEveryone = async () => {
    let shouldStop = false
	let count=1;
    while (true) {
        shouldStop = await unfollowSomebody()
        const unfollowTimeout = randomTimeout()
        console.log(`Waiting ${unfollowTimeout} seconds. Should stop: ${shouldStop}.`)
        await timeout(unfollowTimeout)
		 console.log(`completed unfollowing processing next, count=${count}`)
		 count=count+1;
		 if (count>200) return true;  // exit early
		 if(shouldStop){
			 count=count+5;
			   console.log('No follow button found trying to scroll more to force ajax refresh, increased count ');
			   document.querySelector('[class="_aano"]').scrollBy(0,150);
		 // try to scroll down a bit more
		 }
    }
    console.log('DONE PROCESSING.')
}

unfollowEveryone()

@dunstorm
Copy link

dunstorm commented Sep 8, 2023

const FOLLOWING_BUTTON_TEXT = 'Following'; // Change to match your localized Instagram version
const UNFOLLOW_BUTTON_TEXT = 'Unfollow'; // Change to match your localized Instagram version
const MAX_ATTEMPTS_PER_UNFOLLOW = 3; // Maximum number of attempts to find the Unfollow button
const MAX_TOTAL_ATTEMPTS = 200; // Maximum total attempts before exiting
const TEXT_XPATH = './/div//text()';
const TEXT_XPATH_UNFOLLOW = 'text()';

// Function to click on the Unfollow button
const unfollowUser = async () => {
    const followingButton = document.evaluate(
        `//button[${TEXT_XPATH}="${FOLLOWING_BUTTON_TEXT}"]`,
        document,
        null,
        XPathResult.FIRST_ORDERED_NODE_TYPE,
        null
    ).singleNodeValue;

    if (!followingButton) {
        console.log('Following button not found. Exiting...');
        return true; // No following button found, script should stop
    }

    console.log('Found following button. Clicking...');
    followingButton.click();
    console.log('Clicked following button.');

    // Wait for a brief moment
    await timeout(2000);

    let unfollowButton = null;
    let attempts = 1;

    while (attempts <= MAX_ATTEMPTS_PER_UNFOLLOW && !unfollowButton) {
        console.log(`Attempt ${attempts}: Trying to find Unfollow button...`);
        unfollowButton = document.evaluate(
            `//button[${TEXT_XPATH_UNFOLLOW}="${UNFOLLOW_BUTTON_TEXT}"]`,
            document,
            null,
            XPathResult.FIRST_ORDERED_NODE_TYPE,
            null
        ).singleNodeValue;
        attempts++;
        await timeout(1000); // Wait for 1 second between attempts
    }

    if (unfollowButton) {
        console.log('Found Unfollow button. Clicking...');
        unfollowButton.click();

        // Scroll down to force the next set of users to load
        const scrollContainer = document.querySelector('[class="_aano"]');
        if (scrollContainer) {
            scrollContainer.scrollBy(0, 30);
        }
    } else {
        console.log('Unfollow button not found after retries.');
    }

    return false; // Continue to the next user
};

// Function to add a delay between actions
const timeout = (ms) => new Promise(resolve => setTimeout(resolve, ms));

// Function to control the main unfollow loop
const unfollowEveryone = async () => {
    let totalAttempts = 0;

    while (totalAttempts < MAX_TOTAL_ATTEMPTS) {
        totalAttempts++;

        console.log(`Processing user #${totalAttempts}...`);
        const shouldStop = await unfollowUser();

        if (shouldStop) {
            console.log('No more following buttons found. Exiting...');
            break; // No more following buttons found, exit the loop
        }

        const unfollowTimeout = randomTimeout();
        console.log(`Waiting ${unfollowTimeout / 1000} seconds before the next user.`);
        await timeout(unfollowTimeout);
    }

    console.log('Done processing.');
};

// Function to generate a random timeout between 60-70 seconds
const randomTimeout = () => (Math.floor((Math.random() * 10) + 1) * 1000) + 60000;

// Start the unfollow process
unfollowEveryone();

@chaodonghu
Copy link
Author

This script should be working as of Dec 26 2023. Thanks @DimVat1 for that piece of code, i've added a wait interval and log to your code and added some comments/instructions for users.

Hope this helps!

@superbarney
Copy link

The script works but only for the 20 or so accounts displayed. It is not able to load more accounts after that. A manual reload of the page is required to refresh the list of accounts.

@chaodonghu
Copy link
Author

The script works but only for the 20 or so accounts displayed. It is not able to load more accounts after that. A manual reload of the page is required to refresh the list of accounts.

@superbarney There is a TOTAL_DURATION on line 18 that defines how long the script runs, by default I have it set at 10 minutes so the script will only run for 10 minutes. This in combination with the UNFOLLOW_INTERVAL on line 48 which is by default a multiple of 5 seconds. Either increase the TOTAL_DURATION or decrease the UNFOLLOW_INTERVAL or play with the UNFOLLOW_LIMIT and BREAK_DURATION constants.

I've confirmed it to be still working as intended, a manual reload of the page isn't needed -> https://cdn.zappy.app/883b402c34b4343d3280c20730ee36fa.mp4

@chaodonghu
Copy link
Author

@Alaryne Yes both the "Following" modal and the "Followers" modal should work with this script if you're on an account page that is not yours.

If you mean removing followers that follow your account, I haven't tried it but you can modify line 33 and line 40 to find the "Remove" button instead.

@superbarney
Copy link

@superbarney There is a TOTAL_DURATION on line 18 that defines how long the script runs, by default I have it set at 10 minutes so the script will only run for 10 minutes. This in combination with the UNFOLLOW_INTERVAL on line 48 which is by default a multiple of 5 seconds. Either increase the TOTAL_DURATION or decrease the UNFOLLOW_INTERVAL or play with the UNFOLLOW_LIMIT and BREAK_DURATION constants.

I've confirmed it to be still working as intended, a manual reload of the page isn't needed -> https://cdn.zappy.app/883b402c34b4343d3280c20730ee36fa.mp4

Thanks. I tried again and it did work as intended, although strangely, not consistently. Sometimes, it reaches a point and displays the message "Waiting 300 seconds ..." and it resumes after a period of time, but many times, it hangs at that stage.

I have managed to unfollow close to 3000 accounts with the script but had to do it over the course of about a week. To others seeking to do the same, I'd be patient and not mess with the default values as you will hit the rate limit much faster and be locked out from further reducing followers.

@chaodonghu Thanks for the script, much appreciated.

@isalmanhaider
Copy link

I can verify that the code operates as intended, but it encounters timeouts. Below is an enhanced version of the code, with a particular focus on respecting Instagram's rate limits—both unofficial and official—to mitigate spam and abuse, and improving error handling.

(async function () {
    const UNFOLLOW_LIMIT = 200; // Reduced to stay under potential rate limits
    const UNFOLLOW_INTERVAL = 5000; // Increased interval to be more respectful of rate limits
    const BREAK_DURATION = 5 * 60 * 1000; // 5 minutes break
    const TOTAL_DURATION = 10 * 60 * 1000; // 10 minutes duration

    const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

    const findButtonByText = (text) => 
        Array.from(document.querySelectorAll("button"))
        .find((button) => button.innerText === text);

    console.log("Start");

    let startTime = Date.now();
    let unfollowCount = 0;

    while (Date.now() - startTime < TOTAL_DURATION) {
        for (let i = 0; i < UNFOLLOW_LIMIT; i++) {
            const followButton = findButtonByText("Following");
            if (!followButton) {
                console.log("No more users to unfollow or unable to find button.");
                break;
            }
            followButton.scrollIntoViewIfNeeded();
            followButton.click();
            await delay(100); // Short delay to wait for modal

            const confirmButton = findButtonByText("Unfollow");
            if (confirmButton) {
                await confirmButton.click(); // Confirm unfollow
                unfollowCount++;
                console.log(`Unfollowed #${unfollowCount}`);
            } else {
                console.log("Confirmation button not found.");
            }

            await delay(UNFOLLOW_INTERVAL);
        }

        console.log(`Taking a break for ${BREAK_DURATION / 1000} seconds...`);
        await delay(BREAK_DURATION);
        startTime = Date.now(); // Reset start time for the next cycle
    }

    console.log("The end");
})();
  • Reduced UNFOLLOW_LIMIT and Increased UNFOLLOW_INTERVAL: These changes help to stay under Instagram's rate limits.
    Improved Button Selection: Uses Array.from() for better readability and directly finds the button instead of mapping and then filtering.
  • Added Basic Logging: Basic logging for when no more follow buttons are found or the confirmation button isn't found, which helps in understanding the script's state.
  • Error Handling and Debugging: While explicit error handling isn't added, logging helps identify issues. For actual production scripts, consider try-catch blocks around actions that might fail.

@pauldgayle
Copy link

may be a silly question, but how do you stop the code from running?

@chaodonghu
Copy link
Author

may be a silly question, but how do you stop the code from running?

@pauldgayle You can refresh the page or manually set a limit to stop it from running after a certain limit as mentioned in the script.

  // Modify these variables to your liking
  const UNFOLLOW_LIMIT = 800;

  const BREAK_DURATION = 5 * 60 * 1000; // 5 minutes break

  const TOTAL_DURATION = 10 * 60 * 1000; // 10 minutes duration - Timeout after 10 minutes

@pauldgayle
Copy link

thank you!!

@nikko-guy
Copy link

is there an easy way to add a list of users I don’t want to unfollow?

@chaodonghu
Copy link
Author

is there an easy way to add a list of users I don’t want to unfollow?

@nikko-guy Unfortunately not since the entire script is based on just finding the "Following" button irrespective of the user you are unfollowing.

If you want to add a list of users you don't want to unfollow that would involve finding the "Following" button then the name/username element beside the "Following" button and skipping the iteration if it matches a name/username in some sort of lookup array or object.

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