# Instagram Bio Scraper — Instagram Email, Phone & Profile Stats (`steadyfetch/instagram-profile-details-scraper`) Actor

Instagram bio scraper and full profile record for a handle or profile link: followers, following, posts, bio, category, and the email, phone and address the account publishes itself. Private accounts delivered too, and say so. From $3.00 per 1,000 profiles. An undeliverable account is never charged.

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

## Pricing

from $3.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.
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?

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 Bio Scraper — Instagram Email, Phone & Profile Stats

Paste Instagram handles or profile links and get the whole public record for each account: followers, following and post counts, bio and links, category, verification, pictures — and the **email, phone and address the account publishes on its own business profile**. One price for the whole row: there is no second charge for the contact fields. No login, no cookies, no proxy to configure. **A private account is delivered too** — its counts, bio and category are public even when its posts are not — and the row says so. An account we could not deliver is never charged, and nothing is charged for starting a run.

**Using an AI agent?** Pin this actor in Apify's MCP server with one link: `https://mcp.apify.com?tools=steadyfetch/instagram-profile-details-scraper`

- **Actor id:** `steadyfetch/instagram-profile-details-scraper`
- **Input:** `{ "usernames": ["nasa"] }` — the one field you have to set. A profile link or a numeric account id works in the same box.
- **Cap the bill:** set `maxTotalChargeUsd` on the run (a run option, not Actor input), e.g. `0.50` — the run stops when it reaches it.
- **Price:** from **$3.00 per 1,000 profiles** on Gold and above, $6.00 per 1,000 on the Apify free plan; the about-account panel is a flat $0.003 more, only when you ask for it. No start fee, and an account we could not deliver is never charged. Full table below.
- **How often it changes:** follower, following and post counts move every day on an active account; the bio, links and contact block change only when the account holder edits them, often not for months. Weekly or monthly is what most buyers want — it is the counts that go stale, not the contact fields.

### What you get

One row per account, with everything that account publishes at one price:

| Column | What it holds |
|---|---|
| `username` · `userId` · `profileUrl` · `fullName` | who the account is |
| `biography` · `bioLinks` · `externalUrl` · `pronouns` | the bio, the link-in-bio list and the website |
| `followerCount` · `followingCount` · `mediaCount` · `totalClipsCount` | the audience and how much they post |
| `isPrivate` · `isVerified` · `isBusiness` · `isProfessionalAccount` · `accountType` | what kind of account it is |
| `category` · `categoryId` · `accountBadges` | the label Instagram shows under the name |
| `publicEmail` · `publicPhoneNumber` · `publicPhoneCountryCode` · `contactPhoneNumber` | the contact details published on the profile |
| `businessContactMethod` · `addressStreet` · `cityName` · `zip` · `latitude` · `longitude` | the contact button and the published address |
| `profilePicUrl` · `profilePicUrlHd` | the profile picture, full size and thumbnail |
| `hasHighlightReels` · `hasVideos` · `hasMusicOnProfile` | what else is on the profile |
| `aboutCountry` · `aboutDateJoined` · `aboutFormerUsernames` · `aboutVerified` | the account history, when you ask for it |
| `charged` · `aboutAccountCharged` · `status` · `statusReason` | what was billed and why, so your invoice reconciles from the dataset itself |

**About the contact fields.** They appear only when the account holder has published them on their own profile — that is a setting they control, and most personal accounts never turn it on. When an account has not published one, the column is empty. Nothing here is inferred, enriched or looked up anywhere else: what you get is what that profile shows. Expect them on business and creator accounts, and not on ordinary personal ones.

Picture links are signed and time-limited by Instagram, so download what you need in the same session. The requesting network address is stripped out of every link before it reaches your dataset.

### What a row looks like

One delivered row from a real run — the handle `bluebottle`, 2026-09-12. The whole record is one `profile` charge: the contact fields are not a second event. Profile pictures are signed and time-limited by Instagram, and the account's own outbound links are shown here as placeholders.

