Emulating Humans in Social Media Automation

Emulating Human behaviour in an Automated Context and pushing Social Media Engagement rates

tech
knowhow

Introduction

Our initial version of the script relied on a predefined list of hashtags to grab recently posted media and engage with them through liking, commenting, and following, in that order. However, we realized it was easy to detect, so we didn’t let it run for long.

For our second iteration, we researched Instagram’s hard limits and gathered top media from random hashtags. We then targeted the likers of that media and engaged with them through liking, commenting, and following. However, our sessions were being blocked shortly after starting each engagement cycle because our script was acting like a bot rather than a human user. We had to determine what requests were being made by the app itself and emulate them as closely as possible, like someone using the Instagram app.

Before we could start our third iteration, we had to figure out what requests were needed to disguise our bot as a user. We used our emulator to install a custom certificate, enabling us to monitor all requests sent to the Instagram backend via the Instagram app. We ran into some obstacles but finally set everything up by using an older Instagram version.

We sat together and consciously and slowly used the app like we would normally to find out how a human interacts with the app. We found out that the moment you open the app, the timeline loads. We typically refresh it a few times to see what’s new and engage with content there. Sometimes, we open the explore page and look through it, and maybe engage. That’s where we started.

Requests

Initializing

Firstly, upon initial loading of the Instagram app, the timeline feed is fetched. In Instagram’s terms, this initial load is referred to as a cold fetch. Subsequent attempts to refresh the feed are referred to as a refresh reload.

ProxyTimeline

As seen in the image above, three requests are made: the initial cold fetch, the loading of the reel tray, and the pull-to-refresh reload. Therefore, the first step for engagement iteration four was to immediately request a cold fetch on /feed/timeline/ after logging in, followed by loading /feed/reels_tray/. This refresh step can occur multiple times based on engagement rates, chances, and hard limits.

Next up: The explore page

ProxyExplore

Moving from the timeline to explore is also straightforward. It simply involves loading the explore page by requesting /qp/batch_fetch/. The second request is sent with a location service, which may not be sent if location services are blocked or turned off, so we skipped it.

ProxyExploreReload

On reload, nearly the same thing happens. It is important to note here that the query parameter is_prefetch is set to false.

Content Engagement

We are focusing on three primary ways of engaging with users on Instagram: liking, commenting, and following. However, to fully understand user engagement, we need to determine whether opening images counts as an engagement and whether Instagram tracks it. Let’s dive into it.

For the third iteration, our plan is to log in and navigate to the timeline page. We will then conditionally refresh the page several times or engage with the media. Next, we will conditionally navigate to the explore page and repeat the same process. Finally, we will randomly select a hashtag from a predefined list, fetch a top post, retrieve the list of users who have liked the post, engage with two random media of those users, and conditionally follow them.

Hashtags Top

The Hashtags Top feature of Instagram sends three requests. Firstly, our initial request is for the top media related to a hashtag using the endpoint /tags/:hashtag/sections/.

ProxyHashtagsTop

Afterwards, it sends two more requests using the endpoints /tags/:hashtag/info/ and /tags/:hashtag/story/. However, we only care about the first one. Therefore, we send the first request, parse the result, and simply send the two get requests after without performing any further actions.

A human would typically click on an image to view its likers. We simulate this behavior by calling the /media/:media_pk/info/ endpoint and discarding the result. Now, we are on the image page, and we can view the likers by clicking on the “likers” button, which sends the following requests:

ProxyMediaLikers

The first two requests can be ignored, as they are not important. The third one is the one we want. The /media/:media_pk/likers/ endpoint is used to retrieve the users who have liked the media. This was enough in iteration two to perform our engagement. However, Instagram requires more information to display the user interface. For example, the fifth request to /friendships/show_many/ is used to display the “follow” or “unfollow” button in the user list. We parse this data as we need it to determine whether we already follow this person. We could also request the friendship status of individual users before following them. However, it doesn’t make sense as the app wouldn’t send this request. The fourth request data is not needed, so we request the /ig_fb_xposting/user_setting/ endpoint and discard the response.

Now that we have obtained the list of likers, we want to select a random subset of n likers from this list and engage with them as our target audience.

User info

Upon selecting one of our likers, Instagram loads the user’s page, triggering multiple requests. The initial request to /friendships/show/:user_id/ provides us with information regarding the status of our friendship with the user, if we follow him and if he follows us. Moreover, this also indicates whether the user’s account is private or not. The second request to /feed/user/:user_id/ retrieves the actual medias uploaded by the user, if their account is public or if we are authorized to view it when it is private. The third request to /users/:user_id/info/ retrieves the user’s account information.

ProxyUserPage

We disregard the responses from /highlights/:user_id/highlights_tray/, /multiple_accounts/get_featured_accounts/, and /feed/user/:user_id/story/ since they do not impact our requirements. In fact, we only send them to match the app’s requests. We are ignoring the /qp/batch_fetch/ requests, as they appear unimportant.

Bellow is an example code of this workflow:

    friendship: Relationship = self.client.user_friendship_v1(user_id)
    self.client.user_info_v1(user_id)
    medias = []
    if not friendship.is_private or friendship.is_private and friendship.following:
        medias: list[Media] = self.client.user_medias_v1(user_id)
        self.client.featured_accounts_v1(user_id)
        self.client.user_highlights_v1(user_id)
        self.client.user_stories_v1(user_id)

Engaging Media

Likes

We pick a few random medias from the found account and let our engage workflow run on them. Like in hashtags top, we first load the media info to simulate a click. After that, we conditionally like that image. Liking is nothing special, simple request to /media/:media_pk/like/.

ProxyMediaLike

Commenting

Commenting on the other hand is more interesting.

ProxyCommentMedia

First of all, lets ignore every /wwwgraphql/ig/query/ and /qp/batch_fetch/ again. To comment on something, a user has to click on the comments first. Upon clicking on the comments, the app first loads every available comment. This is the first request /media/:media_pk/comments/. After that, we are writing our comment and sending it. There is one important request we found here, that we also have to send before. /media/comment/check_offensive_comment/, which checks if a comment is offensive or not. We just send it and don’t do something with the response. Finally, the last request is to /media/:user_id/comment/ with our comment data to write the comment.

Following

Following is straightforward again.

ProxyFollowUser

A simple request to /friendships/create/:user_id/ is enough. The others can be ignored again.

Hardlimits

To not get flagged, we experimented and researched on some hardlimits to stay under the radar. Bellow are our findings.

  • 10-15 follow-unfollow actions per hour
  • 60-80 follow-unfollow actions per day
  • 3-5 DMs per hour and 10-12 DMs per day
  • 10-15 likes per hour and 50-60 likes per day
  • 3-4 comments per hour and 10-12 comments per day It’s important to continue with natural activities daily to maintain a healthy account.

Once your account is fully warmed up (4-5 weeks later) and established, you can gradually increase your activity levels.

  • 20-25 follow-unfollow actions per hour
  • 120-160 follow-unfollow actions per day
  • 6-8 DMs per hour and 30-50 DMs per day
  • 25-30 likes per hour and 150-200 likes per day
  • 4-5 comments per hour and 12-18 comments per day

However, it’s still important to balance these activities with natural engagement to avoid any issues with Instagram’s algorithms.