Skip to content

Interactions

aiograpi provides various types of Interactions that can be used to control how the program will interact with the Instagram:

  • Media - Media (Photo, Video, Album, IGTV, Reels or Story)
  • Types - Field reference for public aiograpi.types models
  • Resource - Part of Media (for albums)
  • MediaOembed - Short version of Media
  • Account - Full private info for your account (e.g. email, phone_number)
  • TOTP - 2FA TOTP helpers (generate seed, enable/disable TOTP, generate code as Google Authenticator)
  • User - Full public user data
  • UserShort - Short public user data (used in Usertag, Comment, Media, Direct)
  • Usertag - Tag user in Media (coordinates + UserShort)
  • Location - GEO location (GEO coordinates, name, address)
  • Hashtag - Hashtag object (id, name, picture)
  • Collection - Collection of medias (name, picture and list of medias)
  • Comment - Comments to Media
  • Highlight - Highlights
  • Story - Story
  • StoryLink - Link (Swipe up)
  • StoryLocation - Tag Location in Story (as sticker)
  • StoryMention - Mention users in Story (user, coordinates and dimensions)
  • StoryHashtag - Hashtag for story (as sticker)
  • StorySticker - Tag sticker to story (for example from giphy)
  • StoryBuild - StoryBuilder return path to photo/video and mention co-ordinates
  • DirectThread - Thread (topic) with messages in Direct
  • DirectMessage - Message in Direct
  • Insight - Insights for a post
  • Track - Music track (for Reels/Clips)

Interacting with Instagram Account

aiograpi provides the following Interactions that can be used to control and get the information about your Instagram account:

  • Client(settings: dict = {}, proxy: str = "", tls_verify: Union[bool, str] = True): bool - Init aiograpi client
await cl.login("aiograpi", "42")
# await cl.login("aiograpi", "42", verification_code="123456")  # with 2FA verification_code
# await cl.login_by_sessionid("peiWooShooghahdi2Eip7phohph0eeng")
cl.set_proxy("socks5://127.0.0.1:30235")
# cl.set_proxy("http://username:password@127.0.0.1:8080")
# cl.set_proxy("socks5://username:password@127.0.0.1:30235")
# when addressing the proxy via hostname:
# cl.set_proxy("socks5h://username:password@exampleproxy.tld:30235")

print(cl.get_settings())
print(await cl.user_info(cl.user_id))

We recommend using these proxies

Request

Property Description
request_logger Logger in which various actions from Instagram are registered
request_timeout Timeout in seconds between requests (1 second by default)
private_transport Private mobile transport: native async curl by default for HTTP/2; requests selects the previous HTTPX transport
public_transport Public web transport: requests-compatible async transport by default, or curl when aiograpi[curl] is installed
public_transport_impersonate Browser fingerprint used by the optional curl public transport
tls_verify TLS certificate verification: True by default, False for temporary trusted MITM debugging, or a CA bundle path

Login

Method Return Description
login(username: str, password: str) bool CAA login by username and password; validate and reuse a saved session when present
login(username: str, password: str, verification_code: str) bool CAA login with a supported TOTP, SMS, backup or profile verification code
login_legacy(username: str, password: str, relogin: bool = False, verification_code: str = "") bool Explicit compatibility entry point for the previous login flow
relogin() bool Re-login with clean cookies (required cl.username and cl.password)
login_by_sessionid(sessionid: str) bool Lightweight compatibility login using a session cookie value
inject_sessionid_to_public() bool Inject sessionid from Private Session to Public Session
logout() bool Logout

login() uses CAA directly and does not automatically fall back to login_legacy(). Both entry points accept the same arguments. See the login migration guide for compatibility and saved-session behavior.

login_by_sessionid() only works when Instagram accepts that sessionid for the private mobile API. A browser/web sessionid can be rejected with login_required or invalidated server-side; for long-lived automation, prefer login() once, then dump_settings() and reuse the saved settings.

When login_by_sessionid() needs to recover the account username after a private profile lookup failure, it tries the private mobile profile stream before falling back to public/web GraphQL.

