@luisurrutia/gram
v0.1.0
Published
TypeScript ESM CLI for working with the Instagram account you are already logged into
Downloads
89
Readme
Gram
Gram is a TypeScript ESM CLI for working with the Instagram account you are already logged into. It can show account details, list relationships, list post likers, read the home timeline, inspect one post or reel, read media comments and replies, manage blocked accounts, read or set a Note, and update the profile bio.
The package is @luisurrutia/gram and requires Node >=22. When installed or built from this repo, it exposes the gram bin from dist/index.js.
What it does
- Reads the logged-in account with
whoami. - Lists following and followers for the current account or a target user.
- Lists accounts that liked one Instagram post.
- Prints safe fields from the logged-in home timeline.
- Prints safe details for one Instagram post or reel.
- Prints safe fields from a media comments page.
- Prints safe fields from comment replies on a media comment.
- Lists blocked accounts for the logged-in user.
- Reads the current Instagram Note and sets an Instagram Note, with
--dry-runavailable for set previews. - Updates the profile bio, blocks users, and unblocks users only after
--yesconfirmation. - Loads cookies from Chrome, Arc, Brave, Safari, Firefox, a manual Cookie header, an environment variable, or saved config.
Run and build
Run Gram without a global install:
npx @luisurrutia/gram --help
# or
bunx @luisurrutia/gram --helpThe package exposes a single gram bin, so package runners invoke the CLI directly.
Or install and run it from a checkout.
pnpm install --frozen-lockfile
pnpm run buildAfter a build, run the compiled CLI directly:
node dist/index.js help
node dist/index.js whoami --browser chromeDuring development, run the source entry with pnpm run dev --:
pnpm run dev -- help
pnpm run dev -- note set "gm" --browser chrome --dry-runThe examples below use gram because that is the bin name after the package is built and linked or installed from this repo.
Publishing
@luisurrutia/gram is published from .github/workflows/publish-npm.yml with npm Trusted Publishing. Configure the package on npmjs.com with these Trusted Publisher fields:
- Publisher: GitHub Actions
- Organization/user:
LuisUrrutia - Repository:
gram - Workflow filename:
publish-npm.yml - Environment name: leave empty unless the workflow gains a matching GitHub environment
- Allowed action:
npm publish
The workflow publishes with GitHub OIDC, so it does not need an NPM_TOKEN secret. Create a GitHub release whose tag matches package.json as vX.Y.Z; the release workflow validates, packs, and publishes that exact tag.
Safety and privacy
gram noteis read-only and prints only the current Note text plus created timestamp/date when present.gram note setsends a live mutation by default. Use--dry-runfirst unless you already mean to update the Note.gram profile bio set,gram blocked block, andgram blocked unblockrequire--yes.- Read-only commands are
whoami,following,followers,likers,timeline,post,comments,replies,note, andblocked list. blocked listis read-only, but it still makes authenticated private-web requests.- Gram does not print raw cookies, session IDs, CSRF tokens, LSD tokens,
fb_dtsg,jazoest, email, phone number, profile image URLs, raw response JSON, or raw private-web payloads. gram commentsreads comment and caption text plus safe identity and pagination fields only; it does not fetch child comments or print private-web payloads.gram comments --allfollows headload and tail cursors until comments are exhausted and ignores--limitas the stop condition.gram repliesreads safe parent comment and reply fields from the child comments endpoint only; it does not fetch deeper nested replies or print private-web payloads.- Output keeps safe identity fields such as username, display name, user ID, and private or verified markers.
- Treat
--verbose,--media-links, and live cursor output as sensitive enough that you should not share it casually.
Use Gram only with accounts you control, and assume private-web behavior can change without notice.
Quick Start
Save a cookie source, then check which account Gram can see:
gram setup --browser chrome
gram whoamiRead a Note, or preview a Note change without sending it:
gram note
gram note set "gm" --dry-runRead relationship, liker, or timeline data:
gram following -n 20
gram followers --user <handle> -n 20
gram likers https://www.instagram.com/p/DYj22UUMMYZ/ -n 20
gram timeline -n 20
gram post <shortcode-or-url>
gram comments <media-id|post-url|shortcode> -n 20
gram comments <media-id|post-url|shortcode> --all
gram replies <media-id|post-url|shortcode> <comment-id> -n 20See blocked accounts:
gram blocked listCommon Workflows
Set up a browser cookie source once:
gram setup
gram setup --browser chrome --profile "Default"Inspect the current account and its relationships:
gram whoami
gram following -n 50
gram followers --user <id-or-handle> -n 50Inspect accounts that liked one Instagram post:
gram likers DYj22UUMMYZ -n 20Read the home timeline, then continue with the cursor Gram prints when another page is available:
gram timeline -n 20
gram timeline --after '<cursor-from-previous-output>'Inspect one post or reel without printing media CDN URLs by default:
gram post <shortcode-or-url>
gram post https://www.instagram.com/reel/<shortcode>/ --media-linksRead comments for a media id, post URL, or shortcode, then continue with the cursor Gram prints when another page is available:
gram comments https://www.instagram.com/p/<shortcode>/ -n 20
gram comments <media-id> --min-id '<cursor-from-previous-output>'
gram comments <media-id> --max-id '<cursor-from-previous-output>'
gram comments <media-id> --all --page-size 50Read replies for a parent comment, then continue with the cursor Gram prints when another page is available:
gram replies https://www.instagram.com/p/<shortcode>/ <comment-id> -n 20
gram replies <media-id> <comment-id> --min-id '<cursor-from-previous-output>'
gram replies <media-id> <comment-id> --all --page-size 50Read the current Note, then preview changes first. Remove --dry-run only when you mean to update the live Instagram Note:
gram note
gram note set "working on it" --dry-runRun confirmed account changes only when you intend to mutate the logged-in account:
gram profile bio set "new bio text" --yes
gram blocked block <id-or-handle> --yes
gram blocked unblock <id-or-handle> --yesCommand Reference
gram setup [options]
gram whoami [options]
gram following [options]
gram followers [options]
gram likers <post-url-or-shortcode> [options]
gram timeline [options]
gram post <shortcode-or-url> [options]
gram comments <media-id|post-url|shortcode> [options]
gram replies <media-id|post-url|shortcode> <comment-id> [options]
gram note [options]
gram note set <text> [options]
gram profile bio set <text> --yes [options]
gram blocked list [options]
gram blocked block <id|handle> --yes [options]
gram blocked unblock <id|handle> --yes [options]
gram help [command]gram setup
Detects a usable Instagram cookie source, validates it with a non-mutating whoami request, and saves cookie-source metadata. It does not save raw cookies or web tokens.
Useful options:
--browser <chrome|arc|brave|safari|firefox>--profile <name>
gram whoami
Shows the logged-in Instagram account using non-mutating requests. It requires cookies containing at least sessionid.
Useful options:
--browser <chrome|arc|brave|safari|firefox>--profile <name>--cookie-header <header>--user-agent <ua>
gram following and gram followers
Lists accounts a target user follows, or accounts following a target user. If --user is omitted, Gram uses ds_user_id from cookies. Numeric IDs are used directly. Handles, with or without @, are resolved first.
Useful options:
--user <id|handle>-n, --limit <count>from 1 to 100, default 20- Common cookie options such as
--browser,--profile,--cookie-header, and--user-agent
gram likers
Lists accounts that liked one Instagram post using authenticated, read-only Instagram /api/graphql doc_id requests. It requires cookies containing sessionid; csrftoken, LSD, fb_dtsg, and jazoest are sent when available.
Useful options:
-n, --limit <count>from 1 to 100, default 20; caps displayed rows unless--allis used--allrequests the current doc_id likers response once and prints every returned liker--after <cursor>is accepted for parser compatibility but currently unsupported because this doc_id has no verified cursor- Common cookie options such as
--browser,--profile,--cookie-header, and--user-agent
The positional post reference can be a canonical Instagram /p/, /reel/, or /tv/ URL, a path such as /reel/<shortcode>/, or a bare shortcode. Long share-style tokens inside supported Instagram post URLs are normalized to the canonical shortcode prefix; bare long tokens are rejected before any network request. Gram resolves the shortcode to the numeric media PK locally and sends that PK as variables: { "id": "<media_pk>" } with doc_id 24452425501069647.
Output includes only safe liker identity fields: username, display/full name, user ID, and private or verified markers. It does not print the post URL, shortcode, media PK, raw cookies, tokens, profile image URLs, raw response JSON, email, phone number, or private-web payloads.
-n/--limit limits displayed likers from the current doc_id response unless --all is used. --all performs the same one-shot doc_id request and prints every parsed user returned by that response. --after still fails with a sanitized unsupported-pagination error because no verified cursor field exists for this doc_id. The older query-hash edge_liked_by transport and REST follow_ranking_token pagination are not used.
gram timeline
Lists safe fields from the logged-in home timeline. By default it hides raw media IDs and media CDN URLs. If another page is available, Gram prints a follow-up gram timeline --after ... command.
Useful options:
-n, --limit <count>from 1 to 100, default 20--after <cursor>--doc-id <id>--full-captions--media-links--verbose- Common cookie options such as
--browser,--profile,--cookie-header, and--user-agent
gram post
Shows safe fields for one Instagram post or reel. It accepts a raw shortcode or an Instagram URL with /p/, /reel/, /reels/, or /tv/; query strings and hashes are ignored. By default it hides raw media IDs and media CDN URLs.
Useful options:
--doc-id <id>--full-caption--media-links--verbose- Common cookie options such as
--browser,--profile,--cookie-header, and--user-agent
gram comments
Lists safe fields from an Instagram media comments page using read-only comments requests. The target can be a numeric media ID, a bare shortcode, or an Instagram /p/, /reel/, or /tv/ URL. Shortcodes and post URLs resolve locally before fetching comments. If another page is available, Gram prints a follow-up gram comments <media-id> --min-id ... or --max-id ... command. With --all, Gram keeps following headload and tail cursors until comments are exhausted and ignores --limit as the stop condition.
Useful options:
-n, --limit <count>from 1 to 100, default 20--all--page-size <count>from 1 to 100, default 12, or 50 with--all--min-id <cursor>--max-id <cursor>- Common cookie options such as
--browser,--profile,--cookie-header, and--user-agent
gram replies
Lists safe fields from a media comment's replies using read-only child comments requests. The target can be a numeric media ID, a bare shortcode, or an Instagram /p/, /reel/, or /tv/ URL. The second argument is the parent comment ID. Shortcodes and post URLs resolve locally before fetching replies. If another page is available, Gram prints a follow-up gram replies <media-id> <comment-id> --min-id ... command. With --all, Gram keeps following head child cursors until replies are exhausted and ignores --limit as the stop condition.
Useful options:
-n, --limit <count>from 1 to 100, default 20--all--page-size <count>from 1 to 100, default 12, or 50 with--all--min-id <cursor>- Common cookie options such as
--browser,--profile,--cookie-header, and--user-agent
gram note
Shows the current Instagram Note. This command is read-only and uses ds_user_id from cookies as the target user id. Mutation options such as --dry-run, --actor-id, --doc-id, --lsd, --fb-dtsg, --jazoest, and --resolve-web-tokens are for gram note set only.
Useful options:
--browser <chrome|arc|brave|safari|firefox>--profile <name>--cookie-header <header>--user-agent <ua>
Output includes only the note text and created timestamp/date when present. If no Note is present, Gram prints No current Instagram Note found. It never prints raw cookies, tokens, raw JSON, profile image URLs, or unrelated payload fields.
gram note set
Sets the active Instagram Note. This command sends a live mutation by default when valid credentials and tokens are available. Use --dry-run to preview without sending.
Useful options:
--dry-run--browser <chrome|arc|brave|safari|firefox>--profile <name>--cookie-header <header>--actor-id <id>--doc-id <id>--lsd <token>--fb-dtsg <token>--jazoest <token>--resolve-web-tokens--user-agent <ua>
Dry runs do not fetch Instagram home HTML or resolve web tokens. Live sends can discover missing web tokens before the mutation.
gram profile bio set
Updates the logged-in profile biography. This is a mutating command and requires --yes plus cookies containing sessionid and csrftoken.
Useful options:
--yes- Common cookie options such as
--browser,--profile,--cookie-header, and--user-agent
gram blocked list
Lists blocked accounts for the logged-in user. This command is read-only, but it still makes authenticated private-web requests and requires cookies containing sessionid and csrftoken.
Useful options:
- Common cookie options such as
--browser,--profile,--cookie-header, and--user-agent
gram blocked block and gram blocked unblock
Blocks or unblocks an Instagram user ID or handle. These are mutating commands and require --yes plus cookies containing sessionid and csrftoken.
Useful options:
--yes- Common cookie options such as
--browser,--profile,--cookie-header, and--user-agent
Cookies and config
Gram can get cookies from these sources:
- Browser cookies with
--browser <chrome|arc|brave|safari|firefox>. - Browser profiles with
--profile <name>where supported. - Manual cookies with
--cookie-header <header>. - Manual cookies from
INSTAGRAM_COOKIE_HEADER. - Saved config from
gram setup.
Saved config lives at $XDG_CONFIG_HOME/gram/config.json. If XDG_CONFIG_HOME is not set, Gram uses ~/.config/gram/config.json.
The config stores cookie-source metadata, not raw cookies:
{
"cookies": {
"browser": "chrome",
"profile": "Default"
}
}Cookie source precedence is:
--cookie-headerINSTAGRAM_COOKIE_HEADER- Explicit browser flags such as
--browserand--profile - Saved user config
- An error asking you to pass cookies, choose a browser source, or run
gram setup
Development and validation
Useful local checks:
pnpm run format:check
pnpm run typecheck
pnpm test
pnpm run build
node dist/index.js --help
node dist/index.js help whoami
node dist/index.js help likers
node dist/index.js note set "hello" --cookie-header "csrftoken=fake-csrf; ds_user_id=12345; sessionid=fake-session" --lsd fake-lsd --dry-runDo not validate gram note set without --dry-run unless you intend to update the live Instagram Note. Do not live-validate profile bio set, blocked block, or blocked unblock unless you intend to mutate the logged-in account. Avoid casual live validation of blocked list, likers, comments, or replies; they are read-only, but they still use authenticated private-web requests and produce account data.
Technical docs
Implementation details, request flows, parser notes, and lower-level protocol behavior belong in docs/README.md.
