Metadata-Version: 2.4
Name: insta-wizard
Version: 0.1.0
Summary: Async Python client for Instagram private mobile and web API
Author-email: 5ou1e <example@example.org>
License-Expression: MIT
Project-URL: Homepage, https://github.com/5ou1e/insta-wizard
Project-URL: Repository, https://github.com/5ou1e/insta-wizard
Project-URL: Bug Tracker, https://github.com/5ou1e/insta-wizard/issues
Keywords: instagram,instagram-api,instagram-client,instagram api client,instagram api,instagram client,instagram-private-api,instagram-private,instagram-web,instagram-bot,instagram-automation,instagram-scraper,instagram-scraper,instagram-android,async-client,async-instagram,ig-client,ig-private
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Framework :: AsyncIO
Classifier: Topic :: Internet
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: yarl<2.0,>=1.9.0
Requires-Dist: aiohttp<4.0,>=3.13.3
Requires-Dist: backports.zstd>=1.2.0
Requires-Dist: orjson<4.0,>=3.9.0
Requires-Dist: mashumaro<4.0,>=3.20
Requires-Dist: pytz>=2023.3
Requires-Dist: Babel<3.0,>=2.12.0
Requires-Dist: PyNaCl<2.0,>=1.5.0
Requires-Dist: pycryptodomex<4.0,>=3.15.0
Requires-Dist: pyparsing<4.0,>=3.0.0
Requires-Dist: pydantic<3.0,>=2.0
Requires-Dist: aiofiles
Requires-Dist: rich
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Requires-Dist: ruff>=0.6; extra == "dev"
Requires-Dist: mypy>=1.10; extra == "dev"
Requires-Dist: python-dotenv<2.0,>=1.0; extra == "dev"
Dynamic: license-file

# insta-wizard

[Русский](README.ru.md)

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):

```bash
pip install git+https://github.com/5ou1e/insta-wizard.git
```

---

## Quick start

```python
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:

```python
# 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:

```python
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

```python
from insta_wizard.mobile import print_help

print_help()  # prints a table: command name, module, signature
```

For web:

```python
from insta_wizard.web import print_help

print_help()
```

---

## Common features

### Proxy

```python
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:

```python
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`:

```python
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:

```python
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:

```python
state = client.dump_state()  # returns dict
client.load_state(state)     # accepts dict
```

> `dump_state` / `load_state` do not include proxy or transport settings — pass those in the constructor as usual.

### TransportSettings

```python
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

```python
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:

```python
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:

```python
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

```python
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:

```python
from insta_wizard import WebInstagramClient

async with WebInstagramClient() as client:
    await client.initialize_state()
    # ready to login
```

### Cookies

```python
# inject existing cookies (e.g. from a previous session)
client.set_cookies({"sessionid": "...", "csrftoken": "...", "mid": "..."})

# read current cookies
cookies = client.get_cookies()  # dict
```

### Login

```python
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

```bash
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](https://help.instagram.com/581066165581870). 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](LICENSE)
