BadPassword: correct password rejected by Instagram
Maintained by the instagrapi contributors · Library on GitHub
Updated
instagrapi.exceptions.BadPassword looks simple until you test the same credentials in the Instagram app and they work. The exception name says the password is bad, but the upstream response is not always a literal credential check. Instagram can return the same bad_password envelope when the password is correct but the login attempt looks risky: a burned proxy, a datacenter IP, a fresh device fingerprint, too many repeated password logins, or an account that has recently been challenged.
This page is for the confusing case: you know the password is correct, yet cl.login(username, password) raises BadPassword. The right response is not to brute-force retries or rotate every setting at once. Treat it as an identity-trust failure, run a controlled app-vs-library test, and only then decide whether the password, the network identity, or the account state is the real problem.
Symptoms
The failure happens during login() itself, usually at the accounts/login/ private API call. You may be able to log in from a browser or an already-trusted phone, while a fresh script, VPS, container, or proxy-backed client gets rejected. That mismatch is the clue: Instagram is not just checking the password string. It is scoring the whole login event.
cl.login(username, password)raisesBadPasswordfromaccounts/login/.- The same password works in an existing browser or phone session.
- A fresh browser profile, incognito window, VPS, or proxy may show the same “wrong password” message as instagrapi.
- Repeating the same login attempt keeps failing and can trigger suspicious-login emails,
challenge_required, or forced password reset. - Changing random headers or app versions does not reliably fix it because the account/IP/device trust state is the signal being rejected.
The key distinction is whether the official Instagram app succeeds under the same network identity. If the app works on your normal home connection but fails when you route it through the same proxy as the script, that proxy/IP is part of the problem. If the app succeeds through the same proxy and instagrapi fails, then investigate device settings, session reuse, app version, and login flow.
Real-world traceback
Traceback (most recent call last):
File "login_bad_password.py", line 8, in <module>
cl.login(USERNAME, PASSWORD, verification_code=code)
File ".../instagrapi/mixins/auth.py", line 507, in login
logged = self.private_request("accounts/login/", data, login=True)
File ".../instagrapi/mixins/private.py", line 576, in private_request
self._send_private_request(endpoint, **kwargs)
File ".../instagrapi/mixins/private.py", line 454, in _send_private_request
raise BadPassword(**last_json)
instagrapi.exceptions.BadPassword: We can send you an email to help you get back into your account.
If you are sure that the password is correct, then change your IP address,
because it is added to the blacklist of the Instagram Server
The important frame is instagrapi.mixins.private._send_private_request. That method reads Instagram’s error envelope. When the upstream response contains error_type == "bad_password", instagrapi raises BadPassword. Recent versions also append the IP warning because this exact pattern is common: the account credentials are valid, but Instagram does not trust the login context enough to accept them.
Why it happens
BadPassword has two broad causes: a real credential error, and a risk-model rejection that Instagram represents as a password error. The first is ordinary. The second is the one that burns hours because every surface appears to contradict every other surface.
1. The password is actually wrong or transformed
Start with the boring checks because they are cheap. Environment variables can include stale values, trailing spaces, wrong account credentials, Unicode lookalikes, or shell quoting issues. If you load the password from .env, print its length, not the secret. If the password was changed recently, make sure your worker, secret store, CI variables, and local shell all have the same version. If two-factor authentication is enabled, pass a current verification_code or TOTP code, but do not confuse a 2FA failure with a password failure.
This is the only BadPassword variant where immediate code retry might help after fixing the input. Everything else below gets worse when you keep retrying the same login.
2. Instagram dislikes the proxy or IP
Instagram can reject a correct password when the login comes from a network identity it does not trust. Datacenter IPs, noisy VPN exits, abused residential pool addresses, and cheap shared proxies all show this behavior. The upstream UI may still say “incorrect password”, but the fix is not a new password; it is a cleaner, stable network identity.
The strongest diagnostic is to route the official Instagram app through the exact same proxy or IP and try the same credentials there. If the real app also reports a password error, you have reproduced the problem outside instagrapi. At that point the library is not the root cause. Change the proxy/IP, use a stable residential or mobile exit for that account, and avoid rotating locations mid-challenge.
3. The account, device, and session history do not match
Instagram scores the device fingerprint, app version, cookies, prior sessions, login geography, and account history together. A password that works on your phone can fail from a fresh VPS because the phone is a trusted device and the VPS is not. A password that works on your laptop through a VPN can fail directly on the server even with the same exit IP because the execution environment and session state differ. This is why “same IP” is not proof that the login should be accepted.
Persisted settings help here. Reusing dump_settings()/load_settings() preserves device identifiers and cookies across restarts. A fresh Client() with no saved settings looks more like a new install every time, which raises the chance of BadPassword, challenge_required, or a forced verification flow.
4. Too many fresh password logins
Fresh password logins are expensive trust events. Running cl.login() repeatedly from cron, CI, Lambda, Docker cold starts, or many test machines trains Instagram to distrust the account. A saved session is safer than asking for a new login every run. If a worker already has a valid session, load it and let instagrapi reuse it. If the session dies with login_required, follow the session recovery guide instead of starting from a blank client.
Fix in instagrapi
The fix is a diagnostic flow, not one magic flag. You want to separate “wrong password” from “right password, rejected context” before touching proxies, sessions, or device settings.
-
Verify the credentials outside the script. Use the official Instagram app or web login from the account’s normal network. If the password fails everywhere, fix the password first. If the password works on a trusted phone but fails in the script, continue.
-
Run the same-network app test. Route the real Instagram app through the same proxy/IP that the script uses and try the same login. This can be a phone-level VPN, device proxy, emulator proxy, or a network setup where the app and script share the same egress. The result tells you where to look next.
from instagrapi import Client from instagrapi.exceptions import BadPassword cl = Client() cl.set_proxy("http://user:pass@proxy-host:port") try: cl.login(USERNAME, PASSWORD, verification_code=TOTP_CODE) except BadPassword: print("Do not retry in a loop. Test the same credentials in the official app through this proxy/IP.") raise -
If the app also fails, replace the network identity. Use one clean, stable residential or mobile proxy per account. Avoid datacenter ranges, noisy shared VPN exits, and per-request rotation. Match country and locale to the account’s normal environment. Let the account cool down before retrying; immediate repeated attempts on a newly-swapped IP can keep the same risk flag alive.
-
If the app succeeds, preserve device and session state. Use
load_settings()before login anddump_settings()after successful calls. Keep the same settings file tied to the same account and proxy. Do not reuse one session file across machines with different IPs, and do not throw away settings on every process start.from pathlib import Path from instagrapi import Client session_path = Path("sessions/account.json") cl = Client() cl.set_proxy("http://user:pass@stable-residential-proxy:port") if session_path.exists(): cl.load_settings(session_path) cl.login(USERNAME, PASSWORD, verification_code=TOTP_CODE) cl.dump_settings(session_path) -
Escalate only after the controlled test. If a clean stable IP, a saved settings file, and manual app verification still fail, the account may be in a temporary risk hold. Wait 24-48 hours, log in manually in the official app, clear any security prompts, and avoid automation until the account behaves normally. If your use case is a Business or Creator integration that Meta officially supports, use the official Instagram Platform APIs instead of private API login. If you need private API data at scale and the operational layer is the bottleneck, use a managed API like HikerAPI.
Deep dive
The confusing part of BadPassword is that Instagram intentionally collapses several different failures into the same user-facing bucket. A normal user does not need to know whether the login was rejected because of a stale secret, an impossible-travel signal, a burned datacenter IP, or a device fingerprint that does not match account history. Showing “wrong password” is a safe generic response. For automation, that generic response hides the real branch.
That is why the official-app-with-same-proxy test is so useful. It removes instagrapi from the experiment. If the official app fails under the same egress identity, the server-side decision is already made before any Python code can matter. If the official app succeeds and instagrapi fails, then the remaining difference is the private API client context: app version headers, device identifiers, saved settings, cookies, or the exact login sequence.
There is also a trap around “I tried another IP.” A random new IP is not necessarily cleaner. A fresh datacenter IP, a cheap VPN exit, or a residential address with prior abuse can be worse than the original address. Changing IP also changes the account’s location history, so hopping between many exits during a challenge or password reset makes the account look less stable. Prefer one high-quality, sticky exit per account over rapid rotation.
The operational rule is simple: do not treat BadPassword as retryable. Treat it as a stop signal. Verify the credential manually, test the same proxy/IP in the real app, then either repair credentials, replace the network identity, or preserve session/device state before trying again.
Related errors
- challenge_required: how to fix the Instagram challenge in instagrapi (Python) Fix instagrapi's challenge_required error in Python: SMS/email codes, TOTP fallback, and why session reuse stops it from coming back.
- login_required: how to recover the instagrapi session in Python Fix instagrapi's login_required error: when sessions die mid-script, what causes the cookie to expire, and how to recover without re-triggering 2FA.
- proxy_address_is_blocked: how to recover when Instagram banned the IP ProxyAddressIsBlocked means Instagram has flagged the IP. instagrapi raises this when the proxy/residential address is unusable — rotate or replace.
Related guides
- Configuring proxies in instagrapi: HTTP, SOCKS5, and residential setups Configure HTTP and SOCKS5 proxies in instagrapi (Python). Residential vs datacenter, per-account pinning, and rotating without breaking sessions.
- Persisting instagrapi sessions: file, Redis, and Postgres patterns Reuse instagrapi login sessions across runs and processes: dump_settings, load_settings, and storing the session blob in Redis or Postgres.
- Handling instagrapi 2FA and challenge_required errors in Python Resolve instagrapi 2FA prompts and challenge_required errors: SMS, email, and TOTP flows with working callback handlers.
Frequently asked
Does BadPassword always mean the password is wrong?
No. BadPassword can mean the literal password is wrong, but Instagram also uses bad_password for account, proxy, IP, device, or session trust failures.
How do I tell whether BadPassword is caused by my proxy?
Try the same username and password in the official Instagram app while routing the app through the same proxy or IP. If the app also shows bad_password or BadPassword-style copy, the problem is the network/account trust state, not instagrapi.
Should I keep retrying BadPassword?
No. Repeating BadPassword login attempts usually makes the account look worse. Stop, verify the password manually, switch to a clean stable IP if needed, and let the account cool down.
Can a saved session avoid BadPassword?
A trusted saved session can reduce fresh password logins, but it cannot bypass BadPassword if Instagram has already decided that the account, IP, proxy, or device state is risky.
Skip the infra?
Managed Instagram API — same endpoints, sessions and proxies handled.
Try HikerAPI → Full comparison