# Instagram Profile Scraper - $0.55 per 1,000 Profiles (`dami_studio/instagram-profile-scraper-v1`) Actor

The cheapest Instagram profile scraper on the market: $0.55 per 1,000 profiles. Handles in, one row per profile out - full bio, follower/following/post counts, verified and private flags, business category, bio links, HD avatar, related profiles and optional latest posts. No login.

- **URL**: https://apify.com/dami\_studio/instagram-profile-scraper-v1.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** Social media, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.55 / 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/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

Give it Instagram handles, get one row per public profile back: full biography, follower /
following / post counts, verified and private flags, professional-account and business-category
fields, every bio link, the HD avatar, the account's related-profile suggestions, and — if you tick
the box — its 12 most recent posts.

No Instagram account. No cookies to paste. No session token. No proxy of your own. Type handles,
get rows.

### Price

**$0.55 per 1,000 profiles**, plus $0.0000125 to start a run.

That makes it the cheapest Instagram profile scraper on the market. The rate is also **flat**: the
same $0.55 on the free plan as on any paid plan, with no volume tiers, no minimum spend and no plan
gates, so the number below is what you pay from your very first run.

| Profiles | Cost here |
|---|---|
| 100 | $0.055 |
| 1,000 | $0.55 |
| 10,000 | $5.50 |
| 100,000 | $55.00 |

Plus the $0.0000125 run-start fee, once per run, whatever the size of the run.

#### What is actually billed

- **`profile-scraped` — $0.00055.** Charged once, immediately after a real profile row is written
  to the dataset. One charge per profile, never per post: turning on "include the latest posts"
  adds up to 12 posts to the row and costs nothing extra.
- **`apify-actor-start` — $0.0000125 per GB of run memory.** The Actor's default memory is 1 GB, so
  that is $0.0000125 per run.
- **Free: everything else.** The sample row a run with no input returns is not charged. Diagnostic
  rows (profile not found, blocked, bad input) are not charged. A run that finds nothing costs the
  start fee alone.

#### There are no add-on events

`profile-scraped` is the only result event this Actor has. Switching on `includeLatestPosts` adds up
to 12 posts to the row and costs nothing extra; neither do `relatedProfiles`, the HD avatar, the
full `externalUrls` array or the e-mail/phone extraction — they are all part of the same profile
row. Nothing is metered per page, per request or per proxy gigabyte, so the table above plus the
fractional start fee is the entire bill. A run you cancel mid-way is billed only for the rows it had
already written.

### Input

| Field | What it does |
|---|---|
| `usernames` | Instagram handles. `nasa`, `@nasa` and `https://www.instagram.com/nasa/` are all accepted. |
| `profileUrls` | Same thing, if you would rather paste URLs. Merged with `usernames`, de-duplicated. |
| `includeLatestPosts` | Off by default. On, each row gets `latestPosts` — up to 12 recent posts with caption, hashtags, mentions, media URLs and timestamp. |
| `resultsLimit` | Stop after this many profiles. Leave empty to do every handle you supplied. Ceiling 1,000. |
| `maxConcurrency` | How many profiles to fetch at once. Default 8, ceiling 16. |
| `proxyConfiguration` | Leave empty. See "How it works" for why. |

```json
{
  "usernames": ["nasa", "natgeo", "@nike"],
  "profileUrls": ["https://www.instagram.com/spotify/"],
  "includeLatestPosts": true,
  "resultsLimit": 10
}
```

`usernames` is a `prefill`, not a `default` — running with genuinely empty input returns one
labelled sample row, uncharged, so you can see the row shape before spending anything.

### Output

A real row, trimmed for length (this is `nasa`, run 2026-08-15):

