Appearance
@igpapi/ios
The API reference of @igpapi/ios
iOS platform of igpapi, the Instagram private API Simulator for Node.js. It covers the phone's signals, request signatures, session handling, and a large part of the mobile API surface.
The iOS package is not the priority direction. It is implemented less exhaustively than the Android one, and it has no screens. The guides and the screen pages of this site describe the Android package.
Install
Get access and install sets up the registry, the token and the shared effect. Then:
bash
npm install @igpapi/ios [email protected]The entries
IosIgpapi.install({ …options, main }) and IosIgpapi.launch({ snapshot, …options, main }) run one app process for the length of one program and resolve with its result as a plain Promise, the way the Android entries do. Nothing is built, and nothing is sent, before the call; the process closes when the program ends; a typed failure rejects with the very same tagged error object.
main takes no screen, because iOS has no screens: it is a function returning an Effect over the package's effects.
installis a fresh install. It sends nothing of its own: the login runs its pre-login steps itself, so do not run them by hand before it.launchis a cold start from a stored snapshot (an object, never JSON text). It sends the app's launch (IosAppLaunchFlow) beforemainbegins, which the app does on every start of a saved session. Hitting feature endpoints from a restored session that has not been used for hours, with no launch before them, is a strong anti-abuse signal.- There are no
resume…entries: they continue a checkpoint on the screen it was taken on, and no iOS screen can be checkpointed.
The effects you run inside main: IosLogin (the login), IosPostLoginFlow (the requests the app sends once after a login; run it after the login), IosAppLaunchFlow (the launch), the Direct effects below, ExportSnapshot, and the shared mobile effects such as UsersInfo and the feeds.
ts
import { ExportSnapshot, IosIgpapi, IosLogin, IosPostLoginFlow } from '@igpapi/ios';
import { Effect } from 'effect';
import { readFile, writeFile } from 'node:fs/promises';
const username = process.env['IG_USERNAME'] ?? '';
const password = process.env['IG_PASSWORD'] ?? '';
// The first run: a fresh install logs in and exports the snapshot.
// `IosLogin` runs the pre-login steps itself: do not run them by hand before it.
const snapshot = await IosIgpapi.install({
seed: username,
main: () =>
Effect.gen(function* () {
yield* IosLogin({ username, password });
yield* IosPostLoginFlow();
return yield* ExportSnapshot;
}),
});
await writeFile('snapshot.json', JSON.stringify(snapshot));
// Every later run: `launch` starts the app again for the stored snapshot (an object, never JSON text)
// and sends the app's launch before `main` begins.
const saved = JSON.parse(await readFile('snapshot.json', 'utf8'));
const answer = await IosIgpapi.launch({
snapshot: saved,
main: () => Effect.succeed('launched'),
});
console.log(answer);ExportSnapshot reads the running process, so it runs inside the program whose session it should carry; the snapshot it returns is a copy. A stored snapshot that cannot be restored rejects the call with the typed SnapshotDecodeError: handle it, usually by logging in again. A challenge that interrupts the launch rejects the call (IosIgpapiStartError): iOS has no screens to hand it to.
IosAppLaunchFlow also keeps a few values in the session for the next launch (a launch counter, the last configuration sync, the last peak-hour answer), so export the snapshot after it runs.
Direct
Opening a Direct screen sends its own requests. The ids come from your own records of the account's inbox.
ts
import { IosDirectInboxOpen, IosDirectThreadDetailsOpen, IosDirectThreadOpen, IosIgpapi } from '@igpapi/ios';
import { Effect } from 'effect';
import { readFile } from 'node:fs/promises';
const saved = JSON.parse(await readFile('snapshot.json', 'utf8'));
// The ids of a conversation and of the person in it come from your own records of the account's inbox.
const threadId = process.argv[2] ?? '';
const peerUserId = process.argv[3] ?? '';
await IosIgpapi.launch({
snapshot: saved,
main: () =>
Effect.gen(function* () {
// Opening a Direct screen sends its own requests.
yield* IosDirectInboxOpen();
yield* IosDirectThreadOpen({ threadId, peerUserId });
yield* IosDirectThreadDetailsOpen({ threadId, peerUserId });
}),
});The app layer
IosApp(options) is the Instagram iOS app on a virtual phone as an Effect Layer, for a program that composes the effects itself. One build of the layer is one app process: provide it once around the whole program, or keep it in a ManagedRuntime you own and dispose to run several programs in the same process (Compose the app layer yourself shows the pattern with AndroidApp).
The connection protocol
The iOS process tries HTTP/3 like the iOS app and falls back to HTTP/2 when QUIC does not get through. ProtocolDiagnostics shows which one each host uses (Connection protocol).
Requirements
- Node.js ≥ 22
- TypeScript ≥ 7 (the published declarations are checked against TypeScript 7)
- the
IGPAPI_LICENSE_TOKENenvironment variable
Related packages
@igpapi/android: the Android variant@igpapi/core: installed with this package