# Instagram Profile Scraper (`coregent/instagram-profile-scraper`) Actor

Scrape public Instagram profiles from usernames, @handles or profile URLs. Extract bio, follower/following counts, posts count, profile picture, verified/private status, category, bio links, business email, phone and other public profile metadata. Export clean JSON, CSV or Excel.

- **URL**: https://apify.com/coregent/instagram-profile-scraper.md
- **Developed by:** [Delowar Munna](https://apify.com/coregent) (community)
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 1,000 profile results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Scraper

![Instagram Profile Scraper](https://raw.githubusercontent.com/coregentdevspace/instagram-profile-scraper-assets/main/thumbnail-instagram-profile-scraper.png)

Scrape public Instagram profiles from usernames, @handles or profile URLs. Extract bio, follower/following counts, posts count, profile picture, verified/private status, category, bio links, business email, phone and other public profile metadata. Export clean JSON, CSV or Excel.

### What this actor does

**Instagram Profile Scraper** takes a list of Instagram handles or profile URLs and returns **one clean, structured row per profile**:

- 👤 **Identity**: username, full name, stable numeric user ID, profile URL, HD profile picture, bio, pronouns
- 📊 **Counts**: followers, following, posts, highlight reels
- ✅ **Flags**: verified, private, business account, professional (creator) account, has Reels, Threads badge, recently joined
- 🏷️ **Category**: the business/creator category shown on the profile
- 🔗 **Links**: the website field **plus every bio link**, not just the first one
- 📇 **Public business contact**: business email, phone, address and preferred contact method, but only when Instagram itself shows them publicly
- 🧭 **Related profiles**: Instagram-suggested similar accounts, when Instagram exposes them
- 🖼️ **Optional recent-posts preview**: the latest few posts (URL, type, caption, timestamp, likes, comments, thumbnail)

> **Field availability:** Instagram currently throttles anonymous traffic and errors on many business profiles. Those profiles are served by a fallback source, which returns identity, counts, flags, category, bio and links but not the posts count, highlight count, related profiles, recent posts or public email/phone. On a heavily throttled run, most rows look like the [fallback sample below](#profile-served-by-the-fallback-source-sample-record).

It is a **profile metadata scraper**, not a posts or Reels scraper. It's built for creator enrichment, lead enrichment, competitor tracking and as the profile layer in larger Instagram pipelines.

### How it works

1. **Normalize inputs.** `nasa`, `@nasa`, `instagram.com/nasa`, `m.instagram.com/nasa/?igsh=…` all become `nasa`. Tracking parameters are stripped and duplicate aliases are removed.
2. **Fetch.** Each profile comes from Instagram's public web profile endpoint over plain HTTP. No browser, no login.
3. **Fall back instead of waiting.** A rate limit or login wall gets one retry on a fresh session/IP. If Instagram still refuses, the profile is fetched from an alternative source. When Instagram refuses many requests in a row, the run sends profiles to the alternative source first and keeps checking whether Instagram has recovered. Profiles that don't exist are never retried.
4. **Export.** One row per profile. Inputs that can't be resolved get a free **status row**, so every input can be matched to an output.

**Speed:** 100 profiles in about 70 seconds at the default concurrency of 10 (measured on the Apify platform). Raise `maxConcurrency` (up to 20) for larger lists.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `profiles` | `string[]` | required | Usernames, @handles, profile URLs or numeric user IDs (up to 1,000 per run) |
| `includeBusinessContact` | bool | `true` | Public business email / phone / address shown by Instagram |
| `includeBioLinks` | bool | `true` | All public bio links |
| `includeRelatedProfiles` | bool | `true` | Instagram-suggested related accounts (listed, not scraped) |
| `includeRecentPosts` | bool | `false` | Lightweight preview of the latest posts |
| `maxRecentPosts` | int | `3` | Posts per profile in the preview (1–12) |
| `proxyConfiguration` | object | Apify Proxy on | Proxy settings |
| `maxConcurrency` | int | `10` | Profiles fetched in parallel |
| `maxRetries` | int | `4` | Retries per profile on rate limits / login walls / temporary errors |
| `debugMode` | bool | `false` | Verbose logging |

#### Example 1: Enrich a list of handles

```json
{
  "profiles": ["nasa", "@nike", "https://www.instagram.com/instagram/"]
}
```

#### Example 2: Lead enrichment with a recent-posts preview

```json
{
  "profiles": [
    "https://www.instagram.com/glossier/",
    "https://www.instagram.com/allbirds/",
    "@gymshark"
  ],
  "includeBusinessContact": true,
  "includeBioLinks": true,
  "includeRelatedProfiles": false,
  "includeRecentPosts": true,
  "maxRecentPosts": 5
}
```

#### Example 3: Bulk brand and creator lookup (used for the screenshot and samples below)

```json
{
  "profiles": [
    "natgeo", "nasa", "@nike", "@starbucks",
    "https://www.instagram.com/instagram/", "https://www.instagram.com/spotify/",
    "netflix", "cristiano", "@therock", "taylorswift",
    "https://www.instagram.com/bbcnews/", "nytimes", "@redbull", "gopro",
    "airbnb", "https://www.instagram.com/lego/", "apple", "google", "@unicef", "time",
    "this_profile_does_not_exist_9x",
    "https://www.instagram.com/p/C0xAbCdEfGh/"
  ],
  "includeBusinessContact": true,
  "includeBioLinks": true,
  "includeRelatedProfiles": true,
  "includeRecentPosts": false
}
```

#### Example 4: Mixed valid and invalid inputs

Invalid and nonexistent entries don't stop the run; each gets its own free status row.

```json
{
  "profiles": [
    "nasa",
    "@nike",
    "this_profile_does_not_exist_9x",
    "https://www.instagram.com/p/C0xAbCdEfGh/"
  ]
}
```

### Output

One row per profile. Unavailable values are `null`, never guessed. Every row has the same columns, so CSV/Excel exports stay stable. The dataset has three tabs: **Profiles**, **Business & contact** and **Status & errors**.

![Profiles table view](https://raw.githubusercontent.com/coregentdevspace/instagram-profile-scraper-assets/main/instagram-profile-scraper-output-profiles-table-view.png)

#### Profiles tab: sample record

```json
{
  "profilePicUrl": "https://scontent-lga3-1.cdninstagram.com/v/t51.2885-19/29090066_159271188110124_1152068159029641216_n.jpg?stp=dst-jpg_s320x320_tt6",
  "username": "nasa",
  "fullName": "NASA",
  "profileUrl": "https://www.instagram.com/nasa/",
  "followersCount": 104267097,
  "followingCount": 90,
  "postsCount": 4955,
  "isVerified": true,
  "isPrivate": false,
  "isBusinessAccount": true,
  "categoryName": "Government Agencies",
  "biography": "Making the seemingly impossible, possible. ✨",
  "externalUrl": "https://www.nasa.gov/",
  "highlightReelCount": 6,
  "userId": "528817151",
  "status": "SUCCESS",
  "scrapedAt": "2026-10-08T12:13:29.284Z"
}
```

#### Business & contact tab: sample record

```json
{
  "username": "nike",
  "fullName": "Nike",
  "profileUrl": "https://www.instagram.com/nike/",
  "isBusinessAccount": true,
  "isProfessionalAccount": true,
  "categoryName": null,
  "businessEmail": null,
  "businessPhone": null,
  "businessAddress": {
    "street": "One Bowerman Dr",
    "zipCode": "97005",
    "cityName": "Beaverton, Oregon",
    "cityId": "108410602520455",
    "regionName": null,
    "countryCode": null,
    "latitude": 45.5076448,
    "longitude": -122.8269159
  },
  "businessContactMethod": "CALL",
  "externalUrl": "http://empli.fi/nike",
  "bioLinks": [
    { "title": null, "url": "http://empli.fi/nike", "linkType": "external" }
  ],
  "followersCount": 291032089
}
```

#### Status & errors tab: sample records

Every input gets a row, so you can reconcile inputs with outputs. These rows are free. A deleted or renamed account is returned as `PROFILE_NOT_FOUND`, even when Instagram was throttling the request and the alternative source confirmed it:

```json
{
  "input": "justinbieber",
  "status": "PROFILE_NOT_FOUND",
  "errorMessage": "Instagram rate-limited the request; the fallback source reports this account does not exist (This account does not exist.)",
  "username": "justinbieber",
  "userId": null,
  "profileUrl": "https://www.instagram.com/justinbieber/",
  "scrapedAt": "2026-10-08T16:45:56.857Z"
}
```

```json
{
  "input": "https://www.instagram.com/p/C0xAbCdEfGh/",
  "status": "INVALID_INPUT",
  "errorMessage": "Post/Reel URL — provide the profile URL or username instead",
  "username": null,
  "userId": null,
  "profileUrl": null,
  "scrapedAt": "2026-10-08T12:12:45.218Z"
}
```

#### Profile served by the fallback source: sample record

Instagram's own profile endpoint currently errors for many business accounts and throttles heavy anonymous traffic, so these profiles come from an alternative source. Identity, follower counts, flags, category, bio and links are returned. `postsCount`, `highlightReelCount`, related profiles, the recent-posts preview and public email/phone aren't available for them. These rows are `SUCCESS` and charged like any other profile.

```json
{
  "status": "SUCCESS",
  "errorMessage": null,
  "username": "gopro",
  "fullName": "GoPro",
  "userId": "28902942",
  "profileUrl": "https://www.instagram.com/gopro/",
  "profilePicUrl": "https://scontent-mad1-1.cdninstagram.com/v/t51.2885-19/449069728_1006900254437011_2641997517452774766_n.jpg",
  "biography": "We help the world to capture and share itself in immersive and exciting ways.\n#GoProMISSION1 Series👇",
  "externalUrl": "http://gopro.com/MISSION1",
  "bioLinks": [
    { "title": null, "url": "http://gopro.com/MISSION1", "linkType": "external" }
  ],
  "followersCount": 20017422,
  "followingCount": 240,
  "postsCount": null,
  "isVerified": true,
  "isPrivate": false,
  "isBusinessAccount": true,
  "isProfessionalAccount": true,
  "categoryName": "Product/service",
  "pronouns": null,
  "businessEmail": null,
  "businessPhone": null,
  "businessAddress": null,
  "businessContactMethod": null,
  "highlightReelCount": null,
  "hasReels": true,
  "hasChannel": null,
  "facebookId": "17841401168260451",
  "metaEntityId": null,
  "hasThreadsProfile": false,
  "isJoinedRecently": null,
  "relatedProfiles": [],
  "recentPosts": null,
  "input": "gopro",
  "scrapedAt": "2026-10-08T12:13:19.318Z"
}
```

#### Full record (all fields)

`bioLinks` and `relatedProfiles` are trimmed to two entries and image URLs are shortened; the real row for this profile has 5 bio links and 50 related profiles.

```json
{
  "status": "SUCCESS",
  "errorMessage": null,
  "username": "spotify",
  "fullName": "Spotify",
  "userId": "224223453",
  "profileUrl": "https://www.instagram.com/spotify/",
  "profilePicUrl": "https://scontent-ord5-1.cdninstagram.com/v/t51.2885-19/445718249_461892682885950_4604984260220263282_n.jpg?stp=dst-jpg_s320x320_tt6",
  "biography": "the #VMAs may be over, but we’re replaying the performances on Spotify 💚",
  "externalUrl": "https://open.spotify.com/episode/5oG3OBikHqGgz7WkzqOdZp?si=FjkBVeW3TqSZkI2DnqD3fQ",
  "bioLinks": [
    { "title": "Billions Club Live with Olivia Rodrigo: A Concert Film", "url": "https://open.spotify.com/episode/5oG3OBikHqGgz7WkzqOdZp?si=FjkBVeW3TqSZkI2DnqD3fQ", "linkType": "external" },
    { "title": "Fitness with Spotify", "url": "https://open.spotify.com/genre/0JQ5DAqbMKFJ6dHNHTv6Mx", "linkType": "external" }
  ],
  "followersCount": 14572641,
  "followingCount": 2344,
  "postsCount": 8699,
  "isVerified": true,
  "isPrivate": false,
  "isBusinessAccount": true,
  "isProfessionalAccount": true,
  "categoryName": "Personal Goods & General Merchandise Stores",
  "pronouns": null,
  "businessEmail": null,
  "businessPhone": null,
  "businessAddress": null,
  "businessContactMethod": null,
  "highlightReelCount": 5,
  "hasReels": true,
  "hasChannel": false,
  "facebookId": "17841400943593503",
  "metaEntityId": "105569130841322",
  "hasThreadsProfile": true,
  "isJoinedRecently": false,
  "relatedProfiles": [
    { "id": "20698621", "username": "iheartradio", "fullName": "iHeartRadio", "isVerified": true, "isPrivate": false, "profilePicUrl": "https://scontent-ord5-1.cdninstagram.com/v/t51.82787-19/564929969_18543059638026622_5952893746579818399_n.jpg" },
    { "id": "1156947332", "username": "amazonmusic", "fullName": "Amazon Music", "isVerified": true, "isPrivate": false, "profilePicUrl": "https://scontent-ord5-1.cdninstagram.com/v/t51.2885-19/464314201_916142467235385_4291848297418975667_n.jpg" }
  ],
  "recentPosts": null,
  "input": "https://www.instagram.com/spotify/",
  "scrapedAt": "2026-10-08T12:13:23.804Z"
}
```

#### Status values

Every row has a `status`. Only `SUCCESS` rows are charged.

| Status | Meaning |
|---|---|
| `SUCCESS` | Profile returned (private accounts included: their public metadata is still a result) |
| `PARTIAL_RESULT` | Profile found, but core fields (user ID, follower or following count) are missing. Delivered free |
| `INVALID_INPUT` | Not a valid username / profile URL (e.g. a post or Reel URL) |
| `PROFILE_NOT_FOUND` | The username or ID does not exist (also when Instagram was throttling and the fallback source confirmed the account doesn't exist) |
| `PROFILE_UNAVAILABLE` | Instagram returned an error for this profile instead of its data (e.g. restricted, or an Instagram-side error) |
| `RATE_LIMITED` / `LOGIN_WALL` / `BLOCKED` | Instagram throttled or walled this profile and the fallback source couldn't return it either |
| `TEMPORARY_INSTAGRAM_ERROR` / `PROVIDER_ERROR` | Instagram or network error persisted through retries |

#### Recent posts preview fields

`postId`, `shortCode`, `postUrl`, `type` (`Image` / `Video` / `Sidecar` / `Reel`), `caption`, `timestamp`, `likesCount`, `commentsCount`, `thumbnailUrl`.

#### Run summary

Each run writes a `SUMMARY` record to the key-value store with status counts, charged profiles, retries, profiles/minute, how many profiles came from the fallback source (and why), and availability rates (private, business, email, phone, bio links, related profiles).

### Pricing

Pay per event, **per successfully returned unique profile**. You are not charged for invalid inputs, duplicate inputs, profiles that don't exist, partial results or error rows. There is no start fee. The per-profile rate can differ by Apify plan, so see the **Pricing** tab for the current rate on yours. This README deliberately doesn't quote figures.

The actor respects the spending limit you set for a run: when it's reached, the run stops cleanly and no further profiles are fetched.

#### 🚦 Proxy policy

Use **Apify Datacenter** proxy (the default) or **no proxy** for normal runs. Both are supported: when Instagram throttles the proxy's IPs, profiles are served by the fallback source instead.

**Apify Residential proxy is not supported.** The actor fails at startup if `apifyProxyGroups` includes `RESIDENTIAL`. In pay-per-event actors, residential bandwidth is billed per GB to the developer, not the run user, so a single bandwidth-heavy run could cost more than the per-result revenue.

If you need residential routing, supply your own residential provider in the proxy editor's **Custom proxy URLs** field. That traffic goes through your provider, not Apify, and is unaffected:

```
http://user:pass@proxy.iproyal.com:12321
http://user:pass@proxy.brightdata.com:22225
http://user:pass@proxy.oxylabs.io:7777
```

### Limitations

- **Public data only.** Private accounts return the metadata Instagram shows publicly (name, picture, counts, bio). Their posts are never accessed.
- **Numeric user IDs are best-effort.** Instagram often requires login to look up an ID, so these inputs can end as `LOGIN_WALL`. Usernames and URLs are the reliable path.
- **Related profiles** come from Instagram's own suggestions, typically up to ~50 per profile. Some profiles return none.
- **Some profiles are served by a fallback source.** Instagram's own profile endpoint currently errors for many business and creator accounts, and it throttles anonymous traffic on busy runs. In both cases the actor retrieves the profile from an alternative source, returning the same identity, follower/following, verified/private/business flags, category, bio and link fields, but **without** public email/phone, the posts count (`postsCount` is `null`), highlight count, related profiles or the recent-posts preview. If the fallback source reports that the account doesn't exist, the row is a free `PROFILE_NOT_FOUND`; if it fails for another reason, a free `PROFILE_UNAVAILABLE` or `RATE_LIMITED` row. On a heavily throttled run, most rows can come from the fallback source.
- **Business contact fields** are Instagram's own public fields. Large brands rarely show a public email or phone, so expect these mostly from smaller businesses and creators. The actor doesn't search the web for emails or extract hidden contact details.
- **Recent posts** are a short preview from the profile response, capped at 12. Use a dedicated posts scraper for full content.
- Instagram limits anonymous traffic per IP. Throttled profiles go to the fallback source after one retry; a profile neither source can return ends as `RATE_LIMITED`. Those rows are free.

### Legal and privacy

This is an unofficial tool and is not affiliated with, endorsed by or sponsored by Instagram or Meta. It collects only publicly available profile metadata and doesn't log in, collect credentials or bypass private-account controls.

Public business contact fields can contain personal information. You are responsible for processing the data lawfully (e.g. GDPR/CCPA). Collect only what you need, and don't use profile data for unlawful discrimination, harassment or sensitive profiling.

### Local development

```bash
cd actor
npm install
npm test       # unit tests (normalizer, parser, response classification)
npm start      # run with INPUT.json
```

# Actor input Schema

## `profiles` (type: `array`):

Usernames (natgeo), @handles (@natgeo), profile URLs (https://www.instagram.com/natgeo/) or numeric user IDs. Mix freely — inputs are normalized and duplicates are removed. Up to 1,000 profiles per run.

## `includeBusinessContact` (type: `boolean`):

Return the business email, phone and address that Instagram itself exposes publicly on the profile. No external email discovery is performed.

## `includeBioLinks` (type: `boolean`):

Return every public link in the profile bio (not just the single website field).

## `includeRelatedProfiles` (type: `boolean`):

Return Instagram-suggested related accounts when Instagram exposes them. Related profiles are listed only, never scraped recursively.

## `includeRecentPosts` (type: `boolean`):

Return a lightweight preview of the latest posts (URL, type, caption, timestamp, likes, comments, thumbnail) taken from the same profile response. Not a full posts scraper. Private accounts never return posts.

## `maxRecentPosts` (type: `integer`):

Number of recent posts to include per profile when the preview is enabled.

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

Apify Proxy configuration. Defaults to Apify Proxy (datacenter) enabled, which rotates IPs between sessions to avoid Instagram's per-IP limits.

## `maxConcurrency` (type: `integer`):

Maximum number of profiles fetched in parallel.

## `maxRetries` (type: `integer`):

How many times a profile is retried on a fresh session/IP after rate limits, login walls or temporary Instagram errors. A profile Instagram still throttles after the first retry is fetched from an alternative source instead. Not-found profiles are never retried.

## `debugMode` (type: `boolean`):

Enable verbose logging.

## Actor input object example

```json
{
  "profiles": [
    "natgeo",
    "@nike",
    "https://www.instagram.com/instagram/"
  ],
  "includeBusinessContact": true,
  "includeBioLinks": true,
  "includeRelatedProfiles": true,
  "includeRecentPosts": false,
  "maxRecentPosts": 3,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "maxConcurrency": 10,
  "maxRetries": 4,
  "debugMode": false
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `businessContacts` (type: `string`):

No description

## `errors` (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 = {
    "profiles": [
        "natgeo",
        "@nike",
        "https://www.instagram.com/instagram/"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("coregent/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 = {
    "profiles": [
        "natgeo",
        "@nike",
        "https://www.instagram.com/instagram/",
    ],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("coregent/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 '{
  "profiles": [
    "natgeo",
    "@nike",
    "https://www.instagram.com/instagram/"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call coregent/instagram-profile-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,coregent/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/7lPr42K2JmeKdl9Yh/builds/91tmkoucZtMGwLVuH/openapi.json
