Appearance
Your first program
In about 15 minutes you install the Simulator, log an account in, save its session, and like a post from the saved session. Every value in the code is explained below it.
At the end you have an account logged in once, its session saved in session.json, and a second run that likes the first post of the home feed without logging in again.
Before you start
- The Simulator installed (Get access and install), with
IGPAPI_LICENSE_TOKENset. - A test account first, not the account you care about (Account safety and pacing).
- The account's name and password in the environment:
IG_USERNAMEandIG_PASSWORD. If the account uses an authenticator app, you also need its setup key (Log in); this page uses an account without a second step.
bash
export IG_USERNAME='your_test_account'
export IG_PASSWORD='its password'Step 1: log in and save the session
Save this as login.ts.
ts
import { AndroidIgpapi, FeedTimelineScreen } from '@igpapi/android';
import { Effect } from 'effect';
import { writeFile } from 'node:fs/promises';
const username = process.env['IG_USERNAME'] ?? '';
const password = process.env['IG_PASSWORD'] ?? '';
// One call is one app process: a fresh install, the login, and the checkpoint of the feed it reaches.
const checkpoint = await AndroidIgpapi.install({
seed: username,
main: home =>
Effect.gen(function* () {
const next = yield* home.logIn({ username, password });
// An account with a second step shows another screen: see "Log in".
if (!FeedTimelineScreen.is(next)) return undefined;
return yield* next.checkpoint();
}),
});
if (checkpoint === undefined) {
console.log('Instagram asked for a second step: see "Log in".');
} else {
await writeFile('session.json', JSON.stringify(checkpoint));
console.log('The session is saved in session.json.');
}What each name is:
AndroidIgpapi.installstarts the Instagram Android app as a fresh install on a virtual phone, runs your program, and closes the app when the program ends. The call resolves with whatmainreturns.seednames the virtual phone. Use a stable string for an account (its username is a good one): the same account then always gets the same phone. Instagram recognises a phone it has seen before.mainis your program. It receives the first screen the app shows, which for a fresh install is the login screen, and it is an Effect:Effect.gen(function* () { … })withyield*in front of every step.homeis the handle of that screen.home.logIn({ username, password })types the username and password and taps Log in. It continues into the app's start-up by itself and answers the next screen: the home feed for an ordinary account.FeedTimelineScreen.is(next)asks whether that screen is the home feed. An account with a second step shows another screen (see Log in).next.checkpoint()returns the session and the screens you have open, as an object. It is a secret: it holds the account's credentials. This program writes it to a file; a real project stores it as a secret.
Run it:
bash
npx tsx login.tsIt prints The session is saved in session.json. and writes the file.
Step 2: start from the session and like a post
Save this as like.ts.
ts
import { AndroidIgpapi, FeedTimelinePostItem, FeedTimelineScreen } from '@igpapi/android';
import { Effect } from 'effect';
import { readFile } from 'node:fs/promises';
// The checkpoint the first program saved: an object, parsed from the file.
const saved = JSON.parse(await readFile('session.json', 'utf8'));
// A stored session is a new app process: `launch` starts the app again for the saved account.
const liked = await AndroidIgpapi.launch({
checkpoint: saved,
screen: FeedTimelineScreen,
main: feed =>
Effect.gen(function* () {
// The post in front, or the first post after a few swipes (an ad or a Reel can come first).
let item = feed.current;
for (let swipes = 0; item !== undefined && !FeedTimelinePostItem.is(item) && swipes < 10; swipes += 1) {
item = yield* feed.swipeToNext();
}
if (item === undefined || !FeedTimelinePostItem.is(item)) return undefined;
const post = yield* item.open();
yield* post.like();
return post.data.mediaId;
}),
});
console.log(liked === undefined ? 'No post came in front.' : `Liked the post ${liked}.`);What each name is:
JSON.parse(…)turns the file back into the object the entry takes. An entry never takes JSON text.AndroidIgpapi.launchstarts the app again for a saved account, the way a person opens the app after it was closed. It does not log in: the saved session is used, and the app sends what it sends when it starts.screen: FeedTimelineScreennames the screen you expect.mainthen receives exactly that screen's handle, with no narrowing.feed.currentis the post, Reel or ad in front;feed.swipeToNext()brings the next one in front, as a swipe does.FeedTimelinePostItem.is(item)keeps only posts.item.open()opens the post screen,post.like()likes it, andpost.dataholds what the screen shows.
Run it:
bash
npx tsx like.tsIt prints Liked the post <id>. The post is liked on the account.
If something goes wrong
| What you see | What it is | What to do |
|---|---|---|
The call rejects with IgLoginBadPasswordError | The password is wrong. | Fix IG_PASSWORD. Retrying the same password only adds failed attempts. |
Instagram asked for a second step | The account has two-factor authentication or Instagram wants another approval. | Log in. |
The call rejects with an UnexpectedScreenError | Instagram showed a screen your program does not describe, usually a challenge. | Challenges: answer them once with otherwise. |
Please, provide license token as … | IGPAPI_LICENSE_TOKEN is not set. | Get access and install. |
Every error has a page: Errors.
Next
- A complete session, every error answered: the same program, with two-factor, the feed, comments and Reels, and every error answered.
- How the Simulator works: the model behind the code, in one page.
- Screens: everything a person can do, screen by screen.
- Notes for coding agents.