# Instagram Followers & Following Scraper – Export Lists (`lance_api/instagram-followers-following-scraper`) Actor

Export Instagram followers and following lists from public profiles. Get usernames, user IDs, names, profile URLs, verified/private status, with automatic pagination, multi-profile support, deduplication, API access, and CSV/JSON/Excel export. No Instagram login required.

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

## Pricing

from $0.80 / 1,000 follower / followings

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 Followers & Following Scraper API

An Instagram followers scraper and following list API for public-profile audience research: give it one or more usernames, and it reads the **followers** list, the **following** list, or both — with automatic pagination, de-duplication and a transparent coverage status for every list. Results are written one row per relationship to your Dataset.

**Collect the followers or following list of public Instagram profiles — up to 25,000 results per profile, with pagination handled for you.**

Give this Actor one or more Instagram usernames, choose **Followers**, **Following**, or **Both**, and set how many results you want. It automatically paginates toward your requested limit and never exceeds it, removes duplicates, and writes one clean row per relationship to your Dataset.

- **You say how many. It handles the pages.** Set `resultsLimit` and the Actor keeps requesting pages until your number is reached, the list ends, or Instagram stops exposing more of it. No cursor, no token, no manual second run.
- **One row per relationship.** Every row carries the source profile, the direction (`follower` / `following`), its position in the list, and the counterparty account — ready to filter, join or export.
- **Batch up to 50 profiles in one run.** Profiles are processed independently, so one private or misspelled account never stops the rest of the batch.
- **You always know why a list stopped.** Every list gets a coverage status — `COMPLETE`, `LIMIT_REACHED`, `INSTAGRAM_LIMITED`, `PARTIAL`, and the rest — with a one-sentence reason.
- **Three billed events, all visible in the run summary.** A run charge applies once per run; a profile fee applies once when a canonical profile begins processing; a result fee is created only for unique relationship rows successfully written to the Dataset.

Typical uses: audience overlap analysis, creator due diligence, competitor audience comparison, community and network mapping, follower/following monitoring, public-account research, and downstream analytics on the exported data.

***

### Quick start

1. Open the Actor's **Input** tab.

2. In **Instagram profiles**, enter one or more usernames. All of these are accepted and treated as the same profile:

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

3. Keep **Data to scrape** on `Followers` and **Maximum results per profile** on `100` for a first run.

4. Click **Start**.

5. Open the **Output** tab to browse the Dataset, or **Export** to download it as JSON, CSV, Excel or XML.

Minimal input:

```json
{
  "profiles": ["instagram"],
  "dataToScrape": "followers",
  "resultsLimit": 100
}
```

***

### Input

| Field | Type | Required | Default | Limits |
|---|---|---|---|---|
| `profiles` | array of strings | yes | prefill `instagram` | 1–50 per run. Free plan: 1 profile per run. |
| `dataToScrape` | string | no | `followers` | `followers`, `following`, `both` |
| `resultsLimit` | integer | no | `100` | 1–25,000 (free runs: 100) |

#### `profiles` — Instagram profiles

Enter usernames (`hormozi`), `@usernames` (`@hormozi`) or profile URLs (`https://www.instagram.com/hormozi/`). Query strings and URL fragments are ignored, so a link copied straight from a browser works as-is.

Three ways of writing the same profile are recognised as one. Your batch is normalized and de-duplicated before anything is requested, so **the same profile is processed once and charged once** — you do not pay for a copy-paste mistake.

Enter usernames rather than numeric Instagram user IDs: this Actor resolves the internal ID itself, so you never have to look one up. (An all-digits value is read as a username, because that is also a valid Instagram username.)

#### `dataToScrape` — Data to scrape

- **`followers`** — the accounts that follow the profile.
- **`following`** — the accounts the profile follows.
- **`both`** — each list is collected independently.

With `both`, one account that appears in both lists is delivered as **two rows**, because they are two different relationships. Each list also gets its own limit, its own row numbering and its own coverage status.

#### `resultsLimit` — Maximum results per profile

Maximum number of **unique** results for each profile **and** each selected relationship type. Use it to match the Dataset size to your research scope instead of collecting — and paying for — more than you need.

Examples:

