Instagram Followers & Following Scraper – Export Lists
Pricing
from $0.80 / 1,000 follower / followings
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
Maintained by CommunityActor 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
resultsLimitand 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
-
Open the Actor's Input tab.
-
In Instagram profiles, enter one or more usernames. All of these are accepted and treated as the same profile:
instagram@instagramhttps://www.instagram.com/instagram/ -
Keep Data to scrape on
Followersand Maximum results per profile on100for a first run. -
Click Start.
-
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
| 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.
{"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:
- requests the first page;
- normalizes and de-duplicates what came back;
- keeps the new results up to your
resultsLimit; - requests the next page if one is offered;
- 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-startper run. - One
profileunit 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
resultunit 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
resultcharge for that list. Theprofileunit 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
profilefee.
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_PRIVATEwith no rows and no result charge. Theprofilefee 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 osfrom apify_client import ApifyClientclient = 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
| 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.
profilePicUrlpoints 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.