⚠️ Telegram support group moved to aiograpi_support — the previous
@instagrapigroup has been restricted by Meta and is no longer maintained.
Fast and effective unofficial Instagram API wrapper for Python.
instagrapi combines public web and private mobile API flows, supports session persistence and challenge handling, and covers the main automation primitives for users, media, stories, direct messages, notes, locations, comments, insights, and uploads.
Private API automation is fragile in production because account trust, proxies, device state, challenges, and rate limits can change independently of the library. For account-owned business workflows, prefer official Instagram APIs where they cover your use case. For production private API infrastructure, a hosted provider such as HikerAPI may be a better fit than maintaining accounts, proxies, and challenge handling yourself.
The instagrapi project is best suited for testing, research, and controlled internal automation.
✨ aiograpi - Asynchronous Python library for Instagram Private API ✨
Support Python 3.10+
Python 3.9 support was dropped in 2.5.0. Upstream security patches for Pillow 12.x and pytest 9.x are not backported to Python 3.9, leaving conditional pins permanently exposed to known CVEs. Users who need Python 3.9 should pin to instagrapi==2.4.5.
pip install instagrapi
Optional public web TLS impersonation support is available as an extra:
pip install "instagrapi[curl]"
Use it only for public web endpoints that are sensitive to browser TLS fingerprints:
cl = Client(public_transport="curl", public_transport_impersonate="chrome136")
See the public transport guide for live comparison results and caveats.
TLS certificate verification is enabled by default. For a trusted debugging MITM proxy, prefer Client(tls_verify="/path/to/proxy-ca.pem"); use Client(tls_verify=False) only for temporary local debugging because it allows session interception.
If your project uses uv, you can add the package with:
uv add instagrapi
Or install it into the active virtual environment:
uv pip install instagrapi
Video uploads can use a built-in MP4 metadata parser when you provide thumbnail=.... Automatic thumbnail generation, StoryBuilder, and video/audio composition still need the optional video dependencies, MoviePy 2.2.1, and executable ffmpeg:
pip install "instagrapi[video]"
pip install --no-deps "moviepy==2.2.1"
MoviePy 2.2.1 currently declares Pillow<12, but instagrapi keeps Pillow>=12.2.0 for security fixes; the --no-deps install keeps the safe Pillow version. If your project imports MoviePy directly, migrate any MoviePy 1.x code from moviepy.editor, set_*, resize, and subclip APIs to the MoviePy 2.x API before upgrading.
Android users should see Pydroid and ffmpeg and Termux.
from instagrapi import Client
cl = Client()
cl.login(ACCOUNT_USERNAME, ACCOUNT_PASSWORD)
user_id = cl.user_id_from_username(ACCOUNT_USERNAME)
medias = cl.user_medias(user_id, 20)
Practical scripts live in examples/README.md. They cover session login, public lookups, media downloads, feed uploads, Reels and Trial Reels, story uploads, Direct messages, proxies, challenge handling, and optional curl-backed public transport.
from instagrapi import Client
cl = Client()
cl.login(USERNAME, PASSWORD)
cl.dump_settings("session.json")
# reload later without entering credentials again
cl = Client()
cl.load_settings("session.json")
cl.login(USERNAME, PASSWORD)
If you want more explicit control over the loaded session object:
from instagrapi import Client
cl = Client()
cl.set_settings(cl.load_settings("session.json"))
cl.login(USERNAME, PASSWORD)
from instagrapi import Client
cl = Client()
cl.login_by_sessionid("<your_sessionid>")
login_by_sessionid() is best treated as a lightweight compatibility path. For long-lived automation, prefer the normal login() -> dump_settings() -> load_settings()/set_settings() session flow.
If a browser/web sessionid returns login_required or logs the browser out, Instagram rejected that session for the private mobile API. Use a stable password login once, save settings with dump_settings(), and reuse those settings instead of repeatedly importing browser cookies.
from instagrapi import Client
cl = Client()
cl.login(USERNAME, PASSWORD)
target_id = cl.user_id_from_username("target_user")
posts = cl.user_medias(target_id, amount=10)
for media in posts:
# download photos to the current folder
cl.photo_download(media.pk)
See examples/session_login.py for a standalone script demonstrating these login methods.
from instagrapi import Client
cl = Client()
cl.login(USERNAME, PASSWORD)
places = cl.location_search_name("Times Square")
place = places[0]
same_place = cl.location_search_pk(place.pk)
print(same_place.name, same_place.pk)
from instagrapi import Client
cl = Client()
cl.login(USERNAME, PASSWORD)
target_id = cl.user_id_from_username("target_user")
sent = cl.direct_send("Hello from instagrapi", user_ids=[target_id])
print("sent", sent.id)
threads = cl.direct_threads(amount=5)
for thread in threads:
last_message = thread.messages[0] if thread.messages else None
print(thread.id, thread.thread_title, last_message.text if last_message else "")
Realtime MQTT support is experimental. It opens Instagram's private MQTToT
connection after login, emits live callbacks, and uses the same Client.proxy
settings as HTTP requests. The realtime client can receive Direct message sync
events and publish lightweight Direct actions such as text, reactions, typing,
and seen state. Use the regular direct_* methods for media sends and full
thread management.
import json
from instagrapi import Client
cl = Client()
cl.login(USERNAME, PASSWORD)
def handle_direct_message(payload):
print(json.dumps(payload, indent=2, ensure_ascii=False))
cl.realtime_on("message", handle_direct_message)
rt = cl.realtime_connect()
rt.direct_subscribe()
try:
rt.ping()
rt.direct_send_text(THREAD_ID, "Hello from MQTT")
while True:
rt.read_once()
finally:
cl.realtime_disconnect()
See the full Realtime MQTT guide for lower-level subscriptions and event details.
FBNS uses Instagram's separate push MQTT connection and registers an Android push token for the logged-in session. It is useful when you need push payloads such as Direct notification callbacks.
import json
from instagrapi import Client
cl = Client()
cl.login(USERNAME, PASSWORD)
def handle_push(payload):
print(json.dumps(payload, indent=2, ensure_ascii=False))
cl.fbns_on("push", handle_push)
fbns = cl.fbns_connect()
try:
fbns.ping()
while True:
cl.fbns_read_once()
finally:
cl.fbns_disconnect()
sessionid, and Bloks 2FA fallback/helpers for newer verification flowsdate_followed_latest and date_followed_earliestchaining, fetch_suggestion_details, discover_recommended_accounts_for_category_v1, user_stream_*, user_web_profile_info_v1media_search, fbsearch_accounts_v2, fbsearch_reels_v2, fbsearch_topsearch_v2, fbsearch_typeheadmedia_info_v2) for ad-tagged / sponsored media that the canonical endpoint refusesAnonymous/public web paths are best treated as opportunistic rather than guaranteed. Instagram can change or restrict them independently of the library, so production-grade workflows should prefer authenticated sessions.
API reference and full usage guide live at subzeroid.github.io/instagrapi:
429, challenge, and relogin logic@instagrapi group was restricted by Meta and is no longer maintainedFor other languages, consider instagrapi-rest. For async Python, see aiograpi.
Hands-on guides for real instagrapi work — login flows, sessions, proxies, scraping, posting, error handling — live at instagrapi.com/guides:
challenge_requiredbad_password, challenge_required, login_required, please_wait_a_few_minutes, feedback_required, proxy_address_is_blockedBadPassword: correct password rejected by Instagram — proxy/IP/device/session trust troubleshootingComparing instagrapi to other tools:
Additional example
from instagrapi import Client
from instagrapi.types import StoryMention, StoryMedia, StoryLink, StoryHashtag
cl = Client()
cl.login(USERNAME, PASSWORD, verification_code="<2FA CODE HERE>")
media_pk = cl.media_pk_from_url('https://www.instagram.com/p/CGgDsi7JQdS/')
media_path = cl.video_download(media_pk)
subzeroid = cl.user_info_by_username('subzeroid')
hashtag = cl.hashtag_info('dhbastards')
cl.video_upload_to_story(
media_path,
"Credits @subzeroid",
mentions=[StoryMention(user=subzeroid, x=0.49892962, y=0.703125, width=0.8333333333333334, height=0.125)],
links=[StoryLink(webUri='https://github.com/subzeroid/instagrapi')],
hashtags=[StoryHashtag(hashtag=hashtag, x=0.23, y=0.32, width=0.5, height=0.22)],
medias=[StoryMedia(media_pk=media_pk, x=0.5, y=0.5, width=0.6, height=0.8)]
)
If you need async Python, use [aiograpi](https://github.com/sub
$ claude mcp add instagrapi \
-- python -m otcore.mcp_server <graph>