Skip to main content
Glama

InstantGram

A TypeScript Node.js Instagram DM API server with webhook capabilities, an interactive + JSON CLI, an OpenAPI 3.1 spec, and an MCP server for AI agents.

⚠️ Disclaimer: This project uses the unofficial instagram-private-api library, which is not affiliated with or endorsed by Instagram/Meta. Automating Instagram accounts may violate their Terms of Service and can lead to temporary or permanent account restrictions. Use at your own risk — use only with accounts you own, and rate-limit yourself sensibly.


Features

  • Multi-session management — create, login, restore, delete, and export sessions in memory, persisted to disk

  • Login with 2FA — detects two-factor requirements and completes them via a verification code; handles checkpoints (auto-solve attempt + manual challenge URL fallback)

  • Custom User-Agent & proxy support (http/https/socks5)

  • Messaging API — send text and media (image/video) DMs to a username or numeric user id

  • User lookup — resolve usernames to user ids

  • Webhook system — poll the inbox for new DMs and POST HMAC-SHA256-signed events to your URL, with retries and exponential backoff on rate limits

  • Events: message.received, message.sent, session.error, webhook.test

  • AI-friendly surfaces/openapi.json (full OpenAPI 3.1 spec), /llms.txt + /llms-full.txt docs, and a one-shot --json CLI mode

  • MCP server — Model Context Protocol tools over stdio (npm run mcp) or Streamable HTTP (/mcp), so AI assistants can create sessions, send DMs, and manage webhooks

  • Interactive CLI — create/login sessions, run the full send-message wizard, configure webhooks, watch live webhook events, export/import sessions

  • Session persistence — serialized states (cookies + device) saved to ./sessions/*.session.json and auto-loaded on boot


Related MCP server: instagram-personal-mcp

Installation

# Node.js >= 18 required
npm install

# Configure environment
cp .env.example .env

# Start the API server (development, with hot reload)
npm run dev

# Or production:
npm run build && npm start

# Interactive CLI (in a second terminal):
npm run cli

Environment variables (.env)

Variable

Default

Description

PORT

3000

HTTP port for the API server

WEBHOOK_SECRET

Secret used to sign webhook payloads (X-Webhook-Signature header)

DEFAULT_POLL_INTERVAL

5000

Default inbox poll interval in ms

LOG_LEVEL

info

debug | info | warn | error

SESSION_DIR

./sessions

Directory for persisted session states

HTTP_PROXY / per-session proxy

Proxy for Instagram API traffic

API_BASE_URL

http://localhost:3000

Base URL the CLI, the JSON mode, and the stdio MCP server use to reach the API server

AUTH_TOKEN

Bearer auth — if set, every request except OPTIONS preflights and GET /health must send Authorization: Bearer <AUTH_TOKEN>. Opt-in kill-switch for locking down a public instance; CLI/JSON/stdio-MCP attach it automatically from the same .env

INSTAGRAM_USERNAME / INSTAGRAM_PASSWORD

Fallback credentials for npm run cli -- login (keeps secrets out of shell history)

IG_APP_VERSION / IG_APP_VERSION_CODE

347.0.0.25.106 / 441775334

Instagram app version fingerprint sent in the User-Agent. The library bundles an ancient version (222.x) that Instagram rejects with unsupported_version/checkpoint_required — bump these if sessions start getting flagged


API Reference

All responses use a consistent envelope:

{
  "success": true,
  "data": { },
  "meta": { "timestamp": "2026-08-09T12:00:00.000Z", "sessionId": "…" }
}

Errors return { "success": false, "error": "…", "meta": { … } }.

Bearer auth (AUTH_TOKEN)

If AUTH_TOKEN is set in .env, the server rejects every request (except CORS OPTIONS preflights and the GET /health liveness probe) unless it carries the header:

Authorization: Bearer <AUTH_TOKEN>

This is an opt-in kill-switch: set the variable to instantly lock down a publicly exposed instance with no code changes, unset it to return to open access. The interactive CLI, one-shot JSON mode, and stdio MCP bridge read AUTH_TOKEN from the same .env and send it automatically, so local workflows keep working. Unauthorized requests get 401 with the standard error envelope.

curl -s -X POST localhost:3000/api/session/create \
  -H 'Authorization: Bearer <AUTH_TOKEN>' \
  -H 'Content-Type: application/json' -d '{}'

AI-friendly surfaces

Method

Route

Description

GET

/

JSON service index — points agents at the endpoints below

GET

/api

Route-group index

GET

/health

Liveness probe ({ status, uptime, sessions })

GET

/openapi.json

OpenAPI 3.1 document of every endpoint, schema, and error — the single machine-readable API contract

GET

/llms.txt

LLM-friendly documentation index (llms.txt convention)

GET

/llms-full.txt

Full markdown reference for LLM ingestion

GET / POST / DELETE

/mcp

MCP server over Streamable HTTP (see below)

Sessions

Method

Route

Body

Description

POST

/api/session/create

{ userAgent?, cookies?, proxy? }

Create a session; returns { sessionId }

POST

/api/session/login

{ sessionId, username, password }

Log in. If 2FA is required returns data.twoFactor; if a checkpoint is hit returns data.challenge

POST

/api/session/2fa

{ sessionId, code, verificationMethod? }

Complete a pending 2FA login. For approval-based 2FA (verification method 2 — a push to your phone, no code) send code: "" after approving on the device. Pass verificationMethod when Instagram didn't report it

POST

/api/session/logout

{ sessionId }

Log out

POST

/api/session/restore

{ data: SerializedSessionFile, preferredId? }

Restore a session from exported JSON

POST

/api/session/restore-cookies

{ cookies, userAgent?, proxy? }

Create a session from raw browser cookies (see below)

GET

/api/session

List all sessions with status

GET

/api/session/:id

Single session details

GET

/api/session/:id/export

Export serialized session (cookies + device)

DELETE

/api/session/:id

Delete a session (stops polling too)

Messages

Method

Route

Body / Query

Description

POST

/api/message/send

{ sessionId, recipient, text?, mediaBase64?, mediaType? }

Send a DM. recipient may be a username or numeric user id. mediaBase64 is base64-encoded file data; mediaType is image | video (auto-detected from bytes if omitted)

GET

/api/user/lookup/:username

?sessionId=…

Resolve a username to a numeric user id

Webhooks

Method

Route

Body

Description

POST

/api/webhook/configure

{ sessionId, url, secret?, events? }

Set the delivery URL (and optional per-session signing secret)

POST

/api/webhook/start

{ sessionId, intervalMs? }

Start inbox polling (min 1000 ms)

POST

/api/webhook/stop

{ sessionId }

Stop polling

POST

/api/webhook/test

{ sessionId }

Send a synthetic webhook.test event through the full pipeline

GET

/api/webhook/status/:sessionId

Polling state, URL, interval, last delivery

GET

/api/webhook/logs/:sessionId

?limit=50

Recent delivery log entries


Webhook payloads

Every event is POSTed to your URL as JSON:

{
  "event": "message.received",
  "timestamp": 1786298000000,
  "sessionId": "8f0e9a2b-…",
  "payload": {
    "messageId": "340282366841710300949128155123748551874",
    "threadId": "340282366841710300949128155123748551874",
    "userId": "123456789",
    "username": "someone",
    "text": "Hey! 👋",
    "itemType": "text"
  }
}

Field

Type

Description

event

string

message.received | message.sent | session.error | webhook.test

timestamp

number

Unix epoch ms

sessionId

string

The session that produced the event

payload.messageId

string

Instagram item id

payload.threadId

string

Direct thread id

payload.userId

string

The other party's user id

payload.username

string

Their username (when known)

payload.text

string

Message text (text items)

payload.mediaUrl

string

CDN URL for media items (photo/video/voice/clip)

payload.itemType

string

Raw Instagram item type

payload.error

string

Error detail (session.error events)

Signature verification

Each request carries an X-Webhook-Signature header:

X-Webhook-Signature: sha256=<hex-encoded HMAC-SHA256 of the raw request body>
X-Webhook-Event: message.received

The signature is computed with the WEBHOOK_SECRET (or the per-session secret configured via /api/webhook/configure).

Node.js receiver example:

const crypto = require('crypto');

app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const secret = process.env.WEBHOOK_SECRET;
  const expected = `sha256=${crypto
    .createHmac('sha256', secret)
    .update(req.body)
    .digest('hex')}`;

  if (!crypto.timingSafeEqual(
    Buffer.from(req.get('x-webhook-signature')),
    Buffer.from(expected),
  )) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  const event = JSON.parse(req.body.toString());
  console.log(`${event.event} from session ${event.sessionId}`, event.payload);
  res.sendStatus(200);
});

Use express.raw() (or read the raw body) so the signature covers the exact bytes that were POSTed — re-serializing parsed JSON will break verification.

Python receiver example:

import hashlib, hmac, json
from flask import Flask, request

app = Flask(__name__)
SECRET = b"your-webhook-secret-here"

@app.post("/webhook")
def webhook():
    body = request.get_data()
    expected = "sha256=" + hmac.new(SECRET, body, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(request.headers.get("X-Webhook-Signature", ""), expected):
        return {"error": "invalid signature"}, 401
    print(json.loads(body))
    return "ok", 200

Restoring a session from raw browser cookies

No username/password required — paste the cookies you can copy from your browser's DevTools (Application → Cookies → i.instagram.com). Only sessionid is mandatory; csrftoken is strongly recommended (state-changing requests like sending messages need it). The user id is derived from the first segment of the sessionid value when ds_user_id isn't provided.

curl -s -X POST localhost:3000/api/session/restore-cookies -H 'Content-Type: application/json' -d '{
  "cookies": "sessionid=38499037450%3ABX1JtIMMfeCui8%3A10%3AAYhKmCtPNU97naqRsPuZpCUA49KbUT4SsWi2KDPUgg; csrftoken=abc123; ds_user_id=38499037450; mid=xyz",
  "userAgent": "Instagram 276.0.0.24.105 Android (30/11; 420dpi; 1080x2340; samsung; SM-G991B; o1s; exynos2100; en_US; 319462287)"
}'
# → { "success": true, "data": { "sessionId": "…", "userId": "38499037450", "status": "logged_in" }, … }

A cookie-header string and a structured object are both accepted:

// structured form
{ "cookies": { "sessionid": "…", "csrftoken": "…", "ds_user_id": "…" } }

The server verifies the cookies in the background (account.currentUser()); if Instagram rejects them (expired/invalid/flagged), the session status flips to error with an explanatory message — check GET /api/session. Cookies are bound to the device fingerprint, so passing the matching userAgent you copied from the browser helps keep the session alive.

CLI usage

npm run cli

The CLI is a self-contained tester that talks to the running API server (start it with npm run dev). Menu map:

Main menu
├── Session Management
│   ├── Create new session        (custom UA / cookies / proxy)
│   ├── Login to session          (auto-detects 2FA and prompts for the code)
│   ├── Complete 2FA login
│   ├── Restore session from JSON file
│   ├── Restore session from raw cookies
│   ├── List active sessions
│   ├── Logout from session
│   └── Delete session
├── Send Message                  (select session → recipient → text/media → confirm)
├── Webhook Management
│   ├── Configure webhook URL     (URL + optional secret + event filter)
│   ├── Start polling             (custom interval)
│   ├── Stop polling
│   ├── Send test webhook
│   ├── View webhook status
│   ├── View webhook logs
│   └── Watch live webhook events (press any key to return)
├── Utilities
│   ├── Lookup user ID from username
│   ├── Export session to file
│   └── Import session from file
└── Exit

One-shot JSON mode (AI / scripting friendly)

npm run cli -- <command> [--flag value ...] runs a single command and prints exactly one JSON document to stdout — nothing else — so output pipes straight into jq, shells, and AI tooling:

npm run cli -- list-sessions
# → {"success":true,"data":[{"id":"…","status":"logged_in",…}]}

npm run cli -- send-message --session-id <id> --recipient friend --text "hi"
# → {"success":true,"data":{"messageId":"…","threadId":"…","recipientUserId":"…","itemType":"text"}}

Errors print { "success": false, "error": "…" } and exit with code 1. Credentials for login can come from INSTAGRAM_USERNAME / INSTAGRAM_PASSWORD to keep secrets out of shell history.

Commands: health, create-session, login, complete-2fa, logout, restore-session (--file), restore-cookies, list-sessions, session-info, export-session, delete-session, send-message, lookup-user, webhook-configure, webhook-start, webhook-stop, webhook-test, webhook-status, webhook-logs, help. Run npm run cli -- help for per-command flags.


MCP server (Model Context Protocol)

InstantGram speaks MCP so AI assistants can drive it directly — create/login sessions, send DMs, look up users, manage webhooks. Every REST endpoint is exposed as a tool, and the docs are available as resources.

Tools: create_session, login, complete_2fa, logout, restore_session, restore_cookies, list_sessions, session_info, export_session, delete_session, send_message, lookup_user, webhook_configure, webhook_start, webhook_stop, webhook_test, webhook_status, webhook_logs, health

Resources: instantgram://openapi.json, instantgram://llms-full.txt

stdio (Claude Desktop, Cursor, …)

The MCP server bridges to the running API server via API_BASE_URL — start the API with npm run dev first, then:

npm run mcp

Register it in claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "instantgram": {
      "command": "npm",
      "args": ["run", "mcp"],
      "cwd": "/absolute/path/to/instantgram"
    }
  }
}

Prefer a compiled entry? npm run build then point command at node dist/mcp.js.

HTTP (Streamable HTTP)

The API server hosts MCP at /mcp (CORS-enabled; stateful sessions use the Mcp-Session-Id header):

GET    http://localhost:3000/mcp   # initialize / SSE handshake
POST   http://localhost:3000/mcp   # JSON-RPC messages
DELETE http://localhost:3000/mcp   # end the session

Point any MCP client (MCP Inspector, web apps, remote assistants) at http://localhost:3000/mcp.


Quick start (end-to-end)

# Terminal 1 — run the server
cp .env.example .env
npm install
npm run dev

# Terminal 2 — run the CLI
npm run cli

# From the CLI:
# 1. Session Management → Create new session → Login to session
# 2. (if 2FA) → enter code
# 3. Webhook Management → Configure webhook URL → Start polling
# 4. Send Message → pick session → recipient → text → confirm
# 5. Webhook Management → Watch live webhook events

Alternatively, drive everything with curl:

# Create a session
curl -s -X POST localhost:3000/api/session/create -H 'Content-Type: application/json' \
  -d '{"userAgent":"Instagram 347.0.0.25.106 Android (30/11; 420dpi; 1080x2340; samsung; SM-G991B; o1s; exynos2100; en_US; 441775334)"}'
# → {"success":true,"data":{"sessionId":"<id>"},...}

# Login
curl -s -X POST localhost:3000/api/session/login -H 'Content-Type: application/json' \
  -d '{"sessionId":"<id>","username":"myaccount","password":"hunter2"}'

# Send a DM to a username
curl -s -X POST localhost:3000/api/message/send -H 'Content-Type: application/json' \
  -d '{"sessionId":"<id>","recipient":"some.friend","text":"Hello from InstantGram!"}'

# Look up a user id
curl -s "localhost:3000/api/user/lookup/some.friend?sessionId=<id>"

# Configure a webhook and start polling
curl -s -X POST localhost:3000/api/webhook/configure -H 'Content-Type: application/json' \
  -d '{"sessionId":"<id>","url":"https://your-server.example.com/webhook"}'
curl -s -X POST localhost:3000/api/webhook/start -H 'Content-Type: application/json' \
  -d '{"sessionId":"<id>","intervalMs":5000}'

# Send media (base64 of a jpg) — use mediaType image/video
IMG_B64=$(base64 -i photo.jpg | tr -d '\n')
curl -s -X POST localhost:3000/api/message/send -H 'Content-Type: application/json' \
  -d "{\"sessionId\":\"<id>\",\"recipient\":\"some.friend\",\"mediaBase64\":\"$IMG_B64\",\"mediaType\":\"image\"}"

2FA methods

Instagram's two_factor_info.verification_method tells you which challenge was triggered:

Method

Type

What to do

0

SMS

Enter the code texted to your phone

1

Authenticator app (TOTP)

Enter the 6-digit code from your app

2

Approval from another device

A push notification appears on your logged-in phone — tap "It was me", then complete with an empty code (code: "")

3

Backup codes

Enter one of your one-time backup codes

The CLI detects method 2 automatically: it tells you to approve on your phone and completes the login without asking for a code.

⏱️ Approval pushes are short-lived (usually one or two minutes). Approve on your phone and confirm in the CLI promptly — if it expires, just run Login to session again to trigger a fresh push. Tapping a stale notification from an earlier login attempt always shows "expired"; use the one from the current attempt. Pending challenges older than 10 minutes are auto-cleared.

# Approval-based 2FA via the API (verificationMethod: 2 = approval push)
curl -s -X POST localhost:3000/api/session/2fa -H 'Content-Type: application/json' \
  -d '{"sessionId":"<id>","code":"","verificationMethod":"2"}'

Session persistence

After a successful login (or 2FA completion), the session's serialized state — cookies, device fingerprint, and constants — is written to SESSION_DIR (default ./sessions) as <sessionId>.session.json.

On server boot, every file in that directory is loaded back into memory, so sessions stay logged in across restarts without re-entering credentials.

  • Export: GET /api/session/:id/export or CLI Utilities → Export session to file. The exported JSON can be moved to another machine and restored with POST /api/session/restore or CLI Import session from file.

  • Rotation: If Instagram invalidates a session (e.g. password change), delete the session and its file, then log in again.

🔒 Treat sessions/ like passwords — it contains live auth cookies. It is git-ignored by default.


How it works

  1. Instagram has no public DM webhooks — this server polls ig.feed.directInbox() on a configurable interval and diffs new item ids against a per-session seen-set.

  2. New incoming items become message.received events; outbound API sends become message.sent events; poll failures become session.error events.

  3. Each event is signed with HMAC-SHA256 and delivered with up to 3 retries (1s/2s/4s backoff). On rate-limit or network errors the poll interval backs off exponentially up to 5 minutes.

  4. Sessions are isolated in memory (Map<sessionId, InstagramService>), each with its own device fingerprint, cookies, proxy, and webhook config.

Error classification

Instagram errors are normalized into categories that map to HTTP statuses and retry behavior:

Category

Trigger

API response

network

Connection/parse failures

502

auth

Bad password, invalid user, logged out

401

rate_limit

Action spam / too many requests

429 (+ poll backoff)

not_found

searchExact miss

404

challenge

Checkpoint / unsupported version

403 (with solve URL)

two_factor

Login-time flows

handled inline

Why you might see checkpoint_required / unsupported_version: the bundled instagram-private-api reports an app version from ~2021 (222.0.0.13.114). Instagram flags that fingerprint. This project patches the version to 347.0.0.25.106 by default (overridable via IG_APP_VERSION/IG_APP_VERSION_CODE, or derived from your custom User-Agent). If Instagram tightens checks again, bump those env values.


Project structure

src/
  ├── index.ts                 # Express server entry (wires services + routes)
  ├── cli.ts                   # Inquirer.js interactive CLI
  ├── cli-json.ts              # One-shot JSON mode (AI / scripting friendly)
  ├── mcp.ts                   # MCP server entry (stdio bridge to the REST API)
  ├── openapi.ts               # OpenAPI 3.1 document (served at /openapi.json)
  ├── types/index.ts           # All shared interfaces
  ├── services/
  │   ├── InstagramService.ts  # IgApiClient wrapper (auth, messaging, polling)
  │   ├── SessionManager.ts    # Multi-session registry + disk persistence
  │   └── WebhookService.ts    # Polling, HMAC delivery, retries, logs
  ├── routes/
  │   ├── session.ts           # /api/session/*
  │   ├── message.ts           # /api/message/*, /api/user/lookup/*
  │   └── webhook.ts           # /api/webhook/*
  ├── mcp/
  │   ├── apiClient.ts         # Envelope-aware REST client (shared by MCP + JSON CLI)
  │   ├── tools.ts             # MCP tool definitions (one per REST endpoint)
  │   └── http.ts              # Streamable HTTP transport mounted at /mcp
  └── utils/
      ├── api.ts               # Response envelope helpers
      ├── crypto.ts            # HMAC-SHA256 sign/verify
      └── logger.ts            # Leveled, colored logging

Scripts

Script

Purpose

npm run dev

Run the API server with tsx watch (hot reload)

npm run build

Compile TypeScript to dist/

npm start

Run the compiled server

npm run cli

Launch the interactive CLI

npm run cli -- <cmd>

One-shot JSON mode (AI / scripting friendly)

npm run mcp

Start the MCP server over stdio (requires the API server)

npm run typecheck

Type-check without emitting

License

MIT

A
license - permissive license
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    -
    quality
    F
    maintenance
    An MCP server that integrates with Instagram's Graph API to enable AI-driven management of Instagram Business accounts. It provides tools for fetching profile data, publishing media, analyzing engagement metrics, and managing direct messages.
    165
    MIT
  • A
    license
    -
    quality
    D
    maintenance
    An MCP server that wraps instagrapi to read, engage, and send DMs from a personal Instagram account, supporting 24 tools for auth, profile, engagement, and messages.
    MIT

View all related MCP servers

Related MCP Connectors

  • Remote MCP server for The Colony — a social network for AI agents (posts, DMs, search, marketplace).

  • MCP server for AI agents to plan, verify, and deploy Cloudflare-native apps.

  • Managed LinkedIn MCP server for AI agents: search, connect, message and enrich on accounts you own.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Houloude9IOfficial/InstantGram'

If you have feedback or need assistance with the MCP directory API, please join our Discord server