| Input | Maximum rows delivered |
|---|---|
| 1 profile, `followers`, `500` | 500 |
| 1 profile, `both`, `500` | 1,000 (500 + 500) |
| 3 profiles, `both`, `500` | 3,000 |

Free runs are capped at **100 results per relationship**, and the run summary states the cap and
the requested value side by side (`resultsLimit` vs `resultsLimitRequested`) so nothing is changed
silently. Paid plans can request up to 25,000.

This is a hard cap, not a target. If the last page returns 50 results and you have 17 slots left, you get 17 — never 67, and never a charge for more than your limit.

***

### Output

Each delivered relationship is one Dataset row.

```json
{
  "sourceUsername": "instagram",
  "sourceUserId": "25025320",
  "sourceProfileUrl": "https://www.instagram.com/instagram/",
  "relationshipType": "follower",
  "rank": 1,
  "userId": "44675251420",
  "username": "layy.rivera",
  "fullName": "Layy Rivera",
  "profileUrl": "https://www.instagram.com/layy.rivera/",
  "profilePicUrl": "https://example.invalid/profile.jpg",
  "isPrivate": false,
  "isVerified": false,
  "fbidV2": "17841444742604406",
  "input": "instagram",
  "scrapedAt": "2026-10-26T12:00:00.000Z"
}
```

> Example values above are illustrative of the structure. Field names, types and nullability are exactly what the Actor produces.

#### Field reference

| Field | Type | Nullable | What it is |
|---|---|---|---|
| `sourceUsername` | string | no | Username of the profile the relationship was collected from. |
| `sourceUserId` | string | yes | Numeric user ID of the source profile. `null` when it could not be resolved. |
| `sourceProfileUrl` | string | no | Profile URL of the source profile. |
| `relationshipType` | string | no | `follower` = this account follows the source. `following` = the source follows this account. |
| `rank` | integer | no | Position in the delivered list, starting at 1. Continuous across pages, so the first row of page 3 is not re-numbered to 1. |
| `userId` | string | yes | Numeric Instagram user ID of the counterparty, kept as a string so large IDs stay exact. `null` when the payload proved a username but no usable ID. |
| `username` | string | yes | Username of the counterparty. `null` when the payload proved an ID but no username. |
| `fullName` | string | yes | Display name. `null` when unavailable — or when the source returned a system placeholder instead of a real name, which is discarded rather than published as someone's name. |
| `profileUrl` | string | yes | Profile URL of the counterparty, built from its username. `null` when the username is unavailable — the Instagram homepage is never substituted as the account URL. |
| `profilePicUrl` | string | yes | Profile picture URL. See *Temporary URLs* below. |
| `isPrivate` | boolean | yes | Whether the counterparty account is private. `null` when the source did not report it. |
| `isVerified` | boolean | yes | Verified badge. `null` when the source did not report it. |
| `fbidV2` | string | yes | Instagram's secondary account identifier, when available. |
| `input` | string | no | The exact value you supplied that produced this row. |
| `scrapedAt` | string | no | Collection time, ISO-8601 UTC. |

When available, `userId` is the preferred stable identifier for joins across runs. Usernames can
change; IDs do not. A row whose identity piece is unavailable carries `null` there — never an empty
string and never a guessed URL.

The default table view leads with `sourceUsername`, `relationshipType`, `rank`, `username`, `fullName`, `isVerified`, `isPrivate` and `profileUrl`. Long image URLs and bookkeeping columns are kept in the data but placed behind those.

***

### Automatic pagination

You never manage a cursor. For every profile and every relationship list, the Actor:

1. requests the first page;
2. normalizes and de-duplicates what came back;
3. keeps the new results up to your `resultsLimit`;
4. requests the next page if one is offered;
5. stops when your limit is reached, when the list ends, when Instagram stops exposing more of it, or when an unrecoverable error occurs.

Safety guards run alongside it: a page that repeats a cursor, or several pages in a row that add nothing new, end the traversal instead of looping. Pages are never re-requested or charged twice within a run.

***

### Coverage status

Every list gets exactly one status, reported per profile and relationship in the run summary.

