Async Python client for Instagram private mobile and web API
Project description
insta-wizard
Async Python library for working with Instagram — mobile (Android emulation) and web (browser emulation) clients.
Status: alpha. Public API may change between versions.
Uses aiohttp as the HTTP transport engine.
Clients overview
| Client | What it emulates | Notes |
|---|---|---|
MobileInstagramClient |
Android Instagram app | Primary client. Most functionality available here |
WebInstagramClient |
Chrome browser | Web-specific flows, web API access |
The mobile client covers most Instagram functionality. The web client is intended for web-specific scenarios and web API access.
What you can do
Mobile client — emulates Android app, uses private mobile API:
- Login, profile management (edit bio, name, profile picture)
- Search users, get user info by ID or username
- Follow / unfollow, manage followers and following lists
- Browse timeline, stories tray, suggested reels
- Direct messages: inbox, pending, group thread management (create, approve, decline, hide, mute)
- Comments: get, add, like, unlike
- Notifications and activity inbox
- Live and Clips discovery
Web client — emulates Chrome browser, uses web API:
- Login
- Follow / unfollow
- Like / unlike media
- Add / like / unlike comments
Installation
Install from GitHub (PyPI release planned):
pip install git+https://github.com/5ou1e/insta-wizard.git
Quick start
import asyncio
from insta_wizard import MobileInstagramClient
async def main():
async with MobileInstagramClient() as client:
await client.account.login("username", "password")
user = await client.users.get_info_by_username("someuser")
print(user)
asyncio.run(main())
Two API styles
1. Sections (primary interface)
Sections are attributes on the client. This is the recommended way to interact — they cover the most common use cases:
# Account
await client.account.login("username", "password")
me = await client.account.get_current_user()
# Users
user = await client.users.get_info_by_username("someuser")
user = await client.users.get_info(user_id)
results = await client.users.search("query")
# Friendships
await client.friendships.follow(user_id)
await client.friendships.unfollow(user_id)
await client.friendships.remove_follower(user_id)
followers = await client.friendships.get_user_followers(user_id)
following = await client.friendships.get_user_following(user_id)
# Feed
timeline = await client.feed.get_timeline()
stories = await client.feed.get_stories_tray()
reels = await client.feed.get_suggested_reels()
# Direct
inbox = await client.direct.get_inbox()
pending = await client.direct.get_pending()
# Media & comments
comments = await client.media.get_comments(media_id)
await client.media.add_comment(media_id, "great post!")
await client.media.like_comment(comment_id)
await client.media.unlike_comment(comment_id)
All mobile client sections:
| Section | What it covers |
|---|---|
account |
login, get current user, edit profile, set bio, set profile picture |
users |
user info by id / username, web profile, search |
friendships |
follow, unfollow, remove follower, followers/following lists, friendship status |
feed |
timeline, stories tray, suggested reels |
direct |
inbox, pending, presence, group thread management |
media |
comments (get / add / like / unlike), blocked users list |
notifications |
notification settings, badge count |
news |
activity inbox |
live |
good time for live |
clips |
discover stream |
challenge |
challenge info |
Web client sections:
| Section | What it covers |
|---|---|
account |
edit profile, age eligibility |
navigation |
load pages, get shared data |
friendships |
follow, unfollow |
comments |
add / like / unlike comment |
likes |
like / unlike media |
challenge |
challenge info |
2. Command bus
Every section method maps to a typed command internally. You can also call commands directly for lower-level, full-parameter access:
from insta_wizard.mobile.commands.user.get_user_info_by_username import UserUsernameInfo
user = await client.execute(UserUsernameInfo(username="someuser"))
Use this when sections aren't enough — for full control over request parameters or access to commands not yet surfaced through sections.
Browsing available commands
from insta_wizard.mobile import print_help
print_help() # prints a table: command name, module, signature
For web:
from insta_wizard.web import print_help
print_help()
Common features
Proxy
from insta_wizard import MobileInstagramClient, ProxyInfo
proxy = ProxyInfo.from_string("user:pass@1.2.3.4:8080")
async with MobileInstagramClient(proxy=proxy) as client:
...
Supported formats for from_string:
1.2.3.4:8080
http://1.2.3.4:8080
user:pass@1.2.3.4:8080
http://user:pass@1.2.3.4:8080
1.2.3.4:8080:user:pass
http://1.2.3.4:8080:user:pass
Change proxy at runtime:
await client.set_proxy(ProxyInfo.from_string("..."))
await client.set_proxy(None) # remove proxy
Auto proxy rotation on network errors
Implement ProxyProvider and pass it via TransportSettings:
from insta_wizard import TransportSettings, ProxyInfo
from insta_wizard.common.interfaces import ProxyProvider
class MyProxyPool(ProxyProvider):
async def provide_new(self) -> ProxyInfo | None:
return ProxyInfo.from_string(fetch_next_from_pool())
settings = TransportSettings(
max_retries_on_network_errors=3,
delay_before_retries_on_network_errors=1.0,
change_proxy_after_all_failed_attempts=True,
proxy_provider=MyProxyPool(),
)
async with MobileInstagramClient(transport_settings=settings) as client:
...
When all retry attempts are exhausted, the client calls proxy_provider.provide_new() and retries with the new proxy.
Session state: dump & load
Persist session between runs — no need to re-login every time:
import json
from insta_wizard import MobileInstagramClient
# Save after login
async with MobileInstagramClient() as client:
await client.account.login("username", "password")
state = client.dump_state()
with open("session.json", "w") as f:
json.dump(state, f)
# Restore on next run
async with MobileInstagramClient() as client:
with open("session.json") as f:
client.load_state(json.load(f))
me = await client.account.get_current_user() # already authenticated
State is a plain Python dictionary — you can work with it directly:
state = client.dump_state() # returns dict
client.load_state(state) # accepts dict
dump_state/load_statedo not include proxy or transport settings — pass those in the constructor as usual.
TransportSettings
from insta_wizard import TransportSettings
settings = TransportSettings(
max_network_wait_time=30.0, # request timeout in seconds
max_retries_on_network_errors=3,
delay_before_retries_on_network_errors=1.0,
)
async with MobileInstagramClient(transport_settings=settings) as client:
...
Mobile client
Device presets
from insta_wizard import AndroidDeviceInfo, MobileInstagramClient
from insta_wizard.mobile.models.android_device_info import AndroidPreset
# from a preset
device = AndroidDeviceInfo.from_preset(AndroidPreset.SAMSUNG_A16)
# with overrides
device = AndroidDeviceInfo.from_preset(AndroidPreset.PIXEL_8, locale="ru_RU", timezone="Europe/Moscow")
# random preset
device = AndroidDeviceInfo.random()
async with MobileInstagramClient(device=device) as client:
...
Available presets: SAMSUNG_A16, SAMSUNG_S23, SAMSUNG_A54, PIXEL_8, REDMI_NOTE_13_PRO.
Local data
MobileClientLocalData holds cookies, auth tokens, and device IDs generated during login:
from insta_wizard import MobileInstagramClient, MobileClientLocalData
local_data = MobileClientLocalData.create() # fresh, empty
client = MobileInstagramClient(local_data=local_data)
# read it back later
local_data = client.get_local_data()
Sync wrapper
For scripts where async is inconvenient:
from insta_wizard.mobile.sync_client import MobileInstagramClientSync
with MobileInstagramClientSync() as client:
client.account.login("username", "password")
user = client.users.get_info_by_username("someuser")
print(user)
All section methods work without await. Internally runs a background event loop thread.
Web client
Emulates Chrome browser. Works with the Instagram web API.
Device presets
from insta_wizard import BrowserDeviceInfo, WebInstagramClient
from insta_wizard.web.models.device_info import BrowserPreset
device = BrowserDeviceInfo.from_preset(BrowserPreset.CHROME_143_WIN11)
device = BrowserDeviceInfo.from_preset(BrowserPreset.CHROME_143_MACOS, locale="ru_RU")
device = BrowserDeviceInfo.random()
async with WebInstagramClient(device=device) as client:
...
Available presets: CHROME_143_WIN11, CHROME_143_MACOS.
Initialization
Before login, call initialize_state() — fetches initial cookies the browser normally receives on first page load:
from insta_wizard import WebInstagramClient
async with WebInstagramClient() as client:
await client.initialize_state()
# ready to login
Cookies
# inject existing cookies (e.g. from a previous session)
client.set_cookies({"sessionid": "...", "csrftoken": "...", "mid": "..."})
# read current cookies
cookies = client.get_cookies() # dict
Login
import asyncio
from insta_wizard import WebInstagramClient
from insta_wizard.web.flows.login import Login
from insta_wizard.web.exceptions import CheckpointRequiredError
async def main():
async with WebInstagramClient() as client:
await client.initialize_state()
try:
await client.execute(Login(username="...", password="..."))
except CheckpointRequiredError:
# Automated checkpoint passing is planned — see Roadmap
pass
cookies = client.get_cookies()
asyncio.run(main())
Exceptions
Key exceptions to handle:
Base (insta_wizard):
| Exception | Description |
|---|---|
InstaWizardError |
Base class for all library errors |
Mobile (insta_wizard.mobile.exceptions):
| Exception | When |
|---|---|
ChallengeRequiredError |
Instagram requires a challenge (captcha / 2FA) |
LoginError |
Authorization failed |
LoginBadPasswordError |
Wrong password |
TooManyRequestsError |
Rate limited (HTTP 429) |
FeedbackRequiredError |
Action blocked by Instagram |
UnauthorizedError |
Session invalid or expired |
NetworkError |
Network connectivity issue |
NotFoundError |
Resource not found |
Web (insta_wizard.web.exceptions):
| Exception | When |
|---|---|
CheckpointRequiredError |
Checkpoint on login |
LoginError |
Authorization failed |
LoginBadPasswordError |
Wrong password |
TooManyRequestsError |
Rate limited (HTTP 429) |
NetworkError |
Network connectivity issue |
StateParametersMissingError |
State not initialized (call initialize_state() first) |
Transport (insta_wizard.common.transport.exceptions):
| Exception | When |
|---|---|
TransportTimeoutError |
Request timed out |
TransportNetworkError |
Low-level network error |
Development
python dev.py fix # ruff check --fix + ruff format
python dev.py lint # ruff check only
python dev.py type # mypy
python dev.py test # pytest
python dev.py check # lint + type + test
Roadmap
Planned features and improvements:
- Automated checkpoint passing
- Account registration
- More Instagram API methods
- httpcloak transport support (TLS fingerprint spoofing for stronger browser emulation)
Disclaimer
This project is a developer tool for building personal integrations and exploring the Instagram API. It is not designed or intended for automation, mass botting, spamming, or any activity that violates Instagram's Terms of Service. We are not affiliated with Meta or Instagram. Use only with accounts and data you have the right to access. Comply with all applicable laws and platform rules.
License
MIT — see LICENSE
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file insta_wizard-0.1.0.tar.gz.
File metadata
- Download URL: insta_wizard-0.1.0.tar.gz
- Upload date:
- Size: 136.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aa49ffa12b6615cae0bd306b2b3b8e63fc8ba9cc305dd47a8addc27b7bcdd5b5
|
|
| MD5 |
f24d5a282f15c80c4a317fdd45c009da
|
|
| BLAKE2b-256 |
8b0217c8aa64f01a54879a8a0bde740e23220dbb45341bdb3753ca7ae7747a72
|
File details
Details for the file insta_wizard-0.1.0-py3-none-any.whl.
File metadata
- Download URL: insta_wizard-0.1.0-py3-none-any.whl
- Upload date:
- Size: 261.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3a0027d23e90e94b593798a37b03e87640df20c08bba1d1744b4cf320a2d6598
|
|
| MD5 |
0acd4ec15d162042d78d1ca9aa4c8238
|
|
| BLAKE2b-256 |
829019071f3221e6b470b44e3d3b8705b24a4534a1482a065bad2b07ce54733a
|