instagrapi

🔥 The fastest and powerful Python library for Instagram Private API 2026 with HikerAPI SaaS

View on GitHub

Best Practices

This is a best practices guide around using the Instagram API so that you don’t get rate limited or banned.

Use a Stable Proxy Identity

If you’re getting errors like this:

Or you notice that Instagram is sending suspicious login emails, review the network identity for that account. Instagram may be rate limiting the IP, distrusting the login location, or challenging the account because the device/session and IP history do not look consistent.

For production automation, the safest baseline is one account per stable proxy/IP. Reusing the same country, city, ASN/mobile carrier, device settings, and saved session is less suspicious than using a different IP address every time you make a request.

Avoid treating any proxy type as a universal fix. Residential, mobile, ISP, and datacenter proxies can all fail if they are abused, shared too widely, or rotated aggressively. The exact provider matters less than consistency, reputation, and whether your request pattern fits the account history.

Here is the shape of using a proxy with instagrapi:

from instagrapi import Client

cl = Client()
before_ip = cl._send_public_request("https://api.ipify.org/")
cl.set_proxy("http://<api_key>:wifi;ca;;;toronto@proxy.soax.com:9137")
after_ip = cl._send_public_request("https://api.ipify.org/")

print(f"Before: {before_ip}")
print(f"After: {after_ip}")

Notes:

Warm Accounts Gradually

New, restored, or previously challenged accounts should not immediately run high-volume actions.

Practical warmup rules:

Add Delays

It’s recommended you try to mimic the behavior of a real user. This means you should add delays between requests. The delays should be random and not too long.

The following is a good example of how to add delays between requests.

from instagrapi import Client

cl = Client()

# adds a random delay between 1 and 3 seconds after each request
cl.delay_range = [1, 3]

Delays are only one layer. For larger jobs, also limit concurrency per account, per proxy, and per action type. A single account doing many parallel actions is more suspicious than the same work spread out with clear cooldowns.

Handle Rate Limits and Anti-Abuse Responses Explicitly

Instagram uses several different responses for throttling, suspicious behavior, or temporary restrictions. Treat them differently instead of retrying every error the same way.

ClientThrottledError / HTTP 429

This usually means the current IP or request pattern is too aggressive for that path right now.

Recommended response:

PleaseWaitFewMinutes

This is usually more serious than a single 429. Instagram is telling you to slow down for that account, device, or IP combination.

Recommended response:

FeedbackRequired

This often means an action was blocked or the account is temporarily restricted.

Recommended response:

LoginRequired

This usually means the current private session is no longer valid.

Recommended response:

ChallengeRequired

This means Instagram wants an additional verification step. Some flows can be automated, but newer challenge paths may still require manual review.

Recommended response:

Practical Rules

Common Anti-Patterns

These patterns often create support issues that are not library bugs:

Separate Library Bugs from Operational Blocks

A library bug usually reproduces consistently for the same endpoint and payload, especially across accounts with healthy sessions. Useful reports include the method called, sanitized request/response data, stack trace, dependency versions, and whether the same account works in the official app.

An operational block is usually account, proxy, session, or action-pattern specific. Signs include 429, PleaseWaitFewMinutes, FeedbackRequired, suspicious login emails, challenges, forced password changes, or behavior that disappears after cooldown, cleaner proxy identity, or manual app verification.

When reporting an issue, include enough context to distinguish these cases. Remove cookies, session IDs, tokens, passwords, phone numbers, emails, and private user data before sharing logs.

Use Sessions

If you call .login() from scratch on every run, Instagram sees repeated fresh logins. That is much more suspicious than reusing a stable device session.

The normal pattern is:

  1. Login once
  2. Save settings with .dump_settings()
  3. Load them later with .load_settings() or .set_settings()
  4. Reuse the same device/session identifiers across runs

The first time you run your script

from instagrapi import Client

cl = Client()
cl.login(USERNAME, PASSWORD)
cl.dump_settings("session.json")

And on the next run:

from instagrapi import Client

cl = Client()
cl.load_settings("session.json")
cl.login(USERNAME, PASSWORD)
cl.get_timeline_feed()  # optional session validity check

You’ll notice we do a call to cl.get_timeline_feed() to check if the session is valid. If it’s not valid, you’ll get an exception.

If you want more explicit control over the loaded settings object:

from instagrapi import Client

cl = Client()
session = cl.load_settings("session.json")
cl.set_settings(session)
cl.login(USERNAME, PASSWORD)

Putting this all together, you can write a reusable login helper like this:

from instagrapi import Client
from instagrapi.exceptions import LoginRequired
import logging

logger = logging.getLogger()

def login_user():
    """
    Attempts to login to Instagram using either the provided session information
    or the provided username and password.
    """

    cl = Client()
    session = cl.load_settings("session.json")

    login_via_session = False
    login_via_pw = False

    if session:
        try:
            cl.set_settings(session)
            cl.login(USERNAME, PASSWORD)

            # check if session is valid
            try:
                cl.get_timeline_feed()
            except LoginRequired:
                logger.info("Session is invalid, need to login via username and password")

                old_session = cl.get_settings()

                # use the same device uuids across logins
                cl.set_settings({})
                cl.set_uuids(old_session["uuids"])

                cl.login(USERNAME, PASSWORD)
            login_via_session = True
        except Exception as e:
            logger.info("Couldn't login user using session information: %s" % e)

    if not login_via_session:
        try:
            logger.info("Attempting to login via username and password. username: %s" % USERNAME)
            if cl.login(USERNAME, PASSWORD):
                login_via_pw = True
        except Exception as e:
            logger.info("Couldn't login user using username and password: %s" % e)

    if not login_via_pw and not login_via_session:
        raise Exception("Couldn't login user with either password or session")

    return cl

Prefer Read/Write Separation

If your workload is mostly data retrieval, keep that path separate from account-changing actions.

In practice: