instagram-mcp
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
| experimental | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| instagram_login_statusA | Report whether an Instagram session is active and how the server is configured. Never logs in and never contacts Instagram, so it is the safe first call when something is not working. Use it to check which account is in use, whether write actions are permitted, and whether credentials are present at all. Returns: logged_in (bool), logged_in_as (str|null), summary (str), and a configuration object reporting which credentials are set (never their values), the write-mode flag, the session cache path and the delay range. |
| instagram_loginA | Authenticate with Instagram using the credentials in the server's .env. Login is automatic on first use, so call this only to recover from an expired session or to supply a two-factor/challenge code. The session is cached on disk and reused, which matters: repeatedly logging in from scratch is what makes Instagram flag an account. Args: verification_code: The code Instagram asked for, if any. force: Re-authenticate even if this process already holds a session. Returns: logged_in (bool), logged_in_as (str), summary (str) describing whether a cached session was reused, and session_file (str) where it was saved. |
| instagram_account_infoA | Get the profile of the logged-in account, including its private fields. Returns the signed-in user's own details (email, phone, gender and birthday are visible here but not on other people's profiles). Use instagram_get_user for anyone else. Returns: An account object: user_id, username, full_name, biography, external_url, is_private, is_verified, is_business, email, phone_number, gender. |
| instagram_get_userA | Get a user's full public profile: counts, biography, category and links. Use this to resolve a username to a user_id, to check whether an account is private before trying to read its posts, or to read follower/following/post counts. For the posts themselves use instagram_get_user_medias. Args: username: The handle to look up. user_id: The numeric id, if you already have it. Returns: user_id, username, full_name, url, biography, external_url, follower_count, following_count, media_count, is_private, is_verified, is_business, category, public_email, profile_pic_url. Fields Instagram does not return are omitted. |
| instagram_search_usersA | Search Instagram for users by handle or display name. Returns the same ranked list the app's search box shows. Results are shallow; follow up with instagram_get_user for counts and biography. Args: query: What to search for. amount: How many results to keep. Returns: count (int) and users[]: user_id, username, full_name, is_private, is_verified. |
| instagram_get_followersA | List the accounts that follow a user, newest follower first. Only works for your own account, public accounts, and private accounts you follow. Fetching thousands of followers is slow and is the fastest way to get an account rate limited - ask for the smallest number that answers the question. Args: username: The handle whose followers to list. user_id: The numeric id, if you already have it. amount: How many followers to fetch. Returns: count, requested, possibly_more (bool) and users[]: user_id, username, full_name, is_private, is_verified. |
| instagram_get_followingA | List the accounts a user follows. Same visibility rules and rate-limit caution as instagram_get_followers. Args: username: The handle whose following list to read. user_id: The numeric id, if you already have it. amount: How many accounts to fetch. Returns: count, requested, possibly_more (bool) and users[]: user_id, username, full_name, is_private, is_verified. |
| instagram_get_user_mediasA | List a user's posts (photos, videos, reels and albums), newest first. This is the main way to read someone's feed. Captions are truncated; call instagram_get_media on a single post for the full record and media URLs. Args: username: The handle whose posts to list. user_id: The numeric id, if you already have it. amount: How many posts to fetch. Returns: count, requested, possibly_more and medias[]: media_id, code, url, type (photo/video/reel/igtv/album), taken_at, author, caption, like_count, comment_count, view_count and location where present. |
| instagram_get_user_storiesA | Read a user's currently active stories. Stories vanish after 24 hours, so an empty list usually means the user has not posted today rather than that something failed. Viewing stories this way does not mark them as seen. Args: username: The handle whose stories to read. user_id: The numeric id, if you already have it. amount: How many stories to fetch. Returns: count and stories[]: story_id, type (photo/video), taken_at, author, video_duration, mentions, links, thumbnail_url, video_url. |
| instagram_get_mediaA | Get everything about one post, including media URLs and tagged users. Use after a listing tool when you need the full caption, the downloadable photo/video URLs, or the contents of an album. Args: media: A post URL, a shortcode, or a numeric media id. Returns: media_id, code, url, type, taken_at, author, caption, like_count, comment_count, view_count, location, thumbnail_url, video_url, video_duration, comments_disabled, tagged_users[] and album_items[] for multi-photo posts. |
| instagram_get_media_commentsA | Read the comments on a post. Returns top-level comments and replies as a flat list; a comment's replied_to_comment_id tells you what it is answering. Args: media: A post URL, shortcode or media id. amount: How many comments to fetch. Returns: count, requested, possibly_more and comments[]: comment_id, text, author, created_at, like_count, replied_to_comment_id. |
| instagram_get_media_likersA | List the accounts that liked a post. Instagram returns only a recent window of likers, not everyone, so the count here will not match the post's like_count on popular posts. Args: media: A post URL, shortcode or media id. amount: How many likers to keep. Returns: count and users[]: user_id, username, full_name, is_private, is_verified. |
| instagram_download_mediaA | Download a post's photo, video, or every item in an album, to local disk. Writes files to the machine running this server, not to Instagram, so it is safe in read-only mode. Albums produce one file per item. Args: media: A post URL, shortcode or media id. folder: Where to write the files. Returns: type (photo/video/reel/igtv/album), file_count and files[] with the absolute paths written. |
| instagram_get_timeline_feedA | Read the logged-in account's home timeline - posts from accounts it follows. This is the feed the app opens on. Sponsored items are skipped. Args: amount: How many posts to return. Returns: count and medias[]: media_id, code, url, type, taken_at, author, caption, like_count, comment_count. |
| instagram_get_hashtag_infoA | Get a hashtag's total post count. Use this to size a hashtag before pulling posts from it. Args: name: The hashtag, with or without a leading #. Returns: id, name, media_count (int) and url. |
| instagram_get_hashtag_mediasA | List posts carrying a hashtag, either the top posts or the most recent. The main discovery tool: use 'top' to see what performs well on a tag and 'recent' to see current activity. Args: name: The hashtag to read. sort: Ranked ('top') or chronological ('recent'). amount: How many posts to fetch. Returns: hashtag (str), sort (str), count and medias[]: media_id, code, url, type, taken_at, author, caption, like_count, comment_count. |
| instagram_search_locationsA | Find Instagram place pages by name, to get a location_id. Feed instagram_get_location_medias with the location_id you get back. Args: query: The place name to search for. amount: How many places to keep. Returns: count and locations[]: location_id, name, address, city, lat, lng. |
| instagram_get_location_mediasA | List posts tagged at a place. Args: location_id: The place id from instagram_search_locations. sort: Ranked ('top') or chronological ('recent'). amount: How many posts to fetch. Returns: location_id, sort, count and medias[] as in instagram_get_user_medias. |
| instagram_list_direct_threadsA | List direct-message conversations in the inbox. Gives you the thread_id needed by instagram_get_direct_thread and instagram_send_direct_message, plus a preview of the latest message. Args: amount: How many conversations to fetch. only_unread: Restrict to conversations with unread messages. Returns: count and threads[]: thread_id, title, is_group, participants[], last_activity_at, unread, pending, muted and last_message. |
| instagram_get_direct_threadA | Read the messages in one direct-message conversation, newest first. Shared posts, links and attachments are summarised rather than reproduced. Args: thread_id: Which conversation to read. amount: How many messages to fetch. Returns: thread_id, title, is_group, participants[], last_activity_at, unread and messages[]: message_id, sender_id, sent_at, item_type, is_sent_by_you, text, shared_post, link, attachment. |
| instagram_send_direct_messageA | Send a direct message, either into an existing thread or to new recipients. Sends immediately and cannot be undone from here. Confirm the exact recipient and wording with the person you are working for before calling this. Requires INSTAGRAM_ALLOW_WRITES=true. Args: text: The message to send. thread_id: Reply into this conversation. usernames: Or start/continue a conversation with these handles. user_ids: Or the same recipients by numeric id. Returns: sent (bool), message_id, thread_id, sent_at and text. |
| instagram_like_mediaA | Like a post as the logged-in account. Requires INSTAGRAM_ALLOW_WRITES=true. Liking in bursts is the quickest way to trigger an Instagram action block - space these out. Args: media: A post URL, shortcode or media id. Returns: liked (bool) and media (the post URL or reference acted on). |
| instagram_unlike_mediaA | Remove the logged-in account's like from a post. Requires INSTAGRAM_ALLOW_WRITES=true. Args: media: A post URL, shortcode or media id. Returns: unliked (bool) and media (the post reference acted on). |
| instagram_comment_mediaA | Post a comment on a post, or a reply to an existing comment. Publicly visible immediately. Confirm the wording with the person you are working for first. Requires INSTAGRAM_ALLOW_WRITES=true. Args: media: A post URL, shortcode or media id. text: What to say. replied_to_comment_id: Reply to this comment instead of the post. Returns: posted (bool) and comment: comment_id, text, author, created_at. |
| instagram_follow_userA | Follow a user as the logged-in account. For private accounts this sends a follow request instead. Requires INSTAGRAM_ALLOW_WRITES=true. Args: username: The handle to follow. user_id: The numeric id, if you already have it. Returns: followed (bool) and user_id. |
| instagram_unfollow_userA | Unfollow a user as the logged-in account. Requires INSTAGRAM_ALLOW_WRITES=true. Args: username: The handle to unfollow. user_id: The numeric id, if you already have it. Returns: unfollowed (bool) and user_id. |
| instagram_upload_photoA | Publish a photo to the feed. Instagram accepts JPEG; other formats are converted first. This posts publicly and immediately - confirm the file and caption with the person you are working for. Requires INSTAGRAM_ALLOW_WRITES=true. Args: path: The image file to publish. caption: The caption to publish with it. Returns: posted (bool) and media: media_id, code, url, type, taken_at, caption. |
| instagram_upload_videoA | Publish a video to the feed. For vertical short-form content use instagram_upload_reel instead - reels get different distribution. Requires INSTAGRAM_ALLOW_WRITES=true. Args: path: The MP4 to publish. caption: The caption to publish with it. thumbnail: Optional cover image. Returns: posted (bool) and media: media_id, code, url, type, taken_at, caption. |
| instagram_upload_reelA | Publish a reel. Expects vertical 9:16 video. Requires INSTAGRAM_ALLOW_WRITES=true. Args: path: The MP4 to publish as a reel. caption: The caption to publish with it. thumbnail: Optional cover image. Returns: posted (bool) and media: media_id, code, url, type, taken_at, caption. |
| instagram_upload_albumA | Publish several photos or videos as one carousel post. Instagram allows between 2 and 10 items. Requires INSTAGRAM_ALLOW_WRITES=true. Args: paths: The files to publish, in order. caption: The caption to publish with them. Returns: posted (bool), item_count and media: media_id, code, url, type, caption. |
| instagram_upload_storyA | Publish a photo or video to your story, where it lasts 24 hours. The file type is detected from its extension. Requires INSTAGRAM_ALLOW_WRITES=true. Args: path: The image or video to publish. caption: Optional caption stored with the story. Returns: posted (bool) and story: story_id, type, taken_at, author. |
| instagram_delete_mediaA | Permanently delete one of the logged-in account's own posts. This cannot be undone and Instagram keeps no copy you can restore from. Always confirm the exact post with the person you are working for first - read it back with instagram_get_media and check it is theirs. Requires INSTAGRAM_ALLOW_WRITES=true. Args: media: The post URL, shortcode or media id to delete. Returns: deleted (bool) and media (the reference acted on). |
| instagram_find_personA | Find who to message, searching your existing conversations before all of Instagram. Use this when a person is referred to by first name only, or when you are not sure which account is meant. People you already talk to rank first and come back with a thread_id you can pass straight to instagram_prepare_dm. Args: query: The name or handle to look for. limit: How many matches to return. Returns: count and matches[]: user_id, username, full_name, is_verified, thread_id (null if you have never messaged them), has_conversation, is_group. |
| instagram_build_style_profileA | Learn how you write by measuring your own sent DMs, and cache the result. Reads your conversations, keeps only the messages you sent, and measures the habits that make writing recognisable: message length, capitalisation, punctuation, emoji, how you spell laughter, shorthand, language mixing, and whether you send bursts of short messages. It also records how your voice shifts per contact, since nobody writes to their mother the way they write to a friend. Run once to set up, then again occasionally to refresh. The profile is saved locally and read by instagram_prepare_dm. Nothing is sent to Instagram. Args: thread_count: How many conversations to sample. messages_per_thread: How many messages to read from each. Returns: messages_analyzed, threads_analyzed, style_rules[] (plain-language instructions for imitating the voice), voice (the raw measurements), samples[] (real sent messages), contacts (per-person breakdown), and saved_to (the cache path). |
| instagram_get_style_profileA | Read the cached description of how you write, without contacting Instagram. Instant and free, so prefer it over rebuilding. If no profile exists yet it says so and you should call instagram_build_style_profile once. Returns: exists (bool), and when present: generated_at, messages_analyzed, style_rules[], voice, samples[]. |
| instagram_prepare_dmA | Get everything needed to draft a DM in one call: who, the conversation so far, and your voice. This is the tool to reach for when asked to write or reply to someone on Instagram. It bundles the recent back-and-forth with the measured rules of how you write - both in general and specifically to this person - so a draft can match your voice instead of guessing at it. It only gathers context. Nothing is sent until instagram_send_direct_message is called, which is deliberate: the draft should be shown to you first. If the person is ambiguous it returns the candidates instead of guessing. Args: person: Name or handle of who to message. thread_id: Or an existing conversation id. history: How many recent messages to include. Returns: Either needs_disambiguation with candidates[], or: contact, thread_id, conversation[] (chronological, each {from: 'you'|'them', text, at}), your_voice {rules[], samples[], measurements}, with_this_person {my_message_count, burst_rate, my_samples[]}, and last_message_from. |
| instagram_similar_accountsA | List the accounts Instagram itself considers similar to one you name. This reads the same "Suggested for you" graph the app shows under a profile, built from co-follow behaviour Instagram has already modelled. It is by far the highest-precision way to find more accounts like one you already like - far better than searching words - so the usual pattern is to find one good example by any means and then chain outward from it. Instagram refuses chaining for some accounts, mostly private or recently flagged ones; a second, older endpoint is tried before giving up. Args: username: The handle to find lookalikes for. user_id: The numeric id, if you already have it. amount: How many accounts to keep. Returns: seed (the account asked about), count and users[]: user_id, username, full_name, is_private, is_verified. |
| instagram_account_aboutA | Read Instagram's own "About this account" panel: country, join date, past handles. The country field is Instagram's own answer to where an account is based, which makes it the single most trustworthy location signal a profile can carry - worth far more than anything written in a bio. The join date and any former usernames are useful for spotting accounts that are newer, or more recycled, than they present themselves as. Args: username: The handle to look up. user_id: The numeric id, if you already have it. Returns: username, country, joined, former_usernames, and a note when Instagram returned the panel without a country. Fields it does not return are omitted. |
| instagram_search_postsA | Search posts by keyword, the way the app's search results tab does. Unlike hashtag lookup this matches captions and Instagram's own topic modelling, so it works for phrases nobody tags. Mostly useful as a way to reach the accounts behind the posts. Args: query: The phrase to search for. amount: How many posts to fetch. Returns: query, count and medias[]: media_id, code, url, type, taken_at, author, caption, like_count, comment_count. |
| instagram_search_startA | Begin a persona search and get back a search_id to drive it with. Nothing is fetched here. This validates the persona, tells you how certain each attribute could ever be, and opens a job on disk that survives crashes and rate limits - a full search is hundreds of calls over many minutes, so it is run stage by stage rather than in one blocking call. Scorable attributes: country, city, language, niche, gender, age_band, hair, build, follower_range, engagement, account_type, height, ethnicity. Some are advisory - they are reported but never allowed to move the ranking, because they cannot be read off a profile reliably enough to reject anyone on. After this, call instagram_search_recall with a probe plan. Args: persona: The attributes to search for, with optional weights. label: A human-readable name for the search. Returns: search_id, persona (compiled, with max_certainty per attribute) and next_step. |
| instagram_search_recallA | Sweep Instagram for candidates and add them to the search pool. Channels: accounts (handle/name/bio text, paginated), hashtags (post authors), places (authors of geotagged posts - the strongest location probe), posts (keyword search), seeds (accounts similar to one you name), followers, likers, commenters, co_tagged (who someone is photographed with). Fire many cheap probes rather than one careful one. Precision per probe is poor by design; what matters is that candidates surfacing on several unrelated channels rank higher, and that costs nothing extra. Channels fail independently, so a rate limit on one does not lose the others. Repeatable - call again with more probes to widen the pool. Args: search_id: The search to add to. probes: Channel names mapped to lists of probe arguments. Returns: pool_size, corroborated (found by more than one channel), api_calls, channels[] (per-probe counts), errors[] and next_step. |
| instagram_search_gateA | Cut the pool down using only data already fetched. Costs nothing. Run it straight after recall, and again after enrichment - the first pass can only see privacy and handles, while the second can see follower counts and bios. The under-18 floor does not depend on this: it is applied the moment age becomes readable and again at every exit, so no ordering of stages can let a minor through. Every drop is counted by reason, so a gate that removes too much can be loosened deliberately rather than guessed at. Args: search_id: The search to filter. exclude_private: Drop private accounts. exclude_shops: Drop apparent businesses. min_media: Minimum post count. follower_min: Follower floor. follower_max: Follower ceiling. max_pool: Maximum candidates to keep. Returns: kept, dropped, reasons (a count per drop reason) and next_step. |
| instagram_search_expandA | Grow the pool by asking Instagram for accounts similar to your best ones. This is the highest-yield stage in the whole search. Keyword probes find a foothold; this walks Instagram's own similarity graph outward from it, which returns candidates at far better precision than any search box can. Run it once the pool has a few good accounts in it, and again after scoring, when the seeds it picks are proven matches rather than guesses. Args: search_id: The search to expand. seed_count: How many top candidates to use as seeds. usernames: Explicit seeds, overriding the pool's own ranking. Returns: seeds_used, added, pool_size, api_calls, errors[] and next_step. |
| instagram_search_enrichA | Fetch full profiles, posts and country for the most promising candidates. This is the expensive stage - roughly three API calls per candidate, so a limit of 40 is around two hundred seconds of deliberate delay before it returns - and it is the one that gets accounts rate limited, so it works on a ranked subset rather than the whole pool. Progress is saved every few candidates and the lock is released between them, so an interruption costs only the candidate in flight and the server stays responsive throughout. Candidates found on several channels go first, since corroboration is the only precision signal available before a profile has been read. Fetches are best-effort: a candidate whose profile fails is recorded and skipped rather than ending the batch. Nothing already fetched is fetched again, so calling this repeatedly walks further down the pool. Args: search_id: The search to enrich. limit: How many candidates to fetch. posts_per_user: How many recent posts to read each. include_about: Whether to read the account's country. Returns: enriched, failed, api_calls, failures[] and next_step. |
| instagram_search_signalsA | Read every enriched candidate against the persona, for free. Contacts nothing and calls no model. It measures what is already on disk: gender from the given name and any stated pronouns, city from a recency-weighted cluster of where posts were geotagged, country from Instagram's own answer, language from caption wording, niche from tags and category, and age from any birth year given away. It resolves everything except appearance, which no free signal can reach - so run it before spending anything on images, and let vision see only what survives it. Args: search_id: The search to analyse. Returns: scored, confidence_breakdown, top[] (a preview of the ranking) and next_step. |
| instagram_search_shortlistA | Get the best candidates packaged for judging, optionally with their images. Each bundle says explicitly what is still unobserved and what was observed only weakly - those are the reasons to spend a look, and judging anything else is wasted effort. With download_images, each candidate's profile picture and recent post thumbnails are fetched and composed into a single numbered contact_sheet on the machine running this server. Read that one image rather than the tiles individually: it is a twelfth of the attention, the numbers let a judgement cite the tile it came from, and seeing every picture side by side is what makes the hard question answerable. That question is which face is the account holder. Feeds are full of friends, partners and clients, so before judging appearance find the face that recurs across the tiles and check it against the one marked 'avatar', which is the only picture certain to be them. Report the result as owner_face_confidence - a judgement made on the wrong face is worse than none, because it arrives sounding just as certain as a right one. Args: search_id: The search to shortlist from. limit: How many candidates to prepare. download_images: Whether to fetch images to local disk. images_per_user: How many images per candidate. Returns: count, candidates[] (each with biography, signals, needs_judging, weakly_observed and images[]) and how to report judgments back. |
| instagram_search_judgeA | Record what you saw in bios and images, and rescore against the persona. Report observations, not verdicts: say the hair was 'brown', not that it scored 0.2 against a persona wanting blonde. Whether an observation satisfies the persona is decided in one place, so judgments cannot grade themselves and a mismatch cannot be smuggled in as a good score. Naming the source matters: several images agreeing ('vision_multi') is a stronger measurement than one avatar ('vision_single'), and each carries a different ceiling. Use 'strength' below 1.0 when a reading is genuinely uncertain, and owner_face_confidence when it is unclear which face belongs to the account - both make the reading less certain rather than pretending it fits worse than it does. Args: search_id: The search to update. judgments: Per-candidate attribute readings. Returns: applied, rejected, unknown_candidates, problems[] and next_step. |
| instagram_search_resultsA | The ranked answer, with every attribute's evidence attached. Read two numbers, not one. Attributes marked 'unobserved' were not visible, which is not the same as not matching. Attributes marked advisory are reported but were never allowed to affect the ranking. Args: search_id: The search to read. limit: How many profiles to return. Returns: count, persona, results[] (rank, username, url, confidence, ranked_score, coverage, found_via, attributes with evidence) and the search's total api_calls. |
| instagram_search_listA | List every persona search on disk, most recently touched first. Reads local files only, so it never contacts Instagram. Use it to pick up a search that was interrupted - nothing already fetched needs fetching again. Returns: count and searches[]: search_id, label, stage, candidates, api_calls, updated_at. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/osAlhaddad1/instagram-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server