# Instagram Profile MCP Server — Tools for AI Agents (`afanasenko/instagram-profile-scraper-mcp-server`) Actor

Instagram MCP server for AI agents — six tools covering followers and following export, profile enrichment, similar-account discovery, keyword and location search, and post engagers. Up to 39 fields per profile: emails, bio links, engagement, category, language. Connect Claude or Cursor to /mcp.

- **URL**: https://apify.com/afanasenko/instagram-profile-scraper-mcp-server.md
- **Developed by:** [Andrey Afanasenko](https://apify.com/afanasenko) (community)
- **Categories:** MCP servers, Agents, Social media
- **Features:** Limited permissions, Multi-tenant, Server interface
- **Stats:** 47 total users, 7 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 profiles

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Instagram Profile MCP Server

[![Apify](https://apify.com/actor-badge?actor=afanasenko/instagram-profile-scraper-mcp-server)](https://apify.com/afanasenko/instagram-profile-scraper-mcp-server)

> **Give your AI agent live Instagram profile data — 5 MCP tools for followers/following export, profile enrichment, network expansion, keyword and location discovery. Connect Claude Desktop, Cursor, or ChatGPT to /mcp and get up to 39 fields per profile: emails, engagement, category, language.**

> ✨ **New — [Instagram Follower Tracker](https://apify.com/afanasenko/instagram-follower-tracker):** see who *started following* and who *unfollowed* any public account over time, on a schedule. Same author.

Same data and pricing as the parent [Instagram Profile Scraper](https://apify.com/afanasenko/instagram-profile-scraper) — but exposed as Model Context Protocol tools so your AI agent picks the right one by intent. Beyond what Instagram's official Graph API offers (which only returns your own business accounts): full bio, contacts, engagement rate, business category, language, Reels analytics, and 39 enrichment columns per row.

### What it does

Given an Instagram handle (or list of handles, seed accounts, keywords, or location), returns enriched profile records — bio, contacts, engagement metrics, post captions, language detection, and Reels analytics — the same columns as the parent scraper. Each tool is one Apify run; results land in the run's default dataset.

Optional paid add-on, the same as the parent's: set `extraction.includeAboutSection: true` in a tool call (or `includeAboutSection: true` in a regular run) to add `Account Country`, `Account Created` (`YYYY-MM`) and `Username Changes` (a count — Instagram does not show the old names) from Instagram's «About this account» panel. See the pricing table for when it starts.

### Output sample (one row, abridged)

```json
{
    "Account": "https://instagram.com/cristiano",
    "Source": "Direct Input",
    "Full Name": "Cristiano Ronaldo",
    "Followers Count": 615000000,
    "Following Count": 568,
    "Biography": "SIUUUbscribe to my YT channel",
    "Email": "press@example.com",
    "Email Source": "public_field",
    "Phone": "+1 555 123 4567",
    "Phone Source": "profile",
    "External URL": "https://www.cristianoronaldo.com",
    "Category": "Athlete",
    "Profile Picture": "https://instagram.fagx1-1.fna.fbcdn.net/...",
    "Address": "N/A",
    "Reels Count": 9,
    "Last Reel (Days Ago)": 7,
    "Median Views": 4200000,
    "Avg Likes": 1420000,
    "Avg Comments": 8400,
    "Views/Followers Ratio": "0.68%",
    "Posts per Month": 8.4,
    "Total Posts": 3654,
    "Detected Language": "English",
    "Last Post Within (Days)": 3,
    "Median ER": "1.42%",
    "Quality": "Good"
}
```

### ⚡ Quick start

1. **Connect from your AI client** (Standby `/mcp`) — see "Connect from Claude Desktop / Cursor / ChatGPT" below. The actor stays warm and answers in sub-second after the first call.
2. **Or click Run in the Apify Console** — pick `Mode 2: Analyze a Specific List of Accounts`, leave the demo input (`openai, claudeai`), and click Start. Output capped at 10 profiles for ~$0.10 trial cost.
3. **Or POST programmatically** to `apify.com/acts/.../runs` with `{"operationMode": "analyzeSpecificAccounts", "specificUsernamesList": ["claudeai"]}`.

### 🤖 Connect from Claude Desktop / Cursor / ChatGPT

The MCP server lives at `https://<actor-id>.apify.actor/mcp`. Auth via Apify token (query param or Bearer header).

#### Claude Desktop

Add to your `claude_desktop_config.json`:

```json
{
    "mcpServers": {
        "instagram": {
            "command": "npx",
            "args": [
                "-y",
                "@modelcontextprotocol/server-fetch",
                "https://instagram-profile-scraper-mcp-server.apify.actor/mcp?token=<YOUR_APIFY_TOKEN>"
            ]
        }
    }
}
```

Restart Claude Desktop. Six tools appear: `instagram_analyze_followers_following`, `instagram_analyze_specific_accounts`, `instagram_network_expansion`, `instagram_keyword_discovery`, `instagram_location_discovery`, `instagram_post_engagement_discovery`.

#### Cursor / ChatGPT / other MCP clients

Point them at `https://<actor-id>.apify.actor/mcp` with `Authorization: Bearer <YOUR_APIFY_TOKEN>`. Streamable HTTP transport requires the header `Accept: application/json, text/event-stream` on every POST.

#### VS Code (GitHub Copilot agent mode)

Add to `.vscode/mcp.json` in your workspace (or the global user config):

```json
{
    "servers": {
        "instagram": {
            "type": "http",
            "url": "https://instagram-profile-scraper-mcp-server.apify.actor/mcp",
            "headers": { "Authorization": "Bearer <YOUR_APIFY_TOKEN>" }
        }
    }
}
```

#### Claude Code (CLI)

```bash
claude mcp add --transport http instagram \
    "https://instagram-profile-scraper-mcp-server.apify.actor/mcp" \
    --header "Authorization: Bearer <YOUR_APIFY_TOKEN>"
```

#### Curl smoke

```bash
TOKEN=<YOUR_APIFY_TOKEN>
curl -sS -X POST "https://<actor-id>.apify.actor/mcp?token=$TOKEN" \
    -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
    -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
```

#### Example agent prompts

Once connected, these plain-English prompts route to the right tool automatically:

- *"Get the followers of @natgeo who have an email in their bio and more than 10k followers."* → `instagram_analyze_followers_following`
- *"Enrich these 20 Instagram handles with engagement rate and business category."* → `instagram_analyze_specific_accounts`
- *"Find accounts similar to @openai."* → `instagram_network_expansion`
- *"Find vet clinics on Instagram in Spanish-speaking markets."* → `instagram_keyword_discovery`
- *"Who posts from Shoreditch, London?"* → `instagram_location_discovery`
- *"Who liked and commented on this post? https://www.instagram.com/p/CA2aJYrg6cZ/"* → `instagram_post_engagement_discovery`

### ▶ Run as a regular Apify actor

The actor also accepts a normal INPUT_SCHEMA via `apify call` / Console / API. Pick a mode + provide its required field(s):

```json
{
    "operationMode": "analyzeSpecificAccounts",
    "specificUsernamesList": ["claudeai"],
    "extractEmail": true,
    "analyzeQuality": true
}
```

All 6 modes are supported in regular-run as well as MCP. See `.actor/input_schema.json` for every field.

### 💰 Pay-per-event pricing

Same as the parent scraper. Charged on event emission — pay only for what you get.

| Event | Price (USD) | When charged |
|---|---|---|
| `PROFILE_ANALYZED` | $0.01 | per profile successfully retrieved + analyzed |
| `USER_LIST_FETCHED` | $0.10 | per follower OR following list dump (Mode 1 only) |
| `ABOUT_ACCOUNT` (Account country & age lookup) | $0.005 | only with `includeAboutSection` on, from **12 November 2026**: per delivered profile whose «About this account» panel returned its country or creation month |

**The account-country add-on is off by default and starts on 12 November 2026.** Until then the option is ignored — no extra columns and nothing extra charged — and the tool result says so. Profiles your filters reject are not looked up, and a panel that is empty or does not answer is not charged (its three columns read `N/A`).

Not-found, private, and failed retrievals are never billed. Filters run **after** a profile is retrieved, so they narrow your output, not your bill — see [What filters do to your bill](#what-filters-do-to-your-bill). `RUN_SUMMARY.profilesCharged` shows the exact billed count after every run.

#### What filters do to your bill

Instagram has no API that filters for you. Every filter in this actor runs on a profile that has already been retrieved, so:

- **Filters narrow your output, not your bill.** A profile your filter rejects still counts as one `PROFILE_ANALYZED`.
- **Your first run is the exception.** On a customer's very first run, filter rejects are not billed until the run finds a profile that matches — that first match is where the guarantee ends and normal billing resumes. Up to 100 rejects per run are covered this way.
- **A zero-row run tells you which filter did it.** The `USER_MESSAGE` record (and, over MCP, the tool result itself) names the filter by the exact title it has in the Input form, and shows what the rejected profiles actually looked like — e.g. "the profiles we found had 14,320–291,698 followers, your minimum is 999,999,999".
- **You can exclude by words, too.** `filters.excludeKeywords` (in a regular run: "Exclude Profiles With Keywords") skips profiles whose bio, name or post captions — the same place `keywordLocation` points at — contain any of these words or phrases, e.g. `keywords: ["constructor"]` + `excludeKeywords: ["SEO", "marketing agency"]`. Whole words, not case-sensitive: `seo` drops `SEO expert` and `#seo`, not `Seoul`. A skipped profile is charged like any other filter reject.

Practical order: start with no filters and a small `maxCount`, look at what comes back, then add one filter at a time.

**Demo input cap**: when you leave the example seed (`natgeo` / `openai, claudeai` / `openai`) at default with no `maxCount`, the run is capped at 10 profiles — at most a $0.20 trial on any plan (Mode 1 adds one $0.10 list fetch; Modes 2/3 cost ~$0.10).

### Free vs paid

|  | Free plan | Paid plan |
|---|---|---|
| Runs per calendar month | 15 — each MCP tool call counts as one run, the same as a regular run | unlimited |
| Analyzed profiles per calendar month | 100 — every profile fetched counts, including ones your filters reject | unlimited |
| Profiles / run | 50 | unlimited (50 per MCP call) |
| Mode 1 targets | 3 | unlimited |
| Modes 3/4/5/6 discovery | 5 profiles — with a filter on, 5 **matching** profiles: up to 25 candidates are checked and the search stops at 5 matches or 20 filter rejects, whichever comes first (rejects are charged like any analyzed profile) | unlimited (50 per MCP call) |
| Email | masked (`j***@a***.com`) | full address |
| `searchDepth=2` (Mode 3) | not available | available |

When the month's free runs or profiles are used up, the next call or run stops before fetching anything, charges nothing, and says when the allowance resets (the 1st of the next month, UTC). The demo on the example input does not count. While an unusually large number of new free accounts run this Actor at once, free runs from new accounts may be reduced to one profile for a short while. Paid plans are never affected.

### 🔒 Storage records

Every run writes the same record surface as the parent scraper:

| Key | Format | Purpose |
|---|---|---|
| `RUN_SUMMARY` | JSON | profiles found / enriched / skipped, email-resolution stats, plan, durationSeconds, MCP tool-call count, `profilesCharged` / `listFetchesCharged` (real billed counts), `filterRejectsWaived` (filter rejects the first-run guarantee covered) and `cost.spent` |
| `SKIPPED_ACCOUNTS` | JSON | per-username list of skips with reason + category (`filter` / `not_found` / `private` / `list_hidden` / `error`); `list_hidden` is a public account whose owner has hidden its followers or following list |
| `FREE_LIMITS_APPLIED` | JSON | which FREE-plan ceilings actually clipped the run (profile cap, target cap, expansion cap) — empty on paid |
| `USER_MESSAGE` | JSON | diagnosis banner: missing-input errors, zero-result cause + top fix, narrow-filter heads-up — present whenever a run fails validation or delivers nothing |
| `FILTER_BURN_PREDICTION` | JSON | pre-run yield estimate when filters are active (regular runs only) |
| `status.html` | HTML | live progress dashboard (Apify Console Output tab); zero-result runs show the diagnosis instead of a bare stats grid |

Standby instances aggregate counters across all tool calls in the same instance and flush every 10 calls + on `Actor.exit`.

### 🛠️ Programmatic API

Trigger the actor as a regular run:

```bash
curl -X POST "https://api.apify.com/v2/acts/instagram-profile-scraper-mcp-server/runs?token=$TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"operationMode":"analyzeSpecificAccounts","specificUsernamesList":["claudeai"]}'
```

Read the dataset:

```bash
curl "https://api.apify.com/v2/datasets/<datasetId>/items?token=$TOKEN&format=json"
```

### 💡 Tips & Best Practices

#### Getting Maximum Results

- Start with one handle per mode to validate filter shape — Mode 2 with a single username gives the cheapest first signal.
- Layer filters in this order: follower range → contact info → category/keyword. Earlier filters let the actor skip reading posts and Reels for profiles it has already rejected, so the run finishes faster (the per-profile charge is the same either way).
- For Mode 4 keyword discovery, combine `searchQueries` (broader Instagram account search) with `searchHashtags` (top-post authors, biased toward larger accounts) to widen the funnel.
- For Mode 5 location discovery, use `City, Country` format (e.g. `London, UK`) — the country hint disambiguates same-named cities.

#### Cost Optimization

- Use Apify's Run options panel (Maximum cost per run) to cap a run at any USD amount. Independent of the per-event pricing.
- Set `maxCount` realistically per mode — most discovery sources return hundreds of candidates. Free plan ceiling (50 / 3 / 5 by mode) acts as a backstop.
- The real cost lever is `maxCount`, not filters: filters decide what you keep, `maxCount` decides how many profiles are retrieved and billed.
- Skip `extractPosts` / `searchContactsInPosts` when you only need follower counts and bio — those toggles make each profile take a little longer.

#### Performance

- The actor runs up to 16 requests in parallel by default.
- Mode 5 (Location Discovery) is the slowest mode because it resolves each seed via a place-name search before fetching tagged media. Pre-resolving to a numeric place ID (paste the integer instead of `City, Country`) skips the search step.
- Standby instances stay warm across MCP tool calls — the second tool call in a session is faster than the first.

#### Data Quality

- The 39-column dataset has many nullable fields — check `Email` / `Reels Count` / `Median ER` aren't all "N/A" on a sample row before scaling.
- "Mutual Follow" column appears only when Mode 1 has both `analyzeFollowers` AND `analyzeFollowing` enabled.
- "Tagged Location" column appears only in Mode 5. "Liked Posts", "Comments Left" and "Posts Engaged" appear only in Mode 6.
- "Matched By" column appears when `categoryFilter` or `keywords` is active — values: `category`, `keyword`, `both`.
- Free-plan emails are masked (`j***@a***.com`) — the underlying value is correct on the paid plan.

### ❓ FAQ

**Why a separate actor instead of the parent's modes?** The MCP wrapper presents 6 narrow tools — agents can pick by intent. The parent actor uses one INPUT_SCHEMA with a `mode` enum, which is fine for humans but unwieldy for AI clients reading a tool catalog.

**Same dataset shape as parent?** Yes — the same column titles and example values; 39 columns on every profile, plus the columns of the mode that found it.

**Same pricing as parent?** Yes — $0.01 per profile + $0.10 per list dump, plus the optional $0.005 account-country add-on from 12 November 2026, mirroring the parent 1:1.

**Can I get the country an account is based in, or when it was created?** Yes — pass `extraction: { "includeAboutSection": true }` to any tool. Every delivered profile gets `Account Country`, `Account Created` (`YYYY-MM`) and `Username Changes` (a count; Instagram does not show the former names) from Instagram's «About this account» panel. It is a separate paid event, $0.005 per profile whose panel answered, starting 12 November 2026 — before that date the option is ignored and nothing extra is charged. «Not shared» means the owner hid the country.

**Does Standby cost extra?** Standby billing is per actor compute time, not per MCP tool call. Idle Standby instances stop after a few minutes of no traffic.

**Will agents auto-discover this on `mcp.apify.com`?** Yes once the actor is public + categorized as `MCP_SERVERS`. Indexing happens within ~1 hour of publish.

**How is this different from the tool `mcp.apify.com` auto-generates for any actor?** The auto-generated integration exposes one generic "call this actor with this input object" tool, so your agent has to understand the full 45-field input schema to use it. This server presents 6 narrow tools with small, typed argument sets — agents pick the right one by intent and fill 2–3 arguments instead of composing a full input object.

**Do I need an Instagram login, cookies, or a session?** No. The server reads only public profile data — you never provide credentials, and there is no account of yours that could get flagged or banned.

**Can I analyze many accounts in one go?** Yes. Mode 2 accepts up to 50 usernames per call/run, Mode 1 accepts multiple target accounts (3 on the free plan), and every discovery mode returns batches. For thousands of profiles, loop tool calls or schedule regular runs.

**Can I run it on a schedule?** Yes — regular runs work with [Apify Schedules](https://docs.apify.com/platform/schedules) on any plan, including free. Save your input as a task, attach a schedule, and each run appends to its own dataset. For recurring follower change tracking specifically, the [Instagram Follower Tracker](https://apify.com/afanasenko/instagram-follower-tracker) is purpose-built.

**How do I get more results per run?** Three levers: raise the mode's `maxCount` cost-control field (0 = plan ceiling), loosen filters (contact-info and language filters reject the most), and on the free plan mind the ceilings — 50 profiles (Modes 1/2), 3 targets (Mode 1), 5 discovered profiles (Modes 3/4/5/6; with a filter on, 5 matching profiles out of up to 25 candidates checked), and per calendar month 15 runs (a tool call is a run) and 100 analyzed profiles. Paid plans lift these to 50 per MCP call with unlimited calls/runs.

**What happens when a run returns 0 profiles?** The run explains itself: the `USER_MESSAGE` storage record and the live status page show the cause (all filtered vs unreachable vs empty source) and the top fix. Empty fetches are never billed. If filters rejected everything, the message names the filter that did it and states exactly what was charged — and on a first run, nothing is charged for those rejects (see [What filters do to your bill](#what-filters-do-to-your-bill)).

**My filters rejected everything — do I pay again to retry?** The retry itself costs what it costs: the profiles have to be retrieved again. What you get for free is the diagnosis — the filter is named, with the observed numbers next to it, so the second attempt is informed rather than another guess. On your very first run, the rejects that led to it were not billed at all.

### 🔗 Related actors

| Actor | Description |
|---|---|
| [instagram-profile-scraper](https://apify.com/afanasenko/instagram-profile-scraper) | Parent scraper — same data + pricing, full INPUT_SCHEMA flow |
| [instagram-followers-scraper](https://apify.com/afanasenko/instagram-followers-scraper) | Lighter spinoff — followers/following only, single mode |
| [instagram-related-profiles-scraper](https://apify.com/afanasenko/instagram-related-profiles-scraper) | Lighter spinoff — Network Expansion only, single mode |
| [instagram-follower-tracker](https://apify.com/afanasenko/instagram-follower-tracker) | Spinoff — tracks new / lost followers & following for a public account over time (stateful, scheduled) |
| [instagram-reel-script-extractor](https://apify.com/afanasenko/instagram-reel-script-extractor) | Reel content instead of profiles — spoken transcript, on-screen text, hook and structure |
| [instagram-influencer-search](https://apify.com/afanasenko/instagram-influencer-search) | Lighter spinoff — keyword & hashtag search only, single mode |
| [instagram-post-likers-scraper](https://apify.com/afanasenko/instagram-post-likers-scraper) | Lighter spinoff — post likers & commenters only, single mode |
| [instagram-comments-scraper](https://apify.com/afanasenko/instagram-comments-scraper) | Comment TEXT instead of profiles — every comment and reply as a row, @mentions and #hashtags in their own columns |
| [tiktok-profile-scraper](https://apify.com/afanasenko/tiktok-profile-scraper) | TikTok counterpart — profiles, followers, engagement metrics by the same author |
| [zillow-mcp-server](https://apify.com/afanasenko/zillow-mcp-server) | MCP server by the same author — US real-estate listings as agent tools |
| [instagram-email-scraper](https://apify.com/afanasenko/instagram-email-scraper) | Lighter spinoff — the contact details a list of usernames publishes, single mode |

### ⚠️ Disclaimer

This actor scrapes only public Instagram profile data. Respect Instagram's Terms of Service and applicable data-protection regulations (GDPR, CCPA) when handling collected data. The MCP wrapper does not change what data is collected — same surface as the parent scraper.

# Changelog

This Actor's version history is a separate document: https://apify.com/afanasenko/instagram-profile-scraper-mcp-server/changelog.md

# Actor input Schema

## `operationMode` (type: `string`):

Select the main function of the Actor. The fields you need to fill in bellow will depend on this choice.

## `targetUsernames` (type: `array`):

Enter Instagram usernames or profile URLs (one per line). Works with 1 or multiple accounts — public accounts with visible lists only, private targets return nothing. Free plan: up to 3 target accounts.

## `analyzeFollowers` (type: `boolean`):

When true, extracts the followers list of each target account in Mode 1. At least one of analyzeFollowers / analyzeFollowing must be true; combine both to derive the Mutual Follow column.

## `analyzeFollowing` (type: `boolean`):

When true, extracts the following list (accounts each target follows) in Mode 1. Combine with analyzeFollowers to derive the Mutual Follow column.

## `maxCount` (type: `integer`):

Maximum number of profiles to analyze across all targets in Mode 1 — hard cost ceiling because each analyzed profile is one paid event ($0.01). Free plan ceiling: 50 profiles regardless of this value. 0 = unlimited. Default 0.

## `specificUsernamesList` (type: `array`):

Enter usernames or profile URLs to analyze directly (one per line).

## `maxCountList` (type: `integer`):

Maximum number of profiles from the list to analyze in Mode 2 — hard cost ceiling because each analyzed profile is one paid event ($0.01). Free plan ceiling: 50 profiles regardless of this value. 0 = unlimited. Default 0.

## `startUsernames` (type: `array`):

Enter 1-5 usernames to start the network expansion from (one per line).

## `searchDepth` (type: `string`):

How many layers deep to search for related profiles. Depth 2 can find thousands of profiles (not working on a free plan).

## `maxCountExpansion` (type: `integer`):

Maximum number of unique profiles to discover and analyze in Mode 3 (Network Expansion) — hard cost ceiling because each analyzed profile is one paid event ($0.01). Free plan ceiling: 5 profiles — with a filter set, 5 matching profiles (up to 25 candidates are checked, and the ones your filters reject are charged like any analyzed profile). 0 = unlimited. Default 0.

## `searchQueries` (type: `array`):

**Required for Mode 4** (provide either Search Queries or Search Hashtags). Enter one or more search terms (one per line). The actor will search Instagram accounts matching each term, then apply all your filters (language, followers, category, etc.) to the discovered candidates. Great for building a seed list from scratch in any language. Example: veterinario, clinica dental, cardiólogo.

## `searchHashtags` (type: `array`):

**Required for Mode 4** (provide either Search Queries or Search Hashtags). Enter one or more Instagram hashtags (without the # symbol, one per line). For each hashtag the actor collects authors of top posts — these tend to be larger, already-popular accounts, so follower-based filters (e.g. >50k) keep a much higher share than plain keyword search. Combine with Search Queries to widen the top of the funnel. Example: veterinario, clinicaveterinaria, perros.

## `maxSearchPagesPerQuery` (type: `integer`):

How many pages to fetch per search query and per hashtag. More pages = more candidates but slower discovery. Range 1–50. Default 5.

## `maxCountDiscovery` (type: `integer`):

Maximum number of unique candidates from keyword/hashtag search to analyze in Mode 4 — hard cost ceiling because each analyzed profile is one paid event ($0.01). Free plan ceiling: 5 profiles — with a filter set, 5 matching profiles (up to 25 candidates are checked, and the ones your filters reject are charged like any analyzed profile). 0 = unlimited. Default 0.

## `locationSeeds` (type: `array`):

Recommended format: 'City, Country' (e.g. 'London, UK', 'Bristol, UK'). The country hint disambiguates same-named cities — without it, 'Bristol' resolves to Bristol, Connecticut. You can also paste raw numeric Instagram place IDs for full control. Authors of recent + top-tagged media at each place become candidates.

## `maxCountLocation` (type: `integer`):

Maximum number of unique candidates from location-tagged media to analyze in Mode 5 — hard cost ceiling because each analyzed profile is one paid event ($0.01). Free plan ceiling: 5 profiles — with a filter set, 5 matching profiles (up to 25 candidates are checked, and the ones your filters reject are charged like any analyzed profile). 0 = unlimited. Default 0.

## `postUrls` (type: `array`):

Instagram post or reel links, one per line — e.g. https://www.instagram.com/p/CA2aJYrg6cZ/. Every account that liked or commented on those posts becomes a candidate. Deleted posts, and posts inside a private account, cannot be read and are named in the run summary.

## `engagementType` (type: `string`):

Which kind of engagement makes an account a candidate. 'Likers and commenters' merges both into one deduplicated list. 'Commenters only' returns a set you can verify is complete, because comments are read page by page. 'Likers only' is the widest top of funnel.

## `maxCountEngagers` (type: `integer`):

Maximum number of unique engagers to analyze in Mode 6 — hard cost ceiling because each analyzed profile is one paid event ($0.01). Counts unique accounts, not likes: someone who liked three of your posts is one profile. Private accounts are skipped before analysis and never counted. Free plan ceiling: 5 profiles — with a filter set, 5 matching profiles (up to 25 candidates are checked, and the ones your filters reject are charged like any analyzed profile). 0 = unlimited. Default 0.

## `extractEmail` (type: `boolean`):

When true, extracts public email addresses from each profile (Instagram Contact button, bio text, and — when the profile shows none — the page its bio link opens). Set false to skip email extraction. Default true. Populates the Email + Email Source columns.

## `extractPhoneNumber` (type: `boolean`):

When true, extracts public phone numbers from each profile (Instagram Contact button, bio text, and — when the profile shows none — the page its bio link opens). Set false to skip. Default false (most lead-gen workflows rely on email/website). Populates the Phone column when enabled.

## `extractWebsiteUrl` (type: `boolean`):

When true, extracts the bio-link URL from each profile. Set false to skip. Default true. Populates the External URL column.

## `extractBusinessCategory` (type: `boolean`):

When true, extracts the Instagram business category for business profiles (e.g. «Athlete», «Beauty, Cosmetic & Personal Care»). Set false to skip. Default true. Populates the Category column.

## `extractPhysicalAddress` (type: `boolean`):

When true, extracts the public street address shown on a business profile when disclosed. Default false. Populates the Address column.

## `includeAboutSection` (type: `boolean`):

When true, reads Instagram's «About this account» panel for each delivered profile and adds Account Country (where the account is based), Account Created (YYYY-MM) and Username Changes (a count, not the old names). Charged separately: $0.005 per profile whose panel answered, from 12 November 2026; until then the option is ignored and nothing extra is charged. Profiles your filters reject are not looked up. Default false.

## `analyzeQuality` (type: `boolean`):

When true, computes Median Engagement Rate and a coarse Quality score (Excellent/Good/Average/Poor) from recent posts. Makes each profile take a little longer. Default true. Populates Median ER, Quality, Avg Likes, Avg Comments, Posts per Month columns.

## `minEngagementRate` (type: `integer`):

Minimum median engagement rate (%) — profiles below this are skipped. 0 = no filter (default). Needs Calculate Engagement Rate (ER) switched on, and needs readable recent posts: accounts with no posts we can read are rejected outright, not given the benefit of the doubt.

## `extractPosts` (type: `boolean`):

When true, extracts caption text from the 8 most recent feed posts. Default false. Populates Post 1..Post 8 columns and is a prerequisite for searchContactsInPosts.

## `searchContactsInPosts` (type: `boolean`):

When true, scans the extracted post captions for emails and phone numbers (e.g. booking@brand.com referenced in a Reel description). Requires extractPosts to also be true. Default false. Populates Email from Posts + Phone from Posts columns.

## `keywords` (type: `array`):

Only process profiles containing **any** of these keywords. Not case-sensitive.

## `keywordLocation` (type: `string`):

Where to search for keywords — `bio_or_name` (default), `bio` only, `name` only, `posts` (recent post captions, requires post data), or `anywhere`. Choose `posts` for high-intent niche matching beyond profile metadata.

## `excludeKeywords` (type: `array`):

Skip profiles whose text contains **any** of these words or phrases — e.g. keep `constructor` in Filter by Keywords and add `SEO` or `marketing agency` here. Searched in the same place as **Search Keywords In**, as whole words, not case-sensitive: `seo` drops `SEO expert` and `#seo`, not `Seoul`. Works with or without Filter by Keywords. A skipped profile is still read and charged, like any profile a filter rejects.

## `locationKeywords` (type: `string`):

Keeps a profile only if its public business address or city contains one of your terms — any single match keeps it.
• **Separate terms with a comma.** A space-separated list is read as one long term and almost never matches.
• **Only professional accounts publish an address** — so search for business types ("dental clinic"), not people ("fitness coach"). Used alone, this filter returns nothing on 3 runs out of 4.
• **Write a list, not one city** — "Miami, Fort Lauderdale, Boca Raton, Florida". Terms are OR-ed, so an extra term can only help: lists of six or more delivered four times as often as a single word.
• **Add the region in full, the way Instagram spells it** — the state in the US, Canada, Mexico and India ("Miami, Florida"), the country in English elsewhere ("Berlin, Germany"). The field carries "United Arab Emirates", not "UAE".
• **Copy Instagram's spelling** — accents included ("São Paulo", not "Sao Paulo"), and whole place names: short terms match inside longer words ("us" hits "Houston").
• **Want accounts in a place, not businesses at it?** Use Mode 5: Discover by Location instead — do not combine the two.
Leave empty for no location filter.

## `profileLanguage` (type: `string`):

Language filter — restrict results to profiles whose bio + post text was detected as this language. «any» (default) bypasses detection. Detection runs on bio + recent post captions via Google CLD3. Closely related languages can be mistaken for one another — Serbian written in Latin letters is usually detected as Croatian, and Malay as Indonesian.

## `minFollowers` (type: `integer`):

Minimum follower count — profiles below this are skipped. 0 = no minimum (default). Filters run after each profile is retrieved, so a narrow band decides what you keep, not what you pay. Start at 0 and tighten once you have seen what the source returns.

## `maxFollowers` (type: `integer`):

Maximum follower count — profiles above this are skipped. 0 = no maximum (default). Useful for excluding mega-accounts when targeting niche creators.

## `lastPostDays` (type: `integer`):

Activity recency filter — only include profiles that posted any feed content within the last X days. 0 = disabled (default). Drops dormant accounts. Requires post data.

## `lastReelDays` (type: `integer`):

Reel-activity recency filter — only include profiles that posted a Reel within the last X days. 0 = disabled (default). Accounts that post no Reels at all are rejected by this filter, not just stale ones.

## `minPostsInPeriod` (type: `integer`):

Only process accounts with at least this many posts within the specified period. Set to 0 to disable. **Requires post data.**

## `postsCheckPeriodDays` (type: `string`):

The time period (in days) to check for the minimum number of posts.

## `minMedianViews` (type: `integer`):

Only process accounts where the median view count of their recent posts is greater than this number. Set to 0 to disable.

## `viewFollowerRatioMin` (type: `integer`):

Reject profiles whose median Reel views are below this % of their follower count. 0 = no minimum. For B2B targeting, 5 is a sensible floor.

## `viewFollowerRatioMax` (type: `integer`):

Reject profiles whose median Reel views are above this % of their follower count (filters out viral one-hit accounts). 0 = no maximum. For B2B targeting, 30 is a sensible ceiling.

## `contactInfoType` (type: `string`):

Contact-info filter — keep only profiles that have at least the requested contact data. «any» (default, no filter), «emailOrPhone», «emailOnly», «phoneOnly», «emailAndPhone». A contact counts wherever it is found: the Contact button, the bio text, or the page the bio link opens. Instagram shows a public email or phone only on professional accounts whose owner filled them in, so this is the filter most likely to reject nearly everything. Runs independently of the extract-\* flags above.

## `hasWebsite` (type: `boolean`):

When true, only profiles with a website link in their bio pass the filter. Default false. Useful for B2B / lead-gen lists where the link is a qualified-traffic signal.

## `accountType` (type: `string`):

Keep only one kind of account. «Any» is the default. Instagram has three: business and creator are both professional and both can show a Contact button; personal cannot. Creators are a big share of most result sets — measured on 60 accounts per mode: 52% of suggested profiles, 53% of search results, 48% of followers, 41% of a pasted list, 10% of post likers. Looking for influencers? Pick «Creator», not «Personal».

## `filterForInfluencers` (type: `boolean`):

When true, only profiles whose category matches an influencer-leaning list (e.g. Blogger, Artist, Public Figure, Digital Creator) pass the filter — helps automatically exclude most brands and organizations. Default false. Requires profile data.

## `categoryFilter` (type: `string`):

Business-category filter — restrict to profiles whose Instagram-set category matches this exact value (e.g. «Public Figure», «Beauty, Cosmetic & Personal Care», «Fitness Trainer»). «any» (default) = no filter. Independent of filterForInfluencers. MCP clients see only the first ~10 enum entries due to truncation; programmatic callers can pass any of the 1090+ exact values.

## `filterCombination` (type: `string`):

How to combine the Business Category filter and the Keywords filter when BOTH are set.
**AND** (default): a profile must match both filters (existing behavior).
**OR**: a profile passes if it matches **either** the category **or** any of the keywords. The output dataset adds a **Matched By** column showing which filter(s) let each profile through (`category`, `keyword`, or `both`).

## `mustBeVerified` (type: `boolean`):

When true, only verified (blue checkmark) accounts pass the filter. Default false. Useful for vetted-creator outreach lists; off by default since most creator audiences are unverified.

## Actor input object example

```json
{
  "operationMode": "analyzeFollowersFollowing",
  "targetUsernames": [
    "natgeo"
  ],
  "analyzeFollowers": true,
  "analyzeFollowing": false,
  "maxCount": 0,
  "specificUsernamesList": [
    "openai",
    "claudeai"
  ],
  "maxCountList": 0,
  "startUsernames": [
    "openai"
  ],
  "searchDepth": "1",
  "maxCountExpansion": 0,
  "searchQueries": [],
  "searchHashtags": [],
  "maxSearchPagesPerQuery": 5,
  "maxCountDiscovery": 0,
  "locationSeeds": [],
  "maxCountLocation": 0,
  "postUrls": [],
  "engagementType": "both",
  "maxCountEngagers": 0,
  "extractEmail": true,
  "extractPhoneNumber": false,
  "extractWebsiteUrl": true,
  "extractBusinessCategory": true,
  "extractPhysicalAddress": false,
  "includeAboutSection": false,
  "analyzeQuality": true,
  "minEngagementRate": 0,
  "extractPosts": false,
  "searchContactsInPosts": false,
  "keywords": [],
  "keywordLocation": "bio_or_name",
  "excludeKeywords": [],
  "locationKeywords": "",
  "profileLanguage": "any",
  "minFollowers": 0,
  "maxFollowers": 0,
  "lastPostDays": 0,
  "lastReelDays": 0,
  "minPostsInPeriod": 0,
  "postsCheckPeriodDays": "30",
  "minMedianViews": 0,
  "viewFollowerRatioMin": 0,
  "viewFollowerRatioMax": 0,
  "contactInfoType": "any",
  "hasWebsite": false,
  "accountType": "any",
  "filterForInfluencers": false,
  "categoryFilter": "any",
  "filterCombination": "AND",
  "mustBeVerified": false
}
```

# Actor output Schema

## `liveStatus` (type: `string`):

Open the status page for this run. It updates as tool calls come in and remains available after the run finishes.

## `profiles` (type: `string`):

All enriched Instagram profile records collected during this run, across MCP tool calls and the regular-run dispatcher. The same columns as the parent Instagram Profile Scraper — 39 on every profile, plus the columns of the mode that found it.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "operationMode": "analyzeFollowersFollowing"
};

// Run the Actor and wait for it to finish
const run = await client.actor("afanasenko/instagram-profile-scraper-mcp-server").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "operationMode": "analyzeFollowersFollowing" }

# Run the Actor and wait for it to finish
run = client.actor("afanasenko/instagram-profile-scraper-mcp-server").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "operationMode": "analyzeFollowersFollowing"
}' |
apify call afanasenko/instagram-profile-scraper-mcp-server --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,afanasenko/instagram-profile-scraper-mcp-server"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/D4KTavVyAu6ylOUDl/builds/aX4I2s0DawpIJth1f/openapi.json