You can pass settings to the Client (and save cookies), it has the following format:

settings = {
   "uuids": {
      "phone_id": "57d64c41-a916-3fa5-bd7a-3796c1dab122",
      "uuid": "8aa373c6-f316-44d7-b49e-d74563f4a8f3",
      "client_session_id": "6c296d0a-3534-4dce-b5aa-a6a6ab017443",
      "advertising_id": "8dc88b76-dfbc-44dc-abbc-31a6f1d54b04",
      "device_id": "android-e021b636049dc0e9"
   },
   "cookies":  {},  # set here your saved cookies
   "last_login": 1596069420.0000145,
   "device_settings": {
      "cpu": "h1",
      "dpi": "640dpi",
      "model": "h1",
      "device": "RS988",
      "resolution": "1440x2392",
      "app_version": "117.0.0.28.123",
      "manufacturer": "LGE/lge",
      "version_code": "168361634",
      "android_release": "6.0.1",
      "android_version": 23
   },
   "user_agent": "Instagram 117.0.0.28.123 Android (23/6.0.1; ...US; 168361634)",
   "public_transport": "requests",
   "private_transport": "curl",
   "public_transport_impersonate": "chrome136",
   "tls_verify": True
}

cl = Client(settings)

This example contains an older app profile without a Bloks hash. Before a fresh CAA login, follow the older-profile migration steps.

Settings

Store and manage uuids, device configuration, user agent, authorization data (aka cookies) and other session settings

Method Return Description
get_settings() dict Return settings dict
set_settings(settings: dict) bool Set session settings
load_settings(path: Path, override_app_version: bool = False) dict Load session settings; optionally update the app version, version code and Bloks hash
dump_settings(path: Path) bool Serialize and save session settings to file

In order for Instagram to trust you more, you must always login from one device and one IP (or from a subnet):

cl = Client()
await cl.login(USERNAME, PASSWORD)
cl.dump_settings('/tmp/dump.json')

Next time:

cl = Client()
cl.load_settings('/tmp/dump.json')
await cl.login(USERNAME, PASSWORD)
cl.dump_settings('/tmp/dump.json')

Manage device, proxy and other account settings

Method Return Description
set_proxy(dsn: str) dict Support socks and http/https proxy "scheme://username:password@host:port". We recommend using these proxies
private.proxy dict Stores used proxy server for private (mobile, v1) requests
public.proxy dict Stores used proxy server for public (web, graphql) requests
set_device(device: dict) bool Change device settings (Android Device Information Generator Online)
device dict Return device dict which we pass to Instagram
set_user_agent(user_agent: str = "") bool Change User-Agent header (User Agents)
cookie_dict dict Return cookies
user_id int Return your user_id (after login)
base_headers dict Base headers for Instagram
set_country(country: str = "US") bool Set country (advice: use the country of your proxy)
set_country_code(country_code: int = 1) bool Set country calling code. Default: +1 (USA)
set_locale(locale: str = "en_US") bool Set locale (advice: use the locale of your proxy)
set_timezone_offset(seconds: int) bool Set timezone offset in seconds
set_retry_config(...) bool Configure request timing, retry settings and public/private transport choices
set_tls_verify(tls_verify: bool | str) bool Update TLS certificate verification for existing public, private and GraphQL sessions
cl = Client()

# Los Angles user:
cl.set_proxy('http://los:angeles@proxy.address:8080')
cl.set_locale('en_US')
cl.set_timezone_offset(-7 * 60 * 60)  # Los Angeles UTC (GMT) -7 hours == -25200 seconds
cl.get_settings()
{
    ...
    'user_agent': 'Instagram 194.0.0.36.172 Android (26/8.0.0; 480dpi; 1080x1920; Xiaomi; MI 5s; capricorn; qcom; en_US; 301484483)',
    'country': 'US',
    'country_code': 1,
    'locale': 'en_US',
    'timezone_offset': -25200
}

