@alaminnna/ica-unofficial
v2.9.0
Published
Unofficial Instagram Chat API for Node.js ? fca-style. Login, send messages, listen to events.
Downloads
1,115
Maintainers
Readme
Interact with Instagram DM programmatically. Built for bot developers.
⚡ Realtime MQTT listener + FBNS push — pure JavaScript, CommonJS, lightweight deps.
Installation • Basic Usage • API Reference • npm • Issues
I maintain this to keep Instagram bot building simple in Node.js. Small win by small win — faster reply paths, cleaner logs, and full DM surface coverage. — Al A Min
ICA (@alaminnna/ica-unofficial) is an unofficial Instagram Chat API for Node.js with fca-style ergonomics. It drives a user account: login once, send messages, listen for new ones — over HTTPS + realtime MQTT.
Keywords: instagram chat api nodejs, insta fca, unofficial instagram api, mqtt chatbot, instagram dm bot, alaminnna
- What's New
- Installation
- Basic Usage
- Features
- Realtime & FBNS
- API Reference
- Logging
- Author — Al A Min (Alaminnna)
🌟 What's New
- 💬 Comments — post, like/unlike, pin/unpin, bulk delete, offensive check, paged list
- 🧵 Direct room management — create/hide/retitle/add-users, unread, mute, presence, message search, spam inbox
- 💾 Collections + Highlights — full saved/highlight CRUD
- 🔍 Discovery — hashtag module, location, music detail, SERP surfaces, private mobile-GQL lists
- 📖 Stories — info, likers, like, poll vote, sticker tray, archive, highlights
- 🔔 Account — notification center (28 toggles), news inbox, AI label, USDID attestation, legacy signup
- 👥 Users 100% surface — follow requests, close friends, mutes, friendship, about, chaining, stream + GQL lookups
- 📊 Speed proof — MQTT typing RTT next to HTTP reply times
📦 Installation
npm install @alaminnna/ica-unofficialOr with yarn:
yarn add @alaminnna/ica-unofficialRequires Node.js >= 14. Plain CommonJS, no TypeScript build step. Ships index.js + src/ + LICENSE. Optional native helpers (better-sqlite3 for the listen() snapshot store, sharp for photo resize) install when supported and degrade gracefully when absent. ESM works too: import { login, ChallengeRequired } from '@alaminnna/ica-unofficial'.
⚠️ Important Disclaimer
We are not responsible if your account gets restricted for spammy activities such as:
- Sending lots of messages to people you don't know
- Sending messages very quickly
- Logging in and out very quickly
- Running aggressive follow/unfollow/report automation
Recommendation: use a test account, go slow, respect rate limits (writes are rate-guarded by default), and never commit real credentials or session files.
🔍 Introduction
Instagram's official API surface is built for businesses and creators — automating a user account's DMs needs the same private endpoints the app itself uses.
@alaminnna/ica-unofficial speaks those endpoints for you:
- Making the exact same GET/POST requests as the app
- Login with username/password (2FA + challenge aware) or a saved session
- Realtime delivery over MQTT with polling fallback — no scraping
🚀 Basic Usage
1. Login and save the session
const { login } = require('@alaminnna/ica-unofficial');
const api = await login({ username: 'you', password: 'secret' });
api.saveSession('./session.json');2. Resume from a saved session (no password needed)
const { login } = require('@alaminnna/ica-unofficial');
const api = await login({ session: require('./session.json') });3. Send a message (promise or fca callback style)
const info = await api.sendMessage({ text: 'hello', threadId: '123' });
api.sendMessage('hello', '123', (err, info) => {});4. Listen + reply, bot-style
const stop = await api.listen(async (event) => {
if (event.type === 'message' && event.body === '.ping') {
await api.sendMessage({ text: 'pong!', threadId: event.threadID });
}
});
// stop() ends listening cleanly5. Send a photo
await api.sendPhoto({ path: './pic.jpg', threadId: '123' });
await api.sendPhoto({ url: 'https://example.com/pic.jpg', threadId: '123' });Tip: threadId also accepts full pk_userid ids, and most send methods accept userIds to auto-resolve the thread.
🔄 Auto-update check
Every login() first checks the npm registry for a newer release (cached for 6h, 8s timeout). When outdated, it runs npm install @alaminnna/ica-unofficial@latest in your project, then continues logging in — restart the process to run the new version. Registry/install failures never block login; they only log.
await login({ username: 'you', password: 'secret' }); // checks + auto-updates
await login({ username: 'you', password: 'secret', autoUpdate: false }); // check only, no install
await login({ session, skipUpdateCheck: true }); // no check at allEnv overrides: ICA_SKIP_UPDATE_CHECK=1 (no check), ICA_AUTO_UPDATE=0 (check, no install). Manual check: const { checkForUpdate } = require('@alaminnna/ica-unofficial').
✨ Features
- Login two ways: credentials (2FA + challenge aware) or saved session
- Send text/link, photo, video, voice notes, files, cutout stickers; replies
- Threads, inbox, spam box, pending requests (list/count/approve)
- Reactions, heart likes, unsend/delete, mark-seen, typing indicator
- Realtime MQTT listener + FBNS push registration
- Comments, collections, highlights, stories, notes (text + music)
- Users 100% surface — follow requests, close friends, mutes, friendship, discovery, GQL lookups
- Upload: photo/video/reel/album/IGTV (+music, schedule, crosspost)
- Media manage: edit/archive/pin/seen/oembed/livestream
- Discovery: topsearch, places, hashtags, music, insights, private-GQL lists
- Account: profile, security, confirm flows, notifications, USDID, legacy signup
- Proxy support (http/https/socks), anti-ban delays, fastMode
⚡ Realtime Listener
Two modes, one event surface. listenMode option: mqtt (default path
via auto), polling, or auto (try MQTT, degrade on failure).
| | MQTT | Polling fallback | |---|---|---| | Detection | instant (<1s) | ~3s snapshot diff | | Typing/presence/seen | live | not available | | Transport | TLS socket + Thrift | plain HTTPS inbox |
const stop = await api.listen((event) => {
console.log(event.type, event.body);
}, { listenMode: 'auto' });auto falls back after 3 drops in 2 min (warn logged) or immediately
when polling is forced. Auth failures halt with session_expired
(polling cannot help a dead session). Reconnects rebuild auth params
from the live session automatically, resume from the last seq_id,
and dedupe by item_id (LRU-capped at 500 — no double-fire on replay).
Events: message, typing (alias typ), presence, seen (alias
read_receipt), event, session_expired.
📡 Realtime & FBNS (Advanced)
Experimental: this rides Instagram's private MQTT transport, which Instagram can change at any time. HTTP Direct methods stay the source of truth for media and complex ops — MQTT actions cover text/reaction/seen/activity only.
Stateful client (api.realtimeConnect() returns a RealtimeClient,
EventEmitter style):
| Method | What it does |
|---|---|
| realtimeConnect({transport?}) / realtimeDisconnect() | connect / teardown (singleton per client) |
| realtimeOn(event, handler) / realtimePing() / realtimeReadOnce() | subscribe / PINGRESP await / read + dispatch one packet |
| directSendText(threadId, text) | send_item text over MQTT (no HTTP round-trip) |
| directSendReaction(threadId, itemId, emoji) | send_item reaction over MQTT |
| directMarkSeen(threadId, itemId) | mark_seen over MQTT |
| directIndicateActivity(threadId, isActive) | typing indicator over MQTT |
| directSubscribe(amount?) / irisSubscribe(seqId, snapshotAtMs) | inbox sync + seq resume (no gaps on reconnect) |
| graphQlSubscribe(list) / skywalkerSubscribe(list) | raw topic subscribe (advanced) |
| sendForegroundState(state) / publishJson(topic, data) | raw publish helpers |
| fbnsConnect() / fbnsDisconnect() / fbnsOn() / fbnsPing() | FBNS push channel (mqtt-mini.facebook.com) |
Not connected → realtime actions throw RealtimeNotConnected
(no silent fallback). send_response events carry the matching
client_context.
Events (raw kept in event.raw): receive (every decoded publish),
message (message-sync), direct (parsed Direct payloads),
typing / seen / presence (subsets of direct),
send_response, iris_sub_response, realtime_sub. FBNS:
push (parsed fbpushnotif), collapse_key names (e.g.
direct_v2_message), reg_response, registered, logging, pp.
fbns_auth persists in the session file (survives restarts, reused on
later runs) and is never logged — use maskFbnsAuth() before any
FBNS state reaches logs.
📚 API Reference
| Method | Signature | Description |
|---|---|---|
| login | login(options, cb?) | credentials or {session\|cookies}; callback + promise |
| getThreads | (options?) | inbox threads (amount/filter/box) |
| getThread | (threadId, options?) | thread + message history |
| getMessages | (threadId, options?) | messages only |
| sendMessage | ({text, threadIds\|userIds, replyTo?}, cb?) | text/link (+reply) |
| sendReply | (threadId, text, replyTo, cb?) | reply shortcut |
| sendPhoto | ({path\|url, threadId\|userIds}, cb?) | JPEG photo |
| sendVideo | ({path\|url, threadId\|userIds}, cb?) | H.264 MP4 |
| sendVoice | ({path, threadId\|userIds}, cb?) | AAC m4a voice note |
| sendFile | ({path\|url, threadId}, cb?) | photo/video by extension |
| sendReaction / deleteReaction | (threadId, messageId, emoji?, cb?) | reaction set/remove |
| likeDirectMessage / unlikeDirectMessage | (threadId, messageId, cb?) | heart like toggle |
| setMessageReaction | ({emoji, messageId, threadId}, cb?) | react (use deleteReaction to remove) |
| unsendMessage | (threadId, messageId, cb?) | unsend (= delete) |
| deleteMessage | (threadId, messageId, cb?) | delete |
| markThreadSeen | (threadId, messageId, cb?) | mark seen (MQTT fast path when connected) |
| setTyping | (threadId, bool, cb?) | typing indicator (MQTT fast path when connected) |
| sendTyping | (threadId) | fire-and-forget typing via MQTT |
| listen / listenMqtt | (onEvent, options?) | realtime events, returns stop() (keeps a local snapshot store in ./ica-alaminnna/ unless store: false) |
| realtimeConnect / realtimeDisconnect | (options?) | stateful MQTT client |
| realtimeOn / realtimePing / realtimeReadOnce | (…) | subscribe / ping / single read |
| directSendText / directSendReaction | (…, cb?) | MQTT direct actions |
| directMarkSeen / directIndicateActivity | (…, cb?) | MQTT seen / typing |
| directSubscribe / irisSubscribe | (…) | inbox sync + seq resume |
| fbnsConnect / fbnsDisconnect / fbnsOn / fbnsPing | (…) | FBNS push channel |
| createThread | ({userIds, title?}, cb?) | group thread create |
| hideThread | (threadId, moveToSpam?, cb?) | hide (spam optional) |
| updateThreadTitle / addUsersToThread | (threadId, ...) | retitle / add users |
| removeMember | (threadId, users, cb?) | kick (admin-gated, clear error otherwise) |
| leaveGroup | (threadId, cb?) | self-leave (live-confirmed) |
| resolveGroupInvite | (link\|code, cb?) | resolve invite landing (join unconfirmed) |
| setGroupTitle | (threadId, title, cb?) | admin-gated retitle |
| getThreadAdmins | (threadId, cb?) | admins with refresh (latest wins) |
| getGroupMembers | (threadId, cb?) | members with profile pics |
| inspectAccount | (username\|userId, cb?) | flags + labeled bot signals |
| markThreadUnread | (threadId, cb?) | mark unread |
| muteThread / unmuteThread | (threadId, cb?) | thread mute toggle |
| muteVideoCall / unmuteVideoCall | (threadId, cb?) | video-call mute toggle |
| setE2eeEligibility | (value?, cb?) | e2ee state (default 4) |
| getSpamInbox / getSpamInboxChunk | (amount?/cursor?, cb?) | spam inbox (paged) |
| searchMessages | (query, cb?) | message-text search |
| getUsersPresence / getActivePresence | (userIds?, cb?) | presence |
| searchGenAiBots / getChannels | (…) | AI bots / channels |
| searchThreads | (query, mode?, cb?) | ranked thread search |
| getThreadByParticipants | (userIds, cb?) | thread lookup |
| sendCutoutSticker | ({stickerId, threadId}, cb?) | cutout sticker share |
| shareMedia | ({mediaId\|mediaUrl, threadId}, cb?) | share a post/reel |
| shareStory | ({storyId, threadId}, cb?) | share a story |
| shareProfile | ({userId, threadId}, cb?) | share a profile |
| getUserInfo | (userId, cb?) | normalized profile |
| getUserInfoByUsername | (username, cb?) | normalized profile |
| getUserIdFromUsername | (username, cb?) | numeric id string |
| getUsernameFromUserId | (userId, cb?) | username from id (cached + GQL fallback) |
| getFollowers | ({userId, amount, order?}, cb?) | followers page (amount small!) |
| getFollowing | ({userId, amount}, cb?) | following page |
| getMutualFollowers | ({userId, amount}, cb?) | local intersection |
| getFollowersChunk / getFollowingChunk | (options?, cb?) | {users, nextCursor} pages |
| iterUserFollowers / iterUserFollowing | (options?) | async-generator streaming |
| getFollowersPrivateGql / getFollowingPrivateGql | (options?, cb?) | mobile-GQL follow lists |
| follow / unfollow | ({userId}, cb?) | rate-guarded writes |
| block / unblock | ({userId, surface?}, cb?) | surface: profile | direct_thread_info |
| getFriendship / getUserFriendship | (userId, cb?) | flags / following-vs-outgoing classifier |
| getUserFriendships | (userIds, cb?) | bulk show_many |
| getCreatorInfo | (userId, entryPoint?, cb?) | creator info + user |
| getFollowRequests / getFollowRequestsChunk | (options?, cb?) | private-account follow inbox (paged) |
| approveFollowRequest / declineFollowRequest | (userId, cb?) | single approve / decline |
| approveFollowRequests / declineFollowRequests | (userIds, cb?) | sequential batch → {id: bool} |
| closeFriendAdd / closeFriendRemove | (userId, cb?) | besties (wires to story audience) |
| mutePostsFromFollow / unmutePostsFromFollow | (userId, cb?) | post mute toggle |
| muteStoriesFromFollow / unmuteStoriesFromFollow | (userId, cb?) | story mute toggle |
| setPostsNotifications / setVideosNotifications | (userId, enabled, cb?) | post/video favorites |
| setReelsNotifications / setStoriesNotifications | (userId, enabled, cb?) | reels/story favorites |
| removeFollower / reportUser | (userId[, reason], cb?) | remove / FRX spam report |
| searchFollowers / searchFollowing | (userId, query, cb?) | in-list search |
| getUserAbout / getUserGuides | (userId, cb?) | about-this-account / guides |
| getChaining / fetchSuggestionDetails | (userId[, chainedIds], cb?) | suggestion surfaces |
| getSuggestedProfiles / getRelatedProfiles | (userId[, expand], cb?) | suggested (gated → chaining fallback) |
| getRecommendedAccountsForCategory | (userId, cb?) | business-category similars |
| linkAddressBook / unlinkAddressBook | (contacts[, include], cb?) | contact upload (explicit only) |
| getUserShortGql / getUserInfoV2Gql | (userId, cb?) | doc_id GraphQL lookups |
| getUserInfoByUsernameV2Gql | (username, cb?) | doc_id search + profile |
| getUserStreamById / getUserStreamByUsername | (id, cb?) | streamed envelopes |
| getUserStreamByIdFlat / getUserStreamByUsernameFlat | (id, cb?) | merged user dicts |
| getWebProfileInfo | (username, cb?) | private-host web profile |
| getFeedUserStreamItem | (itemId, pullToRefresh?, cb?) | streamed feed item |
| getUserMedias | ({userId, amount}, cb?) | user posts |
| getUserTags | ({userId, amount}, cb?) | tagged medias |
| getUserMediasPaginated / getUserTagsPaginated | ({userId, amount?, endCursor?}, cb?) | cursor pages |
| iterUserMedias | ({userId, pageSize?}) | async iterator |
| getPendingRequests | (options?, cb?) | pending threads |
| getAccountInfo | (cb?) | full private account info |
| editAccount | (fields, cb?) | edit profile (merges email/phone) |
| setBiography | (text, cb?) | set bio |
| getNewsInbox | (markAsSeen?, cb?) | news inbox |
| setAiInfoLabel | (enabled, cb?) | AI-generated label toggle |
| setNotificationSetting | (contentType, value, cb?) | app notification toggle |
| muteAllNotifications / disableAllNotifications | (value?, cb?) | mute-all / disable-all |
| notificationLikes / notificationComments / … (25) | (value?, cb?) | per-type toggles |
| setExternalUrl | (url, cb?) | set external URL |
| getBioLinks / removeBioLinks | (linkIds?, cb?) | list / remove bio links |
| changeProfilePicture | ({uploadId}, cb?) | change avatar |
| setPrivate / setPublic | (cb?) | visibility toggle |
| convertToBusiness / convertToCreator | (categoryId?, opts?, cb?) | professional conversion |
| convertToProfessional | (2\|3, categoryId?, opts?, cb?) | conversion core |
| getSecurityInfo | (cb?) | 2FA state, backup codes |
| changePassword | (old, new, cb?) | rate-guarded, never logged |
| sendPasswordReset | (identifier, cb?) | sends link (www ajax) |
| sendConfirmEmail / confirmEmail | (email[, code], cb?) | email two-step |
| sendConfirmPhone / confirmPhone | (phone[, code], cb?) | phone two-step |
| checkEmail / checkUsername / checkPhoneNumber | (…) | availability checks |
| sendSignupSmsCode / sendVerifyEmail | (…) | code delivery |
| checkConfirmationCode / checkAgeEligibility | (…) | code / age checks |
| accountsCreate / signup | (options) | legacy registration |
| getFeaturedAccounts / getAccountFamily | (…) | featured / family |
| usdidGenerate / usdidRegister | (…) | USDID identity + registration |
| usdidPublicKeyDer / usdidHeader | (…) | key DER / signed header |
| attestationCreateAndroidKeystore / attestationParams | (…) | keystore nonce flow |
| generateTOTP | (seed, opts?) | RFC 6238, zero deps |
| getLikedMedias | (amount?, cb?) | medias you liked |
| getMediaLikers | (mediaId, cb?) | who liked a media |
| getCommentReplies | (mediaId, commentId, amount?, cb?) | comment replies |
| getComments / getCommentsChunk | ({mediaId, amount}, cb?) | paged comment list |
| postComment | ({mediaId, text, repliedToCommentId?}, cb?) | post a comment |
| likeComment / unlikeComment | (commentId, cb?) | like toggle |
| pinComment / unpinComment | (mediaId, commentId, cb?) | pin toggle |
| bulkDeleteComments | (mediaId, commentIds, cb?) | bulk delete |
| checkOffensiveComment | (mediaId, text, cb?) | offensive? boolean |
| getCommentLikers | (commentId, amount?, cb?) | likers via GQL |
| getCollections / getCollectionIdByName | (…) | saved collections |
| getCollectionMedias / getCollectionMediasChunk | (options?, cb?) | collection medias (paged) |
| getCollectionMediasByName | (options?, cb?) | medias by collection name |
| saveMedia / unsaveMedia | (options?, cb?) | save toggle |
| uploadPhoto | ({path\|url, caption?, usertags?, location?, extraData?, scheduleAt?, coauthorUserIds?, shareToFacebook?, shareToThreads?}, cb?) | feed photo publish |
| uploadVideo | ({path\|url, caption?, thumbnail?, ...same}, cb?) | feed video publish |
| uploadAlbum | ({paths[], caption?, usertags? (nested per-item), ...same}, cb?) | carousel publish |
| uploadIGTV | ({path, title, caption?, thumbnail?, ...}, cb?) | IGTV publish (signed) |
| uploadReel | ({path\|url, caption?, thumbnail?, topics?, trial?, showPreviewInFeed?, ...}, cb?) | reel publish (signed) |
| uploadPhotoWithMusic / uploadAlbumWithMusic / uploadReelWithMusic | ({..., track, alacornSessionId?}, cb?) | music metadata attach (no mux) |
| clipTrialEligible | (cb?) | trial reels preflight |
| clipInterestTopics | (cb?) | topic catalog (name+fit_id) |
| changeReelCover | (mediaPk, coverPath, cb?) | published reel cover |
| deleteMedia | (mediaId, cb?) | delete own post |
| editMedia | ({mediaId, caption, title?, usertags?, location?}, cb?) | edit caption/tags |
| linkReel | (mediaId, targetMediaId, linkName?, cb?) | link reels |
| archiveMedia / unarchiveMedia | (mediaId, cb?) | archive toggle |
| getArchivedMedias | (amount?, cb?) | archived list |
| pinMedia / unpinMedia / pinReel / unpinReel | (mediaPk, cb?) | profile/reel pin |
| markMediaSeen / markClipsSeen | (mediaIds[], skipped?/blend?, cb?) | seen-state |
| getMediaOembed / getMediaAuthor / getMediaInfoV2 | (url\|pk\|id, cb?) | oembed/author/v2 fallback |
| videoRefreshResources | (mediaId, cb?) | refresh expired video URLs |
| createMediaNote / deleteMediaNote | ({mediaId, text?}\|noteId, cb?) | post-attached notes |
| createLivestream / startLivestream / endLivestream | (title?\|broadcastId, cb?) | live block (RTMP out of scope) |
| getLivestreamInfo / getLivestreamComments / getLivestreamViewers | (broadcastId, cb?) | live reads |
| getTimelineFeed / getReels / getExploreReels / getFriendsReels | (options?, cb?) | home feed + reels |
| getUserClips / getUserVideos | ({userId, amount?}, cb?) | reels/videos by user |
| uploadCutoutSticker | ({path}, cb?) | cutout sticker |
| uploadPhotoStory / uploadVideoStory | ({path\|url, caption?, mentions?, links?, hashtags?, locations?, medias?, stickers?, polls?, threadIds?}, cb?) | story publish |
| deleteStory | (storyId, cb?) | delete story |
| storyBuilder | (background) | chainable composer → .build() |
| getStoryViewers | ({storyPk, amount?}, cb?) | who viewed |
| getStoryTray | (options?, cb?) | followed users' stories |
| highlightPkFromUrl | (url) | pk from highlight URL |
| getUserHighlights | (userId, amount?, cb?) | highlight tray |
| getHighlightInfo | (highlightId, cb?) | single highlight |
| createHighlight | ({title, storyIds, coverStoryId?}, cb?) | create |
| editHighlight | (highlightId, options?, cb?) | add/remove/title/cover |
| changeHighlightTitle / changeHighlightCover | (…) | title / cover from file |
| addStoriesToHighlight / removeStoriesFromHighlight | (highlightId, storyIds, cb?) | story membership |
| deleteHighlight | (highlightId, cb?) | delete |
| getStoryInfo | (storyId, cb?) | story by pk_userid |
| getStoryLikers / getStoryLikersChunk | (storyId, cb?) | likers (paged) |
| likeStory / unlikeStory | (storyId, cb?) | heart toggle (seen first) |
| voteStoryPoll | (storyId, pollId, vote, cb?) | poll vote |
| getStickerTray | (cb?) | static sticker tray |
| getArchiveStoryDays / getArchiveStories | (options?, cb?) | archive |
| shareMediaToStory | ({mediaId, background, caption?}, cb?) | feed post as story |
| searchLocations / getLocationInfo / getLocationMedias | ({lat,lng}\|pk\|{locationPk,...}, cb?) | places |
| getLocationMediasTop / getLocationMediasRecent | (pk, amount?, cb?) | ranked / recent place medias |
| searchLocationsByName / searchLocationByPk | (name\|pk, cb?) | name / exact-pk lookup |
| completeLocation / buildLocation / locationStoryStickerId | (…) | smart fill / upload JSON / sticker id |
| getLocationGuides | (pk, cb?) | place guides |
| getAccountInsights / getMediaInsights | (\|mediaPk, cb?) | insights (business) |
| searchMusic / musicInFeedBrowser / musicTrending | (query\|..., cb?) | track discovery |
| searchMusicV2 / musicKeywordSearch | (query, options?, cb?) | v2 search / typeahead keywords |
| bookmarkTrack / bookmarkedTracks | (audioId, cb?) | bookmark / saved tracks |
| clipsAudioBrowser / verifyAudioTitle | (…) | clips candidates / title check |
| trackInfoByCanonicalId / trackInfoById / trackStreamInfoById | (id, cb?) | track detail |
| topsearchFlat / searchUsersAdvanced / searchHashtags | (query, cb?) | blended/user/tag search |
| searchUsers | (query, count?, cb?) | exact users/search port |
| searchPlaces / searchMedias | (query, cb?) | place/media search |
| webSearchTopsearch / webSearchTopsearchHashtags | (query, cb?) | web blended / hashtags |
| fbsearchItem / keywordTypeahead / typeaheadStream / typeaheadUsers | (…) | raw item / typeahead |
| recentSearches | (cb?) | recent searches |
| searchAccountsV2 / searchReelsV2 / topsearchV2 | (query, cb?) | SERP surfaces |
| suggestedProfiles | (userId, cb?) | fbsearch suggestions |
| getHashtagInfo | (tag, cb?) | hashtag profile |
| getHashtagMedias / getHashtagMediasTop / getHashtagMediasRecent / getHashtagMediasReels | (…) | tag medias by tab |
| followHashtag / unfollowHashtag / getFollowedHashtags | (tag, cb?) | tag follow management |
| getFollowersPrivateGql / getFollowingPrivateGql | (options?, cb?) | mobile-GQL follow lists |
| getClipsProfileGql / getInboxTrayGql | (userId, cb?) | clips / inbox-tray GQL |
| getFeedUserStreamItem | (itemId, pullToRefresh?, cb?) | streamed feed item |
| createNote | ({text, audience}, cb?) | create note |
| deleteNote | (noteId, cb?) | delete note |
| notesMusicBrowser | (cb?) | notes music candidates |
| createMusicNote | ({track, text, audience?}, cb?) | music note via inbox-tray GQL |
| getPendingCount | (cb?) | {total, unread} |
| approveRequest | (threadId, cb?) | approve a request |
Every method supports both promise and fca-style callback ((err, result)).
Errors are typed (require('@alaminnna/ica-unofficial').ChallengeRequired,
SessionExpired, RealtimeNotConnected, … — full list in src/exceptions).
📋 Logging
One logger, logLevel + logColors + showBanner in config:
silent— nothingerror— failures only (default-safe for bots)info— bot in/out, listening mode, session savesdebug— wire details ([REALTIME],[FBNS],[THREAD],[USER]tags, payloads)
🛠️ Projects Using This API
- Your bot here — built something with ICA? Open an issue with a link and get listed.
🤝 Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a new branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
Every src/ module follows the same contract: file header (responsibility/ported-from/may-use/must-not/debug), CommonJS, rate-guarded writes.
📄 License
MIT License - See LICENSE for details.
👨💻 Author — Al A Min (Alaminnna)
Al A Min — Alaminnna AI Developer • Full Stack Web Developer • Student • Entrepreneur • Open Source Builder 📍 Dhaka, Bangladesh
🌐 Website: alaminnna.ami.bd
⭐ Support
If this project is helpful, please give it a ⭐ on GitHub — it helps others find it.
🔗 Links
Built by Al A Min (Alaminnna) in Dhaka, Bangladesh. Next version already in progress.
Disclaimer: This is an unofficial API and is not officially supported by Instagram. Use responsibly and comply with Instagram Terms of Use.