```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=https%3A%2F%2Fwww.nasa.gov%2F&e=AUA5zD4…",
  "externalUrls": [
    { "title": "NASA.gov Homepage", "url": "https://www.nasa.gov", "lynxUrl": "https://l.instagram.com/?u=…", "linkType": "external" },
    { "title": "NASA Internships", "url": "http://intern.nasa.gov", "lynxUrl": "https://l.instagram.com/?u=…", "linkType": "external" }
  ],
  "followersCount": 104395191,
  "followsCount": 92,
  "postsCount": 4882,
  "hasChannel": false,
  "highlightReelCount": 5,
  "igtvVideoCount": 171,
  "isBusinessAccount": true,
  "isProfessionalAccount": true,
  "joinedRecently": false,
  "businessCategoryName": "Government Agencies",
  "categoryName": null,
  "businessEmail": null,
  "businessPhoneNumber": null,
  "businessContactMethod": "CALL",
  "businessAddress": null,
  "private": false,
  "verified": true,
  "profilePicUrl": "https://scontent.cdninstagram.com/v/t51.2885-19/29090066_159271188110124_…",
  "profilePicUrlHD": "https://scontent.cdninstagram.com/v/t51.2885-19/29090066_159271188110124_…",
  "fbid": "17841401474538262",
  "relatedProfiles": [
    { "id": "1067259270", "username": "nasagoddard", "fullName": "NASA Goddard", "isVerified": true, "isPrivate": false, "profilePicUrl": "https://…" }
  ],
  "latestPosts": [
    {
      "id": "3963668990156895830",
      "type": "Video",
      "shortCode": "DcBx_I1iHZW",
      "url": "https://www.instagram.com/p/DcBx_I1iHZW/",
      "caption": "Check out the eclipse from 50,000 feet in the air!…",
      "hashtags": ["NASA", "Eclipse2026", "Aircraft"],
      "mentions": [],
      "likesCount": 187209,
      "commentsCount": 1234,
      "videoViewCount": 4026346,
      "displayUrl": "https://scontent.cdninstagram.com/v/t51.82787-15/774149093_…",
      "images": ["https://scontent.cdninstagram.com/v/t51.82787-15/774149093_…"],
      "alt": "Video by NASA on August 14, 2026…",
      "isVideo": true,
      "timestamp": "2026-08-14T18:02:41.000Z",
      "ownerUsername": "nasa",
      "ownerId": "528817151"
    }
  ],
  "emailsFound": [],
  "phonesFound": [],
  "bioHashtags": [],
  "bioMentions": [],
  "pronouns": [],
  "dataSource": "web_profile_info",
  "scrapedAt": "2026-08-15T02:31:07.882Z"
}
```

The field names follow the conventions used across Instagram profile Actors — `username`,
`fullName`, `followersCount`, `followsCount`, `postsCount`, `verified`, `private`,
`isBusinessAccount`, `businessCategoryName`, `externalUrl`, `externalUrls`, `profilePicUrl`,
`profilePicUrlHD`, `relatedProfiles`, `latestPosts`, `highlightReelCount`, `igtvVideoCount`,
`fbid`, `inputUrl` — so an existing pipeline can be pointed at this Actor without editing the code
that reads the rows. Everything extra (`emailsFound`, `phonesFound`, `bioHashtags`, `bioMentions`,
`businessEmail`, `businessPhoneNumber`, `businessAddress`, `isProfessionalAccount`, `dataSource`)
is appended after those.

#### Field coverage

Measured on a real 40-account run on the platform, 2026-08-15. 40 of 40 handles returned a row.

| Field | Present |
|---|---|
| `username`, `id`, `url`, `followersCount`, `verified`, `private` | 100% |
| `profilePicUrl`, `profilePicUrlHD`, `fbid`, `highlightReelCount` | 100% |
| `latestPosts` (with `includeLatestPosts` on) | 100% |
| `fullName` | 98% |
| `followsCount` | 95% |
| `isBusinessAccount`, `igtvVideoCount` | 93% |
| `externalUrl`, `externalUrls` | 90% |
| `biography` | 88% (the rest genuinely have an empty bio) |
| `postsCount` | 83% |
| `relatedProfiles` | 70% (Instagram returns none for some accounts) |
| `businessCategoryName` | 20% |
| `businessEmail`, `businessPhoneNumber` | 0% — see below |

Missing values are `null`. Nothing is ever guessed or interpolated.