# Moscow user:
cl.set_proxy('socks5://moscow:proxy@address:8080')
cl.set_locale('ru_RU')
cl.set_country_code(7)  # +7
cl.set_timezone_offset(3 * 3600)  # Moscow UTC+3
cl.get_settings()
{
    ...
    'user_agent': 'Instagram 194.0.0.36.172 Android (26/8.0.0; 480dpi; 1080x1920; Xiaomi; MI 5s; capricorn; qcom; ru_RU; 301484483)',
    'country': 'RU',
    'country_code': 7,
    'locale': 'ru_RU',
    'timezone_offset': 10800
}

Optional curl public transport

For public web endpoints that are sensitive to browser TLS fingerprints, install the optional curl transport:

pip install "aiograpi[curl]"

Then opt in explicitly:

cl = Client(public_transport="curl", public_transport_impersonate="chrome136")

The default remains public_transport="requests". Configure private mobile API requests separately with private_transport. See Public Transport for live comparison results and caveats.

Private HTTP/2 transport

The standard installation includes curl_cffi. Client() sends private mobile API requests over HTTP/2 with an ALPN offer containing only h2, including CAA login. Mobile request headers and account device settings are preserved.

from aiograpi import Client

cl = Client()
cl.load_settings("session.json")  # if you have saved settings
await cl.login(USERNAME, PASSWORD)
cl.dump_settings("session.json")

Transport selection is saved in settings. An explicit saved choice overrides the constructor; settings without this field preserve the constructor choice, which defaults to curl. To migrate settings that explicitly saved requests, call cl.set_retry_config(private_transport="curl") after loading them, then save again. Client(private_transport="requests") selects the previous private transport. Public and GraphQL transports have their own configuration.

Requirements: curl_cffi>=0.15.0 and its bundled libcurl >= 8.10.0. No system curl executable is needed. Availability depends on curl_cffi wheels for your platform; Android/Termux has not been verified.

The TLS offer includes the hybrid X25519MLKEM768 group alongside X25519, P-256 and P-384 to address connection failures on some proxy paths. Servers without hybrid support can still select a classical group, while ALPN offers only h2.

HTTPX prepares requests, scopes cookies, follows redirects and decodes responses. Curl maintains native asynchronous connections with no automatic retries, so an interrupted password POST is not resubmitted by the legacy incomplete-read handler. Curl network errors become the existing ConnectProxyError; an HTTP 429 remains ClientThrottledError with its response.

The curl session uses the explicitly configured proxy and tls_verify, ignoring environment proxy and CA overrides. Set a trusted CA bundle with Client(tls_verify="/path/to/ca.pem"). Changing transport, proxy or TLS settings preserves the curl session's cookies; replaced async clients are closed on the next awaited request or session close. Change configuration between requests, not while requests are in flight.

Curl buffers responses. HTTPX numeric connect/read timeout values map to curl's connect/read tuple, with a total transfer budget equal to their sum; write/pool limits are not separate curl timers. timeout=None is unlimited. Mixed numeric/None connect/read values are rejected before I/O. Native asyncio cancellation propagates normally.

This option addresses a reproduced transport-sensitive empty-429 login case. It does not bypass verification, suspension or rate limits, and does not guarantee that rejected saved sessions will become valid.

Private mobile headers

base_headers follows the current supported Android app profile for normal private API requests, including static transport/network hints such as X-FB-HTTP-Engine, X-Tigon-Is-Retry, and X-Zero-*.

Device-bound or attestation-style headers such as x-meta-zca, x-meta-usdid, and x-ig-attest-params are not generated by default. These values are tied to Android / Google Play / device signing context, so adding fake static values is more likely to hurt trust than help.

TLS verification and debugging proxies

By default aiograpi verifies TLS certificates on all client sessions. Keep this enabled for production, residential proxies, and normal CONNECT-tunnel proxies.

If you use a trusted local debugging proxy that intercepts TLS, prefer installing its CA certificate and pointing the client at that bundle:

cl = Client(tls_verify="/path/to/proxy-ca.pem")

For short local captures you can disable verification explicitly:

cl = Client(tls_verify=False)

Do not disable TLS verification on untrusted networks or shared proxies because session cookies and passwords can be intercepted.

What's Next?