| Status | What it means | Rows delivered | Result charge |
|---|---|---|---|
| `COMPLETE` | The exposed pagination reached its natural end. When the profile's own total cannot be confirmed, this does not independently prove that the Dataset count equals the account's public relationship count. | Everything the exposed list contained | Yes, for what was delivered |
| `LIMIT_REACHED` | Your `resultsLimit` was reached; more may be available. | Up to your limit | Yes, for what was delivered |
| `INSTAGRAM_LIMITED` | Instagram stopped exposing more of the list. | What was available | Yes, only for what was delivered |
| `PARTIAL` | Results were delivered, then the data source stopped responding — or the run hit your maximum-charge budget. | What arrived before the stop | Yes, only for what was delivered |
| `SOURCE_PRIVATE` | The profile is private, so its list is not available. | 0 | No |
| `SOURCE_NOT_FOUND` | The username does not exist or was removed. | 0 | No |
| `BLOCKED` | The request was refused before any list could be read. | 0 | No |
| `FAILED` | Nothing could be collected, and nothing more specific applies. | 0 | No |

`COMPLETE` means the traversal of the exposed list finished cleanly — the last offered page was read
and no further page was offered. It is not, by itself, a certificate that every account Instagram
publicly counts as a follower is present in the Dataset: Instagram does not always expose a
confirmable total, and even where it does, accounts that are private, deactivated or removed are
counted publicly but never appear in any list. See the limitations section below.

A coverage status of `INSTAGRAM_LIMITED` is not a bug report and not a reason to re-run immediately: it means Instagram made part of the list available and then closed it. You keep what was delivered and are charged only for that.

A public profile can genuinely have zero followers or follow nothing. That is a `COMPLETE` result
with 0 rows and no charge — not a failure. When the profile's own counters say it has relationships
but the data source exposes none, the list is reported as `INSTAGRAM_LIMITED` instead, so an empty
Dataset is never mistaken for an empty account.

***

### Instagram availability limitations

This Actor reads what Instagram makes available. It does not guarantee a complete export of every account.

- **The number you see on the profile is not the number you can scrape.** Instagram shows a public follower counter (say, 30,000+), but it serves the actual list in bounded pages and stops exposing it after a limit of its own choosing. A profile whose counter says tens of thousands may expose only a few hundred list entries — or stop at a threshold it picks per account and per day. What comes back is what Instagram exposed during the run, in Instagram's own ordering — a substantial sample, not a census.
- Some large, verified or commercial accounts expose only a limited portion of their follower or following list. Those runs return everything available, are marked `INSTAGRAM_LIMITED`, and are charged only for what was delivered.
- Private profiles return no list at all (`SOURCE_PRIVATE`).
- Instagram can change what it exposes at any time, so the number of results for the same profile can differ between runs on different days.
- A list can also be shorter than the profile's public follower count: private, deactivated and since-removed accounts are counted publicly but cannot be listed.

We do not claim to export the full follower list of any account, and no result should be read as "this account has exactly N followers".

***

### Billing

This Actor uses pay-per-event pricing with three events.

| Event | When it is charged |
|---|---|
| `apify-actor-start` | Once per run, raised by the platform — not by this Actor's code. |
| `profile` | Once, when a canonical profile begins processing — before the profile is resolved. Followers, Following and Both all cost the same single unit. A profile that later turns out to be private or non-existent has still begun processing, so it carries this fee. |
| `result` | Once per unique follower or following relationship **accepted into the Dataset**. |

The result charge and the Dataset write are the same action: the Actor tells Apify to store a batch
of relationships and to charge `result` for each one in a single call. That is what keeps the two
numbers identical — you are never charged for a row that was not stored, and never given a row that
was not charged for.

Result fees follow delivery: a relationship is charged only when its row is successfully written
to the Dataset. A separate Profile processing fee applies once when a canonical profile begins
processing, and the Actor Start event applies once per run.

#### Rates

| Tier | `apify-actor-start` | `profile` | `result` | Per 1,000 results |
|---|---:|---:|---:|---:|
| Free | $0.001 | $0.001 | $0.0015 | $1.50 |
| Bronze | $0.001 | $0.001 | $0.0012 | $1.20 |
| Silver | $0.001 | $0.001 | $0.0010 | $1.00 |
| Gold | $0.001 | $0.001 | $0.0008 | $0.80 |