### Limits

Read this part before you build on it.

- **`businessEmail` and `businessPhoneNumber` were null on every one of the 22 accounts tested**,
  large brands and small shops alike. Instagram no longer returns public contact details on the
  logged-out endpoints. The fields are still in the row, so the row shape stays stable for existing
  pipelines, but do not plan a lead-gen pipeline around them. What does work: any e-mail or phone number an account
  writes into its **bio text** is extracted into `emailsFound` and `phonesFound`.
- **`postsCount` is null on about 17% of accounts.** Instagram's primary profile endpoint currently
  returns HTTP 400 for a large minority of professional accounts (see "How it works"), and the
  fallback path can only recover the exact post total when the profile page states it exactly. When
  the page rounds it — "9.5M Posts" — the field is left `null` rather than reporting a rounded
  number as if it were a count. Follower and following counts are exact on every path.
- **Public profiles only.** A private account returns its bio, counts and avatar — that is all
  Instagram shows a logged-out visitor — with `private: true`. Its posts are not accessible and no
  attempt is made to get them.
- **`latestPosts` is up to 12 posts, never more.** This Actor reads profiles. It does not paginate
  a feed, and it does not scrape comments, followers, stories, tagged posts or hashtags.
- **Counts move.** A followers number is a reading at `scrapedAt`, not a stable identifier. Two runs
  a minute apart on a large account will differ by thousands.
- **A handle that does not exist returns an uncharged `NOT_FOUND` diagnostic row**, not a crash and
  not a charge.
- **1,000 profiles per run** (`resultsLimit` ceiling), and the run timeout is 5 minutes. 40
  profiles took 77 seconds at the default concurrency of 8, so a run covers a few hundred handles
  comfortably; for very large lists, split them across runs or raise `maxConcurrency`.

### FAQ

**Do I need an Instagram account, cookies or a session id?**
No. Nothing to log in to and nothing to paste. Every endpoint used here is one a logged-out visitor
hits.

**Do I need to buy proxies?**
No. Proxy rotation is built in and its cost is already inside the $0.55.

**What does 1,000 profiles cost?**
$0.55, plus the $0.0000125 start fee.

**Is this really the cheapest Instagram profile scraper?**
Yes — $0.55 per 1,000 profiles, and it is the same $0.55 on every plan rather than a headline rate
you only reach at high volume. The full billing mechanics are spelled out under "Price".

**Can I switch from another Instagram profile Actor without changing my code?**
Usually. Input field names follow the common conventions used across similar Actors — `usernames`
is the same field it is everywhere — and the output field names match too, so switching is normally
a drop-in change. An "about section" block (date joined, country) that some Actors offer has no
equivalent here; in the other direction, this Actor returns fields (`emailsFound`, `phonesFound`,
`bioHashtags`, `bioMentions`, `pronouns`, `dataSource`) that most do not.

**Does it return e-mail addresses?**
Only ones written into the bio text, in `emailsFound`. Instagram's public contact-e-mail field came
back empty on every account tested — see "Limits".

**How fast is it?**
40 profiles in 77 seconds on the platform at the default settings, half of them on the slower
fallback path. Accounts on the fast path take 1.5–2 seconds each.

**Can I export to CSV or Excel?**
Yes — Apify exports the dataset as JSON, CSV, Excel or XML, and the same data is available over the
REST API.

**What happens if I run it with no input?**
One labelled sample row, uncharged, and the run finishes as SUCCEEDED.

**Is this affiliated with Instagram or Meta?**
No. It reads public profile data. Check Instagram's terms and your own local rules before using the
output commercially.

### How it works

Instagram's logged-out profile API is metered **per IP address**, not per account, and it answers a
spent address with `401 "Please wait a few minutes before you try again"` — an HTTP status, not a
captcha. The fix is a large pool you can hop through rather than a fast pool you keep hitting, so
this Actor requests a **fresh Apify datacenter address for every attempt**. Datacenter addresses are
not billed per gigabyte, which matters because Instagram answers a profile request with 100–800 KB.
Residential proxy is deliberately not used: it is metered by the gigabyte, and at that response size
the bandwidth alone would cost more per row than a row is priced at. The cheap egress is the reason
this price is possible.

