Instagram Followers & Following Scraper – Export Lists avatar

Instagram Followers & Following Scraper – Export Lists

Pricing

from $0.80 / 1,000 follower / followings

Go to Apify Store
Instagram Followers & Following Scraper – Export Lists

Instagram Followers & Following Scraper – Export Lists

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.

Pricing

from $0.80 / 1,000 follower / followings

Rating

0.0

(0)

Developer

LanceAPI

LanceAPI

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 days ago

Last modified

Categories

Share

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:

    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:

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

Input

FieldTypeRequiredDefaultLimits
profilesarray of stringsyesprefill instagram1–50 per run. Free plan: 1 profile per run.
dataToScrapestringnofollowersfollowers, following, both
resultsLimitintegerno1001–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:

InputMaximum rows delivered
1 profile, followers, 500500
1 profile, both, 5001,000 (500 + 500)
3 profiles, both, 5003,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.

{
"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

FieldTypeNullableWhat it is
sourceUsernamestringnoUsername of the profile the relationship was collected from.
sourceUserIdstringyesNumeric user ID of the source profile. null when it could not be resolved.
sourceProfileUrlstringnoProfile URL of the source profile.
relationshipTypestringnofollower = this account follows the source. following = the source follows this account.
rankintegernoPosition in the delivered list, starting at 1. Continuous across pages, so the first row of page 3 is not re-numbered to 1.
userIdstringyesNumeric 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.
usernamestringyesUsername of the counterparty. null when the payload proved an ID but no username.
fullNamestringyesDisplay 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.
profileUrlstringyesProfile URL of the counterparty, built from its username. null when the username is unavailable — the Instagram homepage is never substituted as the account URL.
profilePicUrlstringyesProfile picture URL. See Temporary URLs below.
isPrivatebooleanyesWhether the counterparty account is private. null when the source did not report it.
isVerifiedbooleanyesVerified badge. null when the source did not report it.
fbidV2stringyesInstagram's secondary account identifier, when available.
inputstringnoThe exact value you supplied that produced this row.
scrapedAtstringnoCollection 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.

StatusWhat it meansRows deliveredResult charge
COMPLETEThe 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 containedYes, for what was delivered
LIMIT_REACHEDYour resultsLimit was reached; more may be available.Up to your limitYes, for what was delivered
INSTAGRAM_LIMITEDInstagram stopped exposing more of the list.What was availableYes, only for what was delivered
PARTIALResults were delivered, then the data source stopped responding — or the run hit your maximum-charge budget.What arrived before the stopYes, only for what was delivered
SOURCE_PRIVATEThe profile is private, so its list is not available.0No
SOURCE_NOT_FOUNDThe username does not exist or was removed.0No
BLOCKEDThe request was refused before any list could be read.0No
FAILEDNothing could be collected, and nothing more specific applies.0No

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.

EventWhen it is charged
apify-actor-startOnce per run, raised by the platform — not by this Actor's code.
profileOnce, 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.
resultOnce 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

Tierapify-actor-startprofileresultPer 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

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

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:

# 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:

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:

{
"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

LimitValue
Profiles per run50 on paid plans, 1 on the free plan
Results per profile per relationship1–25,000 (resultsLimit); 100 on the free plan
Default resultsLimit100
Default memory256 MB
Default timeout3,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.