```json
{
  "userId": "354032059",
  "username": "bluebottle",
  "fullName": "Blue Bottle Coffee",
  "profileUrl": "https://www.instagram.com/bluebottle/",
  "biography": "Experience the best of what coffee has to offer.",
  "bioLinks": [
    {"title":"Brew Guides","url":"https://…"},
    {"title":"Blue Bottle Membership","url":"https://…"},
    {"title":"Shop Blue Bottle Coffee","url":"https://…"},
    {"title":"Sustainability at Blue Bottle","url":"https://…"}
  ],
  "externalUrl": "https://…",
  "pronouns": [],
  "followerCount": 516686,
  "followingCount": 805,
  "mediaCount": 2580,
  "totalClipsCount": 1,
  "mutualFollowersCount": 0,
  "isPrivate": false,
  "isVerified": true,
  "isBusiness": true,
  "isProfessionalAccount": true,
  "accountType": 2,
  "accountBadges": [],
  "category": "Coffee shop",
  "categoryId": 128673187201735,
  "hasHighlightReels": true,
  "hasVideos": true,
  "hasMusicOnProfile": false,
  "publicEmail": "support@bluebottlecoffee.com",
  "publicPhoneNumber": null,
  "publicPhoneCountryCode": null,
  "contactPhoneNumber": null,
  "businessContactMethod": "CALL",
  "addressStreet": null,
  "cityName": null,
  "zip": null,
  "latitude": null,
  "longitude": null,
  "instagramLocationId": "212973824",
  "whatsappNumber": null,
  "profilePicUrl": "https://…",
  "profilePicUrlHd": "https://…",
  "aboutCountry": null,
  "aboutDateJoined": null,
  "aboutFormerUsernames": null,
  "aboutVerified": null,
  "source": "instagram",
  "input": "bluebottle",
  "sourceIndex": 0,
  "scrapedAt": "2026-09-12T10:09:01.900Z",
  "charged": true,
  "aboutAccountCharged": false,
  "status": "delivered",
  "retryable": false,
  "statusReason": null,
  "#k": "bluebottle",
  "#ce": {"profile":1},
  "#ask": {"delivered":1}
}
```

### Price

| Apify plan | Per account | Per 1,000 accounts |
|---|---|---|
| Free plan | $0.006 | $6.00 |
| Bronze | $0.0045 | $4.50 |
| Silver | $0.0036 | $3.60 |
| Gold and above | $0.003 | **$3.00 per 1,000** |

The opt-in account history is a second event, `about-account`, at a flat **$0.003** on every plan — charged only on an account whose history record actually came back, and off unless you switch it on.

Platform usage is included in those prices — there is no separate compute bill on top, and no start fee. Set `maxTotalChargeUsd` on the run and it is a hard ceiling: the run stops cleanly under it and the last row says what is left.

### Private accounts are delivered, not refused

Instagram publishes the account itself even when the posts behind it are private: the follower, following and post counts, the biography, the category and the picture are all public. So a private account here is a normal delivered row with `isPrivate: true` and a sentence saying what is and is not public about it. It is charged like any other delivered account, because it is one. Only the posts are missing, and they were never part of this actor.

### Honest rows: what "not charged" actually means

Every run closes its own books. An account that was not delivered leaves an uncharged row saying which of seven things happened, and none of them is billed on either event:

- **`account_not_found`** — Instagram has no account at that handle. It was spelled differently, deleted or renamed. A definitive answer, not a failure.
- **`stopped_at_limit`** — your result limit, your maximum cost per run, or the run clock stopped it first. The row names which.
- **`vendor_unavailable`** — the read did not go through. Temporary, says nothing about the account, and a re-run is the fix.
- **`source_refused`** — Instagram refused to look that value up at all, and it refuses it the same way every time, so a re-run cannot change it. Check the handle in a browser; if it opens there, say so on the Issues tab.
- **`unsupported_shape`** — Instagram answered in a shape this actor does not read yet. That is on us, not on the account, it says nothing about whether the account exists, and a re-run gets the same answer — tell us on the Issues tab which account it was and support for it will be added.
- **`vendor_budget`** — this actor reached its own monthly collection allowance and stopped rather than collecting more.
- **`user_input`** — the value was not an Instagram account this actor can look up. The row says what it looked like and what to paste instead.

The last row of every run is a receipt: delivered, asked for, how many account-history records came back, what stopped the run, and the charged totals for both events. A time limit ends the collecting, never the delivering — accounts already in hand are always written out.

### How often this data changes

Follower and following counts move every day on an active account, post counts move whenever it posts, and the bio, link and contact fields change only when the account holder edits them — often not for months. So a weekly or monthly re-run is what most buyers want: it is the counts that go stale, not the contact block. This actor deliberately has no repeat memory and never hands back a remembered row, because a remembered row would hand back a stale number, and the number is the thing you are buying. Every run reads the account fresh.

### Limits and good manners

**Max accounts** and **Max run seconds** take any number you type: this actor delivers at most 5,000 accounts in one run and runs for at most an hour (and for at least 30 seconds), so a bigger ask runs at the ceiling instead of being refused, and one uncharged row says what was asked for and what was used.