Platinum and Diamond are charged at the Gold rate. The prices above are Actor event prices. Check
the Actor's Pricing tab for the current platform-usage policy.

Worked example (Bronze): one profile, `dataToScrape: "both"`, `resultsLimit: 100`, 100 followers and
100 following delivered = $0.001 Actor Start + $0.001 Profile + 200 × $0.0012 = **$0.242**.

**What you are charged for**

- One `apify-actor-start` per run.
- One `profile` unit per input profile that begins processing — including a profile that turns out to be private or not found, and including an empty list. Duplicated spellings of the same profile are de-duplicated first and billed once.
- Each unique relationship accepted into the Dataset, at one `result` unit each.

**What you are not charged for**

- **Duplicates.** The same profile written three ways is processed once. A relationship repeated across pages, or re-fetched by automatic recovery, is written once and charged once.
- **Empty results.** No rows means no `result` charge for that list. The `profile` unit still applies: the profile was processed.
- **Results Instagram never exposed.** If Instagram makes 48 of 5,000,000 followers available, you are charged for 48 — not for your `resultsLimit`, and not for the missing tail.
- **The excess on a final page.** A page larger than your remaining slots is trimmed before writing, so you never pay for more than your limit.
- **The same data twice after automatic recovery.** Results already delivered in a run are not re-written or re-billed.
- **Inputs that never start.** An invalid value, an exact duplicate, or a profile refused by the free-usage policy is not billed a `profile` fee.

**What happens if you set a maximum cost**

If you set a maximum charge for the run, the Actor respects it instead of overspending. The moment
the budget can no longer cover another charge — whether that is a `result` row or a `profile` unit —
the run stops collecting, keeps everything already delivered and billed, and reports the affected
list as `PARTIAL` with a termination reason that says the maximum charge was reached. It does not
keep requesting pages it cannot deliver, and it does not start another profile whose fee it cannot
raise.

**How to control cost**

`resultsLimit` is the main lever: it caps unique results per profile **and** per relationship type, and it is enforced before writing. With `dataToScrape: "both"`, remember that each list can return up to `resultsLimit` rows. Free runs are additionally capped at 100 results per relationship.

***

### Partial results and failures

- **One bad input does not stop the batch.** Each profile is processed on its own; a private or misspelled account leaves the other profiles running.
- **Partial results are saved as they arrive.** Rows are written to the Dataset in batches while pagination is still running, not collected in memory until the end. If a later page fails after every recovery option is exhausted, the results already delivered stay in the Dataset, are marked `PARTIAL`, and are the only rows charged for.
- **Memory does not grow with the result count.** A 25,000-result run holds at most one batch of rows at a time, so a large request is limited by runtime rather than by memory.
- **Invalid inputs are reported, not silently dropped.** Values that are not Instagram usernames or profile URLs appear in the run summary under `invalidInputs`.
- **Every failed profile appears in the run summary.** A failure is not left only in the log, so you can reconcile the summary against your input list.

***

### Private and missing profiles

- A **private** profile returns `SOURCE_PRIVATE` with no rows and no result charge. The `profile` fee still applies, because the profile began processing before its privacy was established. No login, session or cookie is used, and none is accepted.
- A profile that **does not exist** returns `SOURCE_NOT_FOUND`; the same profile-fee rule applies.
- This Actor works with **public** data only. There is no option to authenticate as a user, and no option to read a private audience.

***

### FAQ

**Do I need Instagram login, cookies or a session ID?**
No. The Actor reads Instagram's public surface anonymously and never asks for — or accepts — a login, password, cookie or session ID.

**Can it scrape private follower lists?**
No. A private profile exposes no list at all; the run reports it as `SOURCE_PRIVATE`. There is no option to read a private audience.

**Is every follower list complete?**
No. Instagram serves these lists in bounded pages and stops exposing them at a limit of its own choosing. A profile whose public counter shows tens of thousands of followers may expose only a portion of them. See *Instagram availability limitations* above.

**What does `COMPLETE` actually mean?**
That the exposed pagination reached its natural end — the last offered page was read and no further page was offered. It does not independently prove that the Dataset holds the account's full public relationship count.

