# Instagram Profile Scraper - Posts, Emails & Engagement (`flash_scraper/instagram-profile-scraper`) Actor

Scrape Instagram profiles without login: bio, category, followers, business email, phone and address, every bio link unwrapped, recent posts and reels with likes/comments/captions/hashtags, engagement rate, and related-account discovery. No API key, no cookies.

- **URL**: https://apify.com/flash\_scraper/instagram-profile-scraper.md
- **Developed by:** [Flash Scrape](https://apify.com/flash_scraper) (community)
- **Categories:** Social media, Lead generation
- **Stats:** 1 total users, 0 monthly users, 80.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Instagram Profile Scraper — Posts, Emails & Engagement

**Scrape any public Instagram profile without logging in.** Feed in usernames or profile URLs and get back the full profile record — bio, category, follower and following counts, verification and business flags, **business email, phone and address**, every link in the bio, the most recent **posts and reels** with likes, comments, captions, hashtags and tagged users — plus an **engagement rate** computed from those posts. Optionally let it **discover related accounts** and expand a single seed handle into a whole list.

No API key. No session cookie. No login.

***

### Realistic throughput (measured 2026-08-09)

Instagram rate-limits shared proxy pools hard. Measured on Apify RESIDENTIAL, two
consecutive 5-handle runs delivered **2 of 5 profiles each time** — the first handles
succeed, then every remaining exit IP is refused and the run stops rather than burn
your budget. Requests are HTTP/2 (Instagram answers **429 to every HTTP/1.1 request**,
which is why this Actor pins `httpx[http2]`).

**What that means for you:** ask for a handful of handles per run rather than fifty, and
expect partial delivery. You are only charged for profiles that actually return data —
a refused handle costs you nothing. For dependable high volume you need a dedicated
residential proxy rather than a shared pool.

### What you get per profile

| Group | Fields |
|---|---|
| Identity | `username`, `full_name`, `user_id`, `fbid`, `profile_url`, `profile_pic_url`, `pronouns` |
| Audience | `followers`, `following`, `posts_count`, `videos_count`, `highlight_reel_count` |
| Status | `is_verified`, `is_private`, `is_business_account`, `is_professional_account`, `category`, `is_joined_recently` |
| **Contact** | `email`, `business_email`, `bio_emails`, `business_phone`, `business_contact_method`, `business_address`, `external_url`, `bio_links`, `links_count` |
| Engagement | `avg_likes`, `median_likes`, `avg_comments`, `engagement_rate_pct`, `posts_per_week`, `last_post_at`, `days_since_last_post`, `posts_sampled` |
| Content | `recent_posts[]`, `recent_reels[]` — each with caption, hashtags, mentions, likes, comments, view count, timestamp, image URL, dimensions, tagged users, location |
| Discovery | `related_profiles[]`, `related_profiles_count` |

`bio_links` and `external_url` are **unwrapped** — Instagram serves outbound links behind an `l.instagram.com` redirect, and this Actor resolves them to the real destination so you get `https://www.nasa.gov/`, not a tracking URL.

#### Example output

A real row (trimmed — the full record has every field in the table above):

```json
{
  "username": "nasa",
  "full_name": "NASA",
  "biography": "Making the seemingly impossible, possible. ✨",
  "followers": 104251597,
  "following": 92,
  "posts_count": 4865,
  "is_verified": true,
  "is_business_account": true,
  "is_private": false,
  "email": null,
  "business_phone": null,
  "external_url": "https://www.nasa.gov/",
  "links_count": 5,
  "posts_sampled": 12,
  "avg_likes": 148632.4,
  "engagement_rate_pct": 0.1461,
  "posts_per_week": 6.21,
  "days_since_last_post": 1,
  "related_profiles_count": 49
}
```

`email` is `null` here because NASA has not published a public contact email — the field is only populated when the account actually exposes one. See the honesty note below.

### Discover accounts from one seed handle

Instagram returns up to ~49 **related profiles** for each account it serves. Turn on
`discoverRelated` and the Actor follows them, so one input handle can yield a whole niche:

```json
{
  "usernames": ["nasa"],
  "discoverRelated": true,
  "maxRelatedPerProfile": 10,
  "maxProfiles": 200,
  "onlyBusinessAccounts": true,
  "onlyWithContact": true
}
```

That crawls outward from `nasa` and keeps only business accounts that actually publish a contact — a lead list built from a single starting point.

### Input

| Field | Type | Notes |
|---|---|---|
| `usernames` | array | Handles, `@handles`, or profile URLs, mixed freely |
| `discoverRelated` | boolean | Follow each profile's related accounts |
| `maxRelatedPerProfile` | integer | How many related accounts to queue per profile (1–49) |
| `maxProfiles` | integer | Hard cap and the stop condition for discovery |
| `minFollowers` / `maxFollowers` | integer | `0` = no filter; use `maxFollowers` to target micro-influencers |
| `onlyVerified` | boolean | Verified accounts only |
| `onlyBusinessAccounts` | boolean | Business/professional accounts only — these are the ones that can expose contact details |
| `onlyWithContact` | boolean | Keep only rows with an email or phone |
| `onlyWithLink` | boolean | Keep only rows with a link in bio |
| `skipPrivate` | boolean | Drop private accounts |
| `alsoEmitPosts` | boolean | Also emit a flat row per post (`record_type: "post"`) |
| `maxPostsPerProfile` | integer | `0` = keep all returned |
| `delaySeconds` | string | Pause between profiles; lower = higher throttle risk |
| `proxyConfiguration` | object | Strongly recommended — see below |

### Honest limits — please read before buying

This Actor uses Instagram's **public** profile endpoint, which is the only one that answers an anonymous client. That sets real boundaries, and they are worth knowing up front rather than discovering mid-run:

- **About 12 recent posts + 12 recent reels per profile.** There is no way to page deeper without an account, so `maxPostsPerProfile` can only trim that set, never extend it. `posts_count` still reports the true lifetime total.
- **`engagement_rate_pct` is computed from those sampled posts**, not the full history. Every row reports `posts_sampled` so you can see the denominator.
- **No hashtag feeds, location feeds, search, follower/following lists, or stories.** Those endpoints reject anonymous requests (HTTP 302/401). This Actor will never ask you for your Instagram password or session cookie to get around that.
- **Contact fields depend on the account.** Only business/professional accounts can publish an email, phone or address; personal accounts have none, and the fields are `null` rather than missing. `bio_emails` catches addresses written into bio text. Use `onlyWithContact` if you only want rows that have one.
- **Private accounts** return name, category and counts, but never posts.
- **You need a residential proxy. This is the big one.** Instagram blocks shared cloud/datacenter IP ranges outright — measured here, six consecutive Apify datacenter IPs were all refused inside 1.5 seconds, and the platform's own egress IP is refused too. Set `proxyConfiguration` to the **RESIDENTIAL** group (the default) or supply your own residential proxy. The Actor rotates to a fresh IP on every throttle, and when it runs out it **stops and tells you exactly how many handles went unscraped** — it will never hand back a short list that looks complete.
- **A few accounts hit a bug on Instagram's side** (HTTP 400, a deleted business-category asset). That is reported per profile as an upstream error rather than as "profile not found", because they are different problems with different fixes.

### Use cases

- **Influencer and creator research** — filter by follower band, engagement rate and posting cadence rather than follower count alone.
- **Lead generation** — business accounts with a published email or phone, expanded via related-profile discovery.
- **Competitor and brand monitoring** — track posting frequency, average likes and recent captions over time.
- **Market and niche mapping** — start from one account and crawl outward to map who Instagram considers adjacent.

### Pricing

Pay per result: you are charged per **profile** delivered, and per **post row** only if you turn `alsoEmitPosts` on. Profiles removed by your filters, not found, or blocked upstream are **not** charged.

### FAQ

**Do I need an Instagram account or cookies?** No. If any tool asks you for your Instagram password or session cookie, that is a risk to your account — this one never does.

**Can it scrape private profiles?** It returns their public record (name, category, counts). Their posts are not public and are not accessible.

**Why is `email` empty for a big brand?** Because that account has not published one. Most large brands route contact through their website — `external_url` and `bio_links` are the useful fields there.

**Can I get more than 12 posts?** Not without an authenticated account, which this Actor deliberately does not use.

# Actor input Schema

## `usernames` (type: `array`):

Handles or profile URLs, in any mix: 'nasa', '@nasa', 'https://www.instagram.com/nasa/'. Private accounts return their public profile record but no posts.

## `discoverRelated` (type: `boolean`):

Instagram returns up to ~49 related accounts per profile. Turn this on to follow them and find new accounts from a single seed handle - it keeps going until 'Max profiles' is reached.

## `maxRelatedPerProfile` (type: `integer`):

How many of each profile's related accounts to add to the queue. Only used when discovery is on.

## `maxProfiles` (type: `integer`):

Hard cap on profiles scraped in one run - the stop condition for discovery.

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

0 = no filter.

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

0 = no filter. Use it to target micro-influencers instead of megastars.

## `onlyVerified` (type: `boolean`):

Keep only accounts with Instagram's blue verification badge.

## `onlyBusinessAccounts` (type: `boolean`):

Business accounts are the ones that can expose a public email, phone and address.

## `onlyWithContact` (type: `boolean`):

Keeps only rows carrying a business email/phone or an email found in the bio text.

## `onlyWithLink` (type: `boolean`):

Keep only profiles that have at least one link in their bio.

## `skipPrivate` (type: `boolean`):

Private profiles still return name, category and follower counts, but never posts.

## `alsoEmitPosts` (type: `boolean`):

Every profile row already nests its posts and reels. Turn this on to ALSO get a flat row per post (marked record\_type: post) for spreadsheet analysis. Note: post rows are charged too.

## `maxPostsPerProfile` (type: `integer`):

0 = keep all of them. Instagram's public profile endpoint returns about 12 recent posts plus 12 recent reels; there is no way to page further without an account, so this only trims, never extends.

## `delaySeconds` (type: `string`):

Seconds to pause between profiles. Instagram throttles bursts hard, so lowering this raises the chance of a 'please wait' stop mid-run.

## `proxyConfiguration` (type: `object`):

REQUIRED in practice. Instagram blocks shared cloud/datacenter IPs outright - verified: 6 consecutive Apify datacenter IPs were all refused within 1.5 seconds. Use the RESIDENTIAL group, or your own residential proxy. The Actor rotates to a fresh IP automatically each time Instagram throttles it.

## Actor input object example

```json
{
  "usernames": [
    "nasa",
    "nike"
  ],
  "discoverRelated": false,
  "maxRelatedPerProfile": 10,
  "maxProfiles": 50,
  "minFollowers": 0,
  "maxFollowers": 0,
  "onlyVerified": false,
  "onlyBusinessAccounts": false,
  "onlyWithContact": false,
  "onlyWithLink": false,
  "skipPrivate": false,
  "alsoEmitPosts": false,
  "maxPostsPerProfile": 0,
  "delaySeconds": "1.0",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# 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 = {
    "usernames": [
        "nasa",
        "nike"
    ],
    "discoverRelated": false,
    "maxRelatedPerProfile": 10,
    "maxProfiles": 50,
    "minFollowers": 0,
    "maxFollowers": 0,
    "onlyVerified": false,
    "onlyBusinessAccounts": false,
    "onlyWithContact": false,
    "onlyWithLink": false,
    "skipPrivate": false,
    "alsoEmitPosts": false,
    "maxPostsPerProfile": 0,
    "delaySeconds": "1.0",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("flash_scraper/instagram-profile-scraper").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 = {
    "usernames": [
        "nasa",
        "nike",
    ],
    "discoverRelated": False,
    "maxRelatedPerProfile": 10,
    "maxProfiles": 50,
    "minFollowers": 0,
    "maxFollowers": 0,
    "onlyVerified": False,
    "onlyBusinessAccounts": False,
    "onlyWithContact": False,
    "onlyWithLink": False,
    "skipPrivate": False,
    "alsoEmitPosts": False,
    "maxPostsPerProfile": 0,
    "delaySeconds": "1.0",
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("flash_scraper/instagram-profile-scraper").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 '{
  "usernames": [
    "nasa",
    "nike"
  ],
  "discoverRelated": false,
  "maxRelatedPerProfile": 10,
  "maxProfiles": 50,
  "minFollowers": 0,
  "maxFollowers": 0,
  "onlyVerified": false,
  "onlyBusinessAccounts": false,
  "onlyWithContact": false,
  "onlyWithLink": false,
  "skipPrivate": false,
  "alsoEmitPosts": false,
  "maxPostsPerProfile": 0,
  "delaySeconds": "1.0",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call flash_scraper/instagram-profile-scraper --silent --output-dataset

```

## MCP server setup

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

```

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/IgO7ZiOP7aVb3YcLf/builds/WmRrSgpqeCUmoeAFt/openapi.json