Only what Instagram publishes is returned: there is no private data here, and nothing behind a login. A contact field the account holder has not published is empty rather than guessed. This actor may stop working if Instagram changes how the data is served; if it does, accounts that could not be delivered are never charged.

### Related actors

- One account's own posts: **Instagram Profile Posts Scraper** — https://apify.com/steadyfetch/instagram-profile-posts
- Everyone who follows an account: **Instagram Followers Scraper** — https://apify.com/steadyfetch/instagram-followers-scraper

### Reliability

The accounts come from a licensed data feed rather than from scraping Instagram's own web pages, which is why there is nothing to log into and nothing to configure. When that feed cannot answer, the run says so on an uncharged row and finishes successfully — a failed read is never billed and never dressed up as a missing account.

### Support

Something off, or a column you need that is not here? Open an issue on the Issues tab — we usually reply within a couple of hours.

If it earned its keep, a rating helps other buyers find it, and saving the actor keeps it one click away.

# Actor input Schema

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

Instagram accounts, one per line — "nasa", "@nasa" or https://www.instagram.com/nasa/ all work, and a column pasted out of a spreadsheet is split for you. A number of 8 digits or more is read as an Instagram account id; to look up an all-numeric username, paste its profile link instead. An account that does not exist or could not be read is never charged. A post or reel link is refused with a row naming the actor that does take it. Leave it empty and the run returns built-in sample rows instead of looking anything up, so you can see the output shape at no result fee.

## `includeAboutAccount` (type: `boolean`):

OFF (default): nothing extra is read and nothing extra is charged. ON: each delivered account also gets the country Instagram shows for it, the month the account was created and any former usernames, in the four `about*` columns — charged as one `about-account` event at $0.003 on every plan, and only when that record actually came back. If it cannot be read, the account still delivers in full and there is no result fee for the missing part.

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

Hard cap on the accounts this run delivers. Accounts past the cap are not looked up and leave an uncharged row saying so, so a long paste can never cost more than you meant. An account that could not be delivered does not use up one of these slots — the next account on your list takes it. This actor delivers at most 5,000 accounts in one run: ask for more and the run continues at 5,000, with one uncharged row saying so.

## `maxRunSeconds` (type: `integer`):

The run stops cleanly before this many seconds and reports what is left, instead of being killed by a timeout. A time limit ends the collecting, never the delivering: accounts already in hand are always written out. One run lasts at most 3,600 seconds and needs at least 30: ask outside that and the run continues at the nearest of the two, with one uncharged row saying so.

## Actor input object example

```json
{
  "usernames": [],
  "includeAboutAccount": false,
  "resultsLimit": 100,
  "maxRunSeconds": 600
}
```

# Actor output Schema

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

One row per account delivered: followers, following and post counts, bio and links, category, verification, pictures, and the email, phone and address the account publishes on its own profile — all at one price, with no second charge for the contact fields. A contact field the account did not publish is empty, never guessed. Every row carries `charged` and `status`, so the invoice reconciles from the dataset itself: only rows with charged = true were billed the `profile` event.

## `aboutAccount` (type: `string`):

With "Also get the account history" on, each delivered account carries `aboutCountry`, `aboutDateJoined`, `aboutFormerUsernames` and `aboutVerified`, and the row's `aboutAccountCharged` says whether the `about-account` event was billed for it. It is billed only when that record actually came back; an account whose history could not be read still delivers in full with `aboutAccountCharged: false`.

## `misses` (type: `string`):

One uncharged row for every account that was not delivered, saying which of seven things happened: `account_not_found` (Instagram has no account at that handle), `stopped_at_limit` (your result limit, your cost cap or the run clock stopped it first), `vendor_unavailable` (the read failed and a re-run is the fix), `source_refused` (Instagram refused to look that value up at all, the same way every time — a re-run cannot change it), `unsupported_shape` (Instagram answered in a shape this actor does not read yet — on us, and a re-run gets the same answer), `vendor_budget` (this actor reached its own monthly collection allowance), `user_input` (the value was not an Instagram account this actor can look up). A limit you typed above this actor's own ceiling adds one `input_note` row instead of refusing the run, saying what was asked and what was used. None of these is charged on either event.

## `summary` (type: `string`):

Accounts delivered, what was asked for, how many account-history records were delivered and how many could not be read, what stopped the run, and the charged totals for both events.

# 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": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("steadyfetch/instagram-profile-details-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": [] }

# Run the Actor and wait for it to finish
run = client.actor("steadyfetch/instagram-profile-details-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": []
}' |
apify call steadyfetch/instagram-profile-details-scraper --silent --output-dataset

```

## MCP server setup

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