**Why can Dataset rows be lower than the public follower count?**
Three reasons, often combined: Instagram may stop exposing the list before its end (`INSTAGRAM_LIMITED`); private, deactivated and removed accounts are counted publicly but never appear in a list; and your `resultsLimit` caps each list by design.

**Does a private or not-found profile still incur a Profile fee?**
Yes, if the profile reached the processing stage: the `profile` fee is charged once when a canonical profile begins processing, before it is resolved. No `result` fees apply, because no rows were written.

**How does `both` affect result count and pricing?**
Each list is collected independently and can deliver up to `resultsLimit` rows, so `both` can deliver twice as many rows — and result fees scale with rows delivered. The `profile` fee is charged once per profile regardless of `both`.

**Is `rank` a permanent Instagram ranking?**
No. It is the position in the list as Instagram exposed it during this run, 1-based and continuous across pages. Instagram's ordering can change between runs.

**Are duplicate inputs charged twice?**
No. The same profile written in several ways (`hormozi`, `@hormozi`, a profile URL) is de-duplicated before processing and billed one `profile` fee. Duplicate relationships across pages are written and billed once.

***

### API usage

Run the Actor and read the Dataset with the Apify API.

#### JavaScript

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });

const run = await client.actor('lance_api/instagram-followers-following-scraper').call({
  profiles: ['instagram'],
  dataToScrape: 'followers',
  resultsLimit: 100,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, items[0]);
```

#### Python

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ['APIFY_TOKEN'])

run = client.actor('lance_api/instagram-followers-following-scraper').call(run_input={
    'profiles': ['instagram'],
    'dataToScrape': 'followers',
    'resultsLimit': 100,
})

for item in client.dataset(run['defaultDatasetId']).iterate_items():
    print(item['rank'], item['username'], item['relationshipType'])
```

#### cURL

Start the run and wait for it to finish, then read the Dataset:

```bash
## 1) Start the run and wait (up to 300 s) for it to finish.
RUN=$(curl -s -X POST \
  "https://api.apify.com/v2/acts/lance_api~instagram-followers-following-scraper/runs?waitForFinish=300" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "profiles": ["instagram"],
    "dataToScrape": "followers",
    "resultsLimit": 100
  }')

## 2) Read the run's default Dataset.
DATASET_ID=$(echo "$RUN" | python3 -c "import json,sys; print(json.load(sys.stdin)['data']['defaultDatasetId'])")

curl -s "https://api.apify.com/v2/datasets/$DATASET_ID/items" \
  -H "Authorization: Bearer $APIFY_TOKEN"
```

For longer runs, drop `waitForFinish` and poll `GET /v2/actor-runs/{runId}` until `status` is
`SUCCEEDED`, then request the Dataset as above.

The run summary is at:

```text
https://api.apify.com/v2/key-value-stores/<storeId>/records/RUN_SUMMARY
```

***

### Run summary

Alongside the Dataset, the run writes `RUN_SUMMARY` to the key-value store:

```json
{
  "status": "SUCCEEDED",
  "profilesProcessed": 1,
  "profilesSucceeded": 1,
  "profilesFailed": 0,
  "totalResults": 48,
  "chargedResults": 48,
  "coverage": { "INSTAGRAM_LIMITED": 1 },
  "billing": {
    "mode": "pay_per_event",
    "confirmed": true,
    "profilesRequested": 1,
    "profilesCharged": 1,
    "requestedResults": 48,
    "billableResults": 48,
    "chargedResults": 48,
    "listsStoppedByChargeLimit": 0,
    "maxTotalChargeUsd": 5
  },
  "pagination": { "pagesFetched": 1 },
  "results": [
    {
      "sourceUsername": "instagram",
      "sourceUserId": "25025320",
      "relationshipType": "follower",
      "requestedLimit": 500,
      "deliveredCount": 48,
      "uniqueCount": 48,
      "duplicatesDropped": 0,
      "pagesFetched": 1,
      "hasNextPage": false,
      "coverageStatus": "INSTAGRAM_LIMITED",
      "terminationReason": "Instagram did not expose another page for this list.",
      "billableResultCount": 48,
      "chargedResultCount": 48,
      "billingConfirmed": true
    }
  ]
}
```

`deliveredCount`, `uniqueCount` and `billableResultCount` are always the same number: the rows in
your Dataset. `chargedResultCount` is what Apify confirmed it charged for those rows, and
`billingConfirmed` says whether that confirmation exists at all. On a normal priced run all four are
equal, so `Dataset rows = deliveredCount = uniqueCount = chargedResultCount`. A run without platform
pricing reports `chargedResultCount: 0` with `billingConfirmed: false` rather than inventing a total.

`billing.profilesRequested` / `billing.profilesCharged` are the `profile` event, which is counted
separately and never folded into `chargedResults`: one unit per processed profile, whatever
`dataToScrape` says.

The summary intentionally reports outcomes — pages fetched, whether another page was offered, why the list stopped, how much was charged. It does not expose internal request cursors, request counts, provider names or endpoints.

***

### Limits

| Limit | Value |
|---|---|
| Profiles per run | 50 on paid plans, 1 on the free plan |
| Results per profile per relationship | 1–25,000 (`resultsLimit`); 100 on the free plan |
| Default `resultsLimit` | 100 |
| Default memory | 256 MB |
| Default timeout | 3,600 s |

Practical note: 25,000 is the largest target every path can actually reach — at the smallest
documented page size it needs about 2,084 pages, and the Actor's page ceiling sits above that on
purpose. Even so, a very large run is bounded by runtime as much as by the limit: each page is a
real request. Results are written to the Dataset as they arrive, so a run that approaches the
timeout keeps everything collected so far and reports `PARTIAL`.

***

### Privacy and temporary URLs

- Only publicly available Instagram data is collected. No login, password, cookie or session is used or accepted.
- `profilePicUrl` points at an image URL supplied by the data source. These URLs are temporary and can expire — download the images if you need to keep them.
- The Actor does not store your input on its own, and does not share it with anyone.
- Use the data responsibly and in line with Instagram's terms and the law in your jurisdiction. This tool is for public-data research, not for surveillance or for building profiles of individuals without a lawful basis.

***

### Support

Found a problem? Open an issue on the Actor's page in Apify Console, or email **lanceapi2026@hotmail.com**.

To get a fast answer, include:

- the **Run ID**;
- the input you used (without any secrets);
- what you expected and what you got.

Please **never** send API tokens, cookies, session IDs, passwords or proxy credentials.

# Actor input Schema

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

Enter usernames (hormozi), @usernames (@hormozi) or Instagram profile URLs (https://www.instagram.com/hormozi/). Query strings and fragments are ignored. Your batch is normalized and de-duplicated automatically, so the same profile is processed — and charged — once, even if you write it three different ways. Free users: up to 1 profile per run. Paid plans: up to 50 profiles per run.

## `dataToScrape` (type: `string`):

Which relationship list to collect. "Followers" returns the accounts that follow the profile. "Following" returns the accounts the profile follows. "Both" collects each list independently, so one account that appears in both lists is delivered as two separate rows — two different relationships — and each list gets its own limit and its own coverage status.

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

Maximum number of unique results returned for each profile and for each selected relationship type. The Actor automatically paginates until this limit is reached, the list ends, or Instagram stops exposing another page — you never have to manage pagination yourself. This is also your main cost control: a page may return more items than you have left, and the extra ones are dropped before they are written, so you are never charged for more than this number per profile and relationship. With "Both", each list can return up to this many results. Free runs are capped at 100 per relationship and the run says so in its summary; paid plans can request up to 25000.

## Actor input object example

```json
{
  "profiles": [
    "instagram"
  ],
  "dataToScrape": "followers",
  "resultsLimit": 100
}
```

# Actor output Schema

## `dataset` (type: `string`):

One row per unique follower or following relationship actually delivered. Each row carries the source profile, the relationship type, its position in the list, and the counterparty account.

## `datasetConsole` (type: `string`):

No description

## `runSummary` (type: `string`):

Per-run totals in plain language: profiles processed, succeeded and failed, how many relationships were delivered and charged, how many pages were fetched, the coverage status of every list, and which inputs were rejected.

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

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

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,lance_api/instagram-followers-following-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/cwcr3CoSbucMVwFsF/builds/1lNGZ9Kaw1fuX2FJI/openapi.json