Plain Node `fetch` is refused from every address — Instagram is judging the TLS handshake, not the
IP. Requests go through `impit` with a genuine Chrome fingerprint instead, which returns HTTP 200
from the same addresses in the same second.

There is one more thing, and it is the reason this Actor returns rows for accounts where a
single-endpoint scraper returns nothing. As of 2026-08-14 Instagram's main profile endpoint answers **HTTP 400** for a large
minority of professional accounts:

```
{"message":"Asset asset://laser.provider/ig_business_category_subvertical has been deleted.
You cannot use this schema"}
```

That is a bug in Instagram's own schema, not a block: it is deterministic per account and it
reproduces from every address, so retrying and rotating cannot fix it. Measured across 20
well-known handles, **9 of 20 fail this way** — natgeo, netflix, adidas, beyoncé, starbucks, airbnb,
leomessi, shakira and hubspot among them. Any scraper that calls only that endpoint returns nothing
for exactly those accounts.

So this one falls down a ladder instead of failing:

1. `web_profile_info` with the ordinary web app id — the complete answer when it works.
2. The same endpoint with a different app id, which is not subject to the broken schema. Its
   descriptive fields are good; its follower/following and post counts are not, so they are dropped
   rather than reported.
3. The logged-out profile page, whose embedded payload carries the exact follower and following
   counts and the recent posts, with the post total read from the page's own meta description.
4. The feed-by-username route, used only to recover recent posts when rung 3 is rate limited.

Rungs 2, 3 and 4 are merged into one row and `dataSource` records which of them produced it. In the
40-account run above, 21 rows came from rung 1 and 19 needed the ladder — and all 40 returned.

# Actor input Schema

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

Instagram handles to scrape, one profile per row. With or without the @.

## `profileUrls` (type: `array`):

Paste full profile URLs instead of handles — https://www.instagram.com/nasa/ works too.

## `includeLatestPosts` (type: `boolean`):

Adds the account's most recent posts (up to 12) to each row as latestPosts. Costs nothing extra.

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

Stop after this many profiles. Leave empty to scrape every handle you supplied. Hard ceiling 1000.

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

How many profiles to fetch at once. 8 is the default and is also the cheapest setting - the run bills by wall-clock time, so finishing sooner costs less.

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

Leave empty. The Actor rotates Apify datacenter addresses by itself, which is what Instagram's per-address rate limit needs. Only set this if you want to force your own proxy servers.

## `notionConnector` (type: `string`):

Optional. Deliver the scraped profiles straight to your connected Notion workspace.

## `notionParentId` (type: `string`):

Optional Notion database/data-source id to create the pages in.

## Actor input object example

```json
{
  "usernames": [
    "nasa",
    "natgeo"
  ],
  "profileUrls": [
    "https://www.instagram.com/nike/"
  ],
  "resultsLimit": 10,
  "maxConcurrency": 8
}
```

# Actor output Schema

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

One row per Instagram profile in the default dataset. Runs with no input return a single uncharged sample row; failures return uncharged diagnostic rows.

# 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",
        "natgeo"
    ],
    "profileUrls": [
        "https://www.instagram.com/nike/"
    ],
    "resultsLimit": 10,
    "maxConcurrency": 8
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/instagram-profile-scraper-v1").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",
        "natgeo",
    ],
    "profileUrls": ["https://www.instagram.com/nike/"],
    "resultsLimit": 10,
    "maxConcurrency": 8,
}

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/instagram-profile-scraper-v1").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",
    "natgeo"
  ],
  "profileUrls": [
    "https://www.instagram.com/nike/"
  ],
  "resultsLimit": 10,
  "maxConcurrency": 8
}' |
apify call dami_studio/instagram-profile-scraper-v1 --silent --output-dataset

```

## MCP server setup

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

```

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/zMmbIIQqfTh7Dr7mv/builds/wGb523uVOOjS7K0jP/openapi.json
