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

Scrape public Instagram profiles - followers, bio, posts - without logging in.

- **URL**: https://apify.com/skillify/instagram-profile-scraper.md
- **Developed by:** [John Wu](https://apify.com/skillify) (community)
- **Categories:** Social media, Automation, Agents
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 profile scrapeds

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?

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

Turn a list of Instagram handles into a clean spreadsheet of followers, bios, categories and recent posts. No login, no Instagram account, no cookies — just paste usernames and press Start.

Export to **JSON, CSV, Excel, XML or HTML**, or pull results straight from the API.

***

### What you can do with it

#### Track competitor follower growth

Run this on a schedule with your competitors' handles and you get a dated row per profile per run. Point Google Sheets or Looker Studio at the dataset and you have a growth chart nobody had to maintain. The **Follower tracking** dataset view is already trimmed to exactly these columns.

#### Audit an influencer list before you pay anyone

Paste the handles from a media kit or an agency spreadsheet. In one run you can see who is actually verified, who quietly went private, who has 400k followers but posts twice a year, and whose "brand account" is a personal profile. `followersCount` next to `postsCount` and `latestPosts[].likesCount` tells you more in thirty seconds than a media kit does.

#### Qualify leads from a list of brand accounts

`isBusinessAccount`, `businessCategoryName` and `externalUrl` turn a list of handles into a segmented prospect list with a website to enrich against.

#### Find similar accounts

Every scraped profile carries `relatedProfiles` — the accounts Instagram itself suggests alongside it. Feed those back in as input and you can map a niche outward from a single seed account.

#### Archive your own brand presence

Snapshot bios, links and recent post performance across all your regional accounts on a weekly schedule, so you have a record of what changed and when.

***

### Input

Paste usernames or profile URLs. All of these work and mean the same thing:

```
nasa
@nasa
https://www.instagram.com/nasa/
```

```json
{
    "usernames": ["humansofny", "nasa", "https://www.instagram.com/natgeo/"],
    "resultsLimit": 12
}
```

| Field | Type | Default | What it does |
|---|---|---|---|
| `usernames` | array | — | Usernames or profile URLs to scrape. |
| `resultsLimit` | integer | `12` | Recent posts per profile. Instagram gives logged-out visitors at most 12. **Set to `0` for metadata only** — the fastest and cheapest option if you only need follower counts. |
| `proxyConfiguration` | object | residential | Leave as-is. See [Proxies](#proxies). |
| `maxConcurrency` | integer | `5` | Profiles fetched in parallel. |
| `maxRequestRetries` | integer | `5` | Retries per profile, each from a fresh IP. |
| `directUrls` | array | — | Alias for `usernames`, so input written for other Instagram Actors runs unchanged. |

### Output

One dataset item per input profile, always. Field names match the common Instagram profile-scraper format, so existing pipelines keep working.

```json
{
    "inputUrl": "https://www.instagram.com/nasa/",
    "id": "528817151",
    "username": "nasa",
    "url": "https://www.instagram.com/nasa/",
    "fullName": "NASA",
    "biography": "Making the seemingly impossible, possible. ✨",
    "externalUrl": "https://www.nasa.gov/",
    "externalUrlShimmed": "https://l.instagram.com/?u=...",
    "followersCount": 104422532,
    "followsCount": 92,
    "postsCount": 4888,
    "isBusinessAccount": true,
    "businessCategoryName": "Government Agencies",
    "verified": true,
    "private": false,
    "joinedRecently": false,
    "hasChannel": false,
    "highlightReelCount": 5,
    "igtvVideoCount": 0,
    "profilePicUrl": "https://scontent.cdninstagram.com/...",
    "profilePicUrlHD": "https://scontent.cdninstagram.com/...",
    "facebookPage": null,
    "fbid": "17841401474538262",
    "relatedProfiles": [
        {
            "id": "...",
            "username": "nasaearth",
            "full_name": "NASA Earth",
            "is_verified": true,
            "is_private": false,
            "profile_pic_url": "https://..."
        }
    ],
    "latestPosts": [
        {
            "id": "...",
            "type": "Image",
            "shortCode": "C...",
            "url": "https://www.instagram.com/p/C.../",
            "caption": "...",
            "hashtags": ["nasa"],
            "mentions": ["@nasaearth"],
            "likesCount": 481203,
            "commentsCount": 2210,
            "timestamp": "2026-08-19T14:02:11.000Z",
            "displayUrl": "https://...",
            "dimensionsWidth": 1080,
            "dimensionsHeight": 1350,
            "alt": "Photo by NASA on August 19, 2026.",
            "childPosts": []
        }
    ],
    "latestIgtvVideos": [],
    "dataSource": "api",
    "scrapedAt": "2026-08-23T10:41:07.912Z"
}
```

#### Profiles that don't work out still get a row

A run never dies on one bad handle, and you never have to diff your input against your output to find out what's missing. Failures come back as records:

```json
{
    "inputUrl": "https://www.instagram.com/thisdoesnotexist/",
    "username": "thisdoesnotexist",
    "error": "not_found",
    "errorDescription": "Profile does not exist",
    "scrapedAt": "2026-08-23T10:41:09.004Z"
}
```

`error` is one of `not_found`, `blocked`, `unavailable` or `invalid_input`. Filter the dataset on `error` to see just the problems.

**Private profiles are not errors.** Instagram publishes their metadata, so you get a normal record with `private: true`, real follower and post counts, and an empty `latestPosts`.

#### Two fields worth knowing about

- **`dataSource`** — `"api"` or `"html"`. Instagram has a long-standing server-side bug that makes its JSON endpoint fail for a large share of business accounts with a category. Rather than return nothing, this Actor falls back to reading the public profile page.
- **`partial`** — present and `true` only on `"html"` records. Those carry username, name, bio, exact follower and following counts, profile picture, verified and private flags, and the bio link. They cannot carry `latestPosts`, `relatedProfiles` or `businessCategoryName`, and `postsCount` is Instagram's own rounded figure. If you need a strict dataset, filter on `partial != true`.

### Proxies

Instagram blocks datacenter IPs almost immediately and rate-limits any single IP after roughly nine requests, then keeps that IP cold for over an hour. **Residential proxies are not optional** for runs of any size, and are configured by default. The Actor rotates to a fresh IP well before it reaches Instagram's ceiling, and retries blocked profiles on new IPs rather than dropping them.

If you scrape only a handful of profiles occasionally you can try datacenter proxies to save a little, but expect `blocked` records.

### No login, ever

This Actor only reads data that Instagram serves to logged-out visitors. It never signs in, never sends a cookie or session, and never asks you for Instagram credentials. Anyone asking for your Instagram password to run a scraper is asking for your account.

It also does not keep your results. Everything lands in your run's dataset, which belongs to your account — there is no cache, no shared database, no aggregation across runs.

### Notes and limits

- Instagram shows logged-out visitors at most **12 recent posts** per profile; `resultsLimit` above 12 is capped.
- **Numeric profile IDs are not accepted as input** — only usernames and profile URLs. Resolving an ID to a username requires an endpoint Instagram does not serve to logged-out visitors, and this Actor never logs in. An all-digit entry is treated as a username, because Instagram does allow all-digit usernames.
- `latestComments` is always `[]` — comments are not available logged-out.
- Follower counts move constantly. Two runs minutes apart will differ by thousands on large accounts; that is Instagram, not the scraper.
- A very small number of accounts report `followersCount: 0` from Instagram itself. That value is passed through as returned.

### Integrations

Results are available through the Apify API and the JavaScript and Python clients, and connect to Make, Zapier, n8n, Airbyte, Google Sheets, Slack and others through Apify integrations. Schedule runs from the Apify Console to build a time series without writing any glue.

# Actor input Schema

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

Instagram usernames or profile URLs to scrape. All three forms work: `nasa`, `@nasa`, or `https://www.instagram.com/nasa/`. Numeric profile IDs are not supported — resolving one needs an endpoint Instagram does not serve to logged-out visitors.

## `resultsLimit` (type: `integer`):

How many of the profile's most recent posts to include in `latestPosts`. Instagram returns at most 12 to logged-out visitors. Set to 0 for profile metadata only, which produces the smallest, cheapest results.

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

Instagram blocks datacenter IPs almost immediately and rate-limits any single IP after roughly 9 requests. Residential proxies are strongly recommended and are the default.

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

How many profiles to fetch in parallel. Higher values finish sooner but burn through proxy IPs faster. Leave at the default unless you are scraping thousands of profiles.

## `maxRequestRetries` (type: `integer`):

How many times to retry a profile on a block or network error, each time from a fresh IP.

## `directUrls` (type: `array`):

Accepted so that inputs written for other Instagram Actors work unchanged. Entries here are merged with `usernames`.

## Actor input object example

```json
{
  "usernames": [
    "humansofny"
  ],
  "resultsLimit": 12,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "maxConcurrency": 5,
  "maxRequestRetries": 5
}
```

# Actor output Schema

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

All records from this run, in JSON. One item per input, including any that failed.

## `profilesCsv` (type: `string`):

The same records as a CSV, ready for a spreadsheet.

## `runDetail` (type: `string`):

This run in Apify Console, including the log.

# 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": [
        "humansofny",
        "nasa"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("skillify/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": [
        "humansofny",
        "nasa",
    ],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("skillify/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": [
    "humansofny",
    "nasa"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call skillify/instagram-profile-scraper --silent --output-dataset

```

## MCP server setup

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