Facebook Public Follower & Following Snapshot Tool
Get the public preview of a Facebook Page or Profile's followers, following, or friends, plus aggregate follower/following counts and parsed location, workplace, or school details.
Input schema
Input fields, types, required flags, descriptions, defaults, and prefills come from the published definition. Prefill examples are distinct from defaults.
| Field | Type | Required | Description |
|---|---|---|---|
startUrls | array | required | One or more Facebook Page or Profile targets. Accepts full URLs, profile.php?id=..., /people/<name>/<id>/ (plus /followers, /following, /friends, or /both suffixes), /p/..., and URLs already ending in /followers, /following, or ?sk=... . The m/mobile/touch hosts are normalized to www. mbasic is rejected because it is login-gated, and groups/events/marketplace/media pages are not supported. Bare usernames are accepted by the actor; the Console URL-list editor expects a full URL. |
mode | string | optional | Which public list(s) to collect. 'both' collects Followers and Following. 'friends' is a legacy alias: Facebook does not expose Friends to logged-out visitors, so it returns the Followers collection with isFallback=true. |
maxItems | integer | optional | Hard cap on row records across all targets. The default is 50; the daily prefill uses 8. Set 8 to reproduce the old 8-row preview behavior. |
Show all input fields
| Field | Type | Required | Description |
|---|---|---|---|
startUrls | array | required | One or more Facebook Page or Profile targets. Accepts full URLs, profile.php?id=..., /people/<name>/<id>/ (plus /followers, /following, /friends, or /both suffixes), /p/..., and URLs already ending in /followers, /following, or ?sk=... . The m/mobile/touch hosts are normalized to www. mbasic is rejected because it is login-gated, and groups/events/marketplace/media pages are not supported. Bare usernames are accepted by the actor; the Console URL-list editor expects a full URL. |
mode | string | optional | Which public list(s) to collect. 'both' collects Followers and Following. 'friends' is a legacy alias: Facebook does not expose Friends to logged-out visitors, so it returns the Followers collection with isFallback=true. |
maxItems | integer | optional | Hard cap on row records across all targets. The default is 50; the daily prefill uses 8. Set 8 to reproduce the old 8-row preview behavior. |
maxItemsPerTarget | integer | optional | Optional cap per target (0 = no per-target cap). Use it to stop one large account from using the whole maxItems budget. |
searchQuery | string | optional | Keep only accounts whose name contains this text (Facebook's own in-list search, case-insensitive substring). Empty means no filter; one-character queries are treated as no filter because Facebook ignores them. |
includeProfileDetails | boolean | optional | Adds row-level follower/following counts, category, email, phone, website, address, work, education, current city, and hometown when Facebook exposes them. Uses two opt-in GraphQL requests per row (header + light About) plus a profile-HTML fallback, all capped by maxItems. Off by default; absent fields are omitted, never fabricated. |
deduplicateRows | boolean | optional | When enabled, each profileId/profileUrl is emitted once across all targets and modes (first occurrence wins). Per-target pagination duplicates are always removed. |
Download the published input schema
View complete JSON
{
"description": "Collect the public followers and/or following lists of Facebook Pages and Profiles, including real cursor pagination, list-size transparency, search, and optional profile counts/contact/category enrichment. No login, cookies, or proxy setup required.",
"properties": {
"deduplicateRows": {
"default": false,
"description": "When enabled, each profileId/profileUrl is emitted once across all targets and modes (first occurrence wins). Per-target pagination duplicates are always removed.",
"prefill": false,
"title": "Deduplicate rows across targets",
"type": "boolean"
},
"includeProfileDetails": {
"default": false,
"description": "Adds row-level follower/following counts, category, email, phone, website, address, work, education, current city, and hometown when Facebook exposes them. Uses two opt-in GraphQL requests per row (header + light About) plus a profile-HTML fallback, all capped by maxItems. Off by default; absent fields are omitted, never fabricated.",
"prefill": false,
"title": "Include profile counts and contact details",
"type": "boolean"
},
"maxItems": {
"default": 50,
"description": "Hard cap on row records across all targets. The default is 50; the daily prefill uses 8. Set 8 to reproduce the old 8-row preview behavior.",
"maximum": 10000,
"minimum": 1,
"prefill": 8,
"title": "Maximum total rows",
"type": "integer"
},
"maxItemsPerTarget": {
"default": 0,
"description": "Optional cap per target (0 = no per-target cap). Use it to stop one large account from using the whole maxItems budget.",
"maximum": 10000,
"minimum": 0,
"prefill": 0,
"title": "Maximum rows per target",
"type": "integer"
},
"mode": {
"default": "followers",
"description": "Which public list(s) to collect. 'both' collects Followers and Following. 'friends' is a legacy alias: Facebook does not expose Friends to logged-out visitors, so it returns the Followers collection with isFallback=true.",
"editor": "select",
"enum": [
"followers",
"following",
"both",
"friends"
],
"enumTitles": [
"Followers",
"Following",
"Followers + Following",
"Friends (legacy alias - returns Followers)"
],
"prefill": "followers",
"title": "Mode",
"type": "string"
},
"searchQuery": {
"default": "",
"description": "Keep only accounts whose name contains this text (Facebook's own in-list search, case-insensitive substring). Empty means no filter; one-character queries are treated as no filter because Facebook ignores them.",
"editor": "textfield",
"prefill": "",
"title": "Search inside the list",
"type": "string"
},
"startUrls": {
"description": "One or more Facebook Page or Profile targets. Accepts full URLs, profile.php?id=..., /people/<name>/<id>/ (plus /followers, /following, /friends, or /both suffixes), /p/..., and URLs already ending in /followers, /following, or ?sk=... . The m/mobile/touch hosts are normalized to www. mbasic is rejected because it is login-gated, and groups/events/marketplace/media pages are not supported. Bare usernames are accepted by the actor; the Console URL-list editor expects a full URL.",
"editor": "requestListSources",
"example": [
{
"url": "https://www.facebook.com/nike"
},
{
"url": "https://www.facebook.com/zuck"
}
],
"items": {
"properties": {
"url": {
"description": "A Facebook Page or Profile URL, profile.php?id=..., /people/... URL, or bare username.",
"title": "URL",
"type": "string"
}
},
"required": [
"url"
],
"type": "object"
},
"prefill": [
{
"url": "https://www.facebook.com/nike"
}
],
"title": "Facebook Page or Profile URLs",
"type": "array"
}
},
"required": [
"startUrls"
],
"schemaVersion": 1,
"title": "Facebook Followers & Following Scraper Input",
"type": "object"
}
Output schema
Published result links and storage locations. This schema is not an exhaustive contract for every field in a dataset record.
| Field | Type | Required | Description |
|---|---|---|---|
records | string | optional | Dataset containing all scraped follower/following rows, per-target summary records, and per-target error records. |
Download the published output schema
View complete JSON
{
"actorOutputSchemaVersion": 1,
"description": "Facebook Page/Profile follower and following rows collected through public cursor pagination, plus per-target summaries with the actual collection name, reported list size, pagination completeness, access state, and follower/following counts. Optional includeProfileDetails enrichment adds row-level counts and About contact/category fields when Facebook exposes them.",
"properties": {
"records": {
"description": "Dataset containing all scraped follower/following rows, per-target summary records, and per-target error records.",
"template": "{{links.apiDefaultDatasetUrl}}/items",
"title": "Followers / Following records",
"type": "string"
}
},
"title": "Facebook Public Follower & Following Snapshot Tool Output",
"type": "object"
}
Dataset schema
Published dataset views and any declared field definitions. A view can select only a subset of the returned record fields.
Download the published dataset schema
View complete JSON
{
"actorSpecification": 1,
"views": {
"overview": {
"display": {
"component": "table",
"properties": {}
},
"title": "Followers / Following",
"transformation": {
"fields": [
"recordType",
"name",
"profileId",
"profileTypename",
"position",
"collectionName",
"profileUrl",
"isVerified",
"location",
"accessState"
]
}
}
}
}
Key-value schema
Published key-value store definitions, when provided by this build.
This definition is not present in the default published build. See the published README for available documentation.
Published README
Read the full published documentation
Facebook Public Follower & Following Snapshot Tool
Collect who publicly follows a Facebook Page or Profile — and who that Page or Profile follows — without a login, cookies, or proxy setup. The actor reads Facebook's own public collection pages, follows their pagination to the end (or to your row limit), and reports the real collection name, the reported list size, and whether the list was exhausted or cut short.
Facebook shows logged-out visitors an 8-row preview on each collection page. This actor does not stop at that preview: it keeps requesting the next public page until the list ends or maxItems is reached. Large accounts can still have slices that Facebook does not expose publicly, so listSize and paginationComplete are reported beside every result.
What this actor does
- Four modes:
followers,following,both(followers + following), andfriends(a legacy alias that returns Followers — see the FAQ) - Real pagination: returns more than the 8-row preview, up to
maxItems(default 50), with per-target and global caps - In-list search:
searchQueryuses Facebook's own name filter and paginates through the matches - List transparency: every target gets a summary with
collectionName,listSize,hasNextPage,paginationComplete,pagesFetched, andpaginationStoppedReason - Stable identities:
profileId, the raw RelayprofileTypename, and a 1-basedpositionon every row - Optional enrichment:
includeProfileDetailsadds row-level follower/following counts plus About category, email, phone, website, address, work, education, current city, and hometown when Facebook exposes them (two opt-in GraphQL requests per row, with a profile-HTML fallback) - URL forms:
/people/<name>/<id>/followers|following|friends|bothis rewritten to the working?sk=form;m/mobile/touchhosts are normalized towww;mbasicis rejected as login-gated with a clear status - Batch input: any number of Page/Profile URLs or bare usernames per run; duplicates and invalid URLs are skipped
- No login, no cookies, no proxy setup — local direct, cloud residential last resort
- Empty fields are omitted, not returned as
nullor placeholder text
Output
Each target produces follower/following row records plus one target summary record per collection. If a collection cannot be downloaded at all, it produces a targetError record for that collection instead.
Row fields
recordType—followerorfollowingfor the actual collection;friendfor the legacyfriendsaliasname— display nameprofileUrl— direct profile/Page URL when Facebook provides one (omitted when the source returns no URL)profileId— Facebook account ID (numeric orpfbid...)profileTypename— the raw__typenameFacebook returns on the collection edge. It isUserfor Pages too, so it is not a reliable Page/User discriminator; the actor does not claim a semantic type it cannot verify. (The oldprofileTypefield was removed for this reason; it always saidUser, including for Pages.)imageUrl— profile picture URLisVerified— whether Facebook shows a verification badge (true/false)position— 1-based position in the returned collectioncollectionName— the actual collection Facebook served:FollowersorFollowingrequestedMode— the mode that produced this row:followers,following,both, orfriendsisFallback—trueonly when the requested list was served from a different collection (currentlyfriends→ Followers)location— parsed location when Facebook's subtitle line clearly shows oneworksAt— parsed workplace when confidently recognizablestudiedAt— parsed school when confidently recognizablesubtitleRaw— the raw subtitle text, kept even when it cannot be parsedprofileCategory— Page/Profile category (only withincludeProfileDetails=true)profileDelegateId— canonical Page/Profile ID from the profile page (only withincludeProfileDetails=true)profileFollowersCount/profileFollowersCountFormatted— row-level follower count and Facebook's abbreviated form (only withincludeProfileDetails=true; omitted when the header query has no social context)profileFollowingCount/profileFollowingCountFormatted— row-level following count and its abbreviated form (only withincludeProfileDetails=true)profileEmail,profilePhone,profileWebsite,profileAddress— About contact fields when Facebook exposes them (only withincludeProfileDetails=true)profileWork,profileEducation,profileCurrentCity,profileHometown— About text fields when Facebook exposes them (only withincludeProfileDetails=true)accessState— the target's access state (resultsfor rows)sourceUrl— the exact followers/following URL that was fetchedtargetUrl— the original URL/username from your inputscrapedAt— ISO 8601 timestamp
Target summary fields (recordType: "targetSummary")
targetUrl— the original inputmode— the requested moderequestedMode— the requested mode (same asmode; kept explicitly forbothandfriends)accessState—results,empty_valid,private_or_restricted,not_found,login_required,checkpoint_or_challenge,rate_limited, orschema_failurereturnedCount— rows actually emitted for this collection (can exceed 8)followersCount/followersCountFormatted— headline follower count, parsed and as Facebook's abbreviated stringfollowingCount/followingCountFormatted— headline following countcollectionName— the actual collection servedlistSize— Facebook's reported size for that collection. It is approximate, not a hard cap; usehasNextPageto know whether more remainedhasNextPage— whether Facebook still reported more rows when the actor stoppedpaginationComplete—truewhen the source was exhausted or genuinely empty;falsewhen stopped bymaxItems, a block, an unavailable/private target, or an errorpagesFetched— number of pagination pages fetched for this collectionsearchQuery— the applied search filter (omitted when empty)isFallback—truefor the legacyfriendsaliaspaginationStoppedReason—source_exhausted,max_items,max_items_per_target,rate_limited,graphql_error,graphql_unavailable,no_collection, oremptyscrapedAt— ISO 8601 timestamprecordType— alwaystargetSummary
Target error record (recordType: "targetError")
When every download attempt for a collection fails, the dataset gets one record with targetUrl, mode, accessState: "fetch_failed", errorMessage, and scrapedAt. A failed collection never stops the rest of the run.
Input
| Field | Type | Default | Description |
|---|---|---|---|
startUrls |
array | – | Facebook Page/Profile URLs, profile.php?id=..., /people/<name>/<id>/ (plus /followers, /following, /friends, or /both suffixes), /p/..., or bare usernames. URLs ending in /followers, /following, /friends, or ?sk=... are accepted. m/mobile/touch hosts are normalized to www; mbasic is rejected as login-gated. Bare usernames are accepted by the actor; the Console URL-list field itself expects a full URL. |
mode |
string | followers |
followers, following, both, or friends (legacy alias → Followers). |
maxItems |
integer | 50 |
Total row cap across all targets (1–10,000). The daily prefill uses 8. |
maxItemsPerTarget |
integer | 0 |
Optional per-target cap (0 = no cap). |
searchQuery |
string | "" |
Keep accounts whose name contains this text. Empty or one character means no filter. |
includeProfileDetails |
boolean | false |
Add row-level counts and About category/contact/work/education/current-city/hometown fields. Two opt-in GraphQL requests per row plus a profile-HTML fallback; absent fields are omitted. |
deduplicateRows |
boolean | false |
Emit each profileId/profileUrl once across all targets and modes. |
Example: followers of a Page
{
"startUrls": [{ "url": "https://www.facebook.com/nike" }],
"mode": "followers",
"maxItems": 100
}
Example: followers and following together
{
"startUrls": [{ "url": "https://www.facebook.com/nike" }],
"mode": "both",
"maxItems": 40
}
Example: search inside a large follower list
{
"startUrls": [{ "url": "https://www.facebook.com/nike" }],
"mode": "followers",
"searchQuery": "mar",
"maxItems": 50
}
Example: several targets with a per-target cap and profile enrichment
{
"startUrls": [
{ "url": "https://www.facebook.com/nike" },
{ "url": "https://www.facebook.com/zuck" }
],
"mode": "followers",
"maxItems": 20,
"maxItemsPerTarget": 10,
"includeProfileDetails": true
}
Use cases
- Audience research — collect and compare the public follower lists of related Pages
- Influencer and partnership vetting — sample follower identities, verification status, and Page categories
- Competitive monitoring — track how a Page's public follower/following lists change between scheduled runs
- Lead enrichment — add stable Facebook profile IDs plus optional row-level counts, category, and contact fields to a public audience list
- Brand safety review — inspect which public accounts a Page follows
- Growth analysis — combine
listSize,hasNextPage, and stableprofileIdvalues across snapshots
FAQ
Can I get every follower of a very large account?
You get every row Facebook exposes to a logged-out visitor, up to your maxItems. Facebook sometimes exposes only a slice of a large account's list. The summary tells you the reported listSize and whether hasNextPage remained true, so you can see when the source stopped before the reported total.
Why does returnedCount differ from listSize?
listSize is Facebook's own reported size for the collection. It can be approximate or stale (a Page may report 576 while 640 rows are still enumerable). returnedCount is what the actor actually emitted. Use hasNextPage and paginationComplete to judge whether more remained.
How is maxItems shared in both mode?
The remaining row budget is split across Followers and Following, so both lists contribute instead of one list consuming the whole cap. Rows left unused by a shorter list flow to the other list. If maxItemsPerTarget is reached in the first collection, that collection's rows stop at the cap but the other collection still emits its summary with returnedCount: 0 and paginationStoppedReason: "max_items_per_target" (or "max_items" when the global cap was hit). Summary records do not count toward either row cap.
What does friends mode return?
It is a legacy alias. Facebook does not expose a Friends roster to logged-out visitors; the friends tab serves the Followers collection. The actor therefore returns Followers, sets collectionName: "Followers", isFallback: true, and requestedMode: "friends". Rows keep recordType: "friend" for backward compatibility.
Do I need a Facebook account, cookies, or a proxy? No. The actor uses Facebook's public, logged-out pages. No login, cookies, or proxy configuration is required. It runs direct locally; on Apify cloud, cloud verification showed direct and AUTO GraphQL both rate-limited, so the actor uses a hardcoded residential last resort for reliable runs. Nothing is configured in the input, and no paid group is a schema default.
What does includeProfileDetails return, and how reliable is it?
It adds row-level follower/following counts from Facebook's header query and About category/contact/work/education/current-city/hometown fields from the light About query. In the Phase 6 cookieless probe, counts filled 30/30 row profiles, category 30/30, and at least one contact field 20/30. Coverage is conditional: private/hidden sources, pfbid rows that return empty About sections, and accounts without a field will omit it. The actor falls back to the profile HTML and never fabricates a missing value. This mode costs two extra GraphQL requests per row (plus a fallback request when needed), so it is off by default and capped by maxItems.
Why is a location, workplace, or school missing on some rows?
Facebook shows a subtitle line for only a minority of accounts (about 18–19% in the live sample). The actor parses it conservatively and always keeps subtitleRaw; it never fabricates values. includeProfileDetails can add structured About fields for some of the rows whose subtitle is empty.
What is the difference between private_or_restricted and not_found?
An HTTP 404 is not_found. A 200 page with Facebook's "This content isn't available at the moment / owner only shared it" message is private_or_restricted. Facebook serves the same 200 page for a private target and a nonexistent vanity, so the actor cannot distinguish those two cases; this is a documented limitation, not a missing retry.
Why did a run stop with paginationStoppedReason: "max_items"?
The row cap was reached before the source ended. Increase maxItems (or maxItemsPerTarget) to collect more, or keep the cap and use the summary's hasNextPage to confirm that more existed.
Why are some profile or image URLs missing?
Facebook occasionally returns a row with no profile URL. The row is kept with its profileId so you do not lose the account. imageUrl values are signed Facebook CDN links with oh/oe parameters; the observed expiry was about four days, and old links return 403. Rehost them immediately if you need durable images.
What if a run is rate-limited or only partly complete?
Facebook returns GraphQL error code 1675004 when it rate-limits the logged-out collection query. The actor treats that code as rate_limited regardless of the message, keeps every row already collected, preserves the preview's hasNextPage, and finishes the run with a status message that names the partial targets and the rate limit. On Apify cloud, direct and AUTO GraphQL were both rate-limited in verification, so the actor uses a hardcoded residential last resort; residential is never a schema default or an input field.
Is this actor affiliated with Facebook or Meta? No. It is an independent third-party actor that reads publicly accessible Facebook pages. It is not affiliated with, endorsed by, or operated by Meta Platforms, Inc.
Limitations (hard limits and honest coverage)
- 8 rows per request. Facebook caps every public collection response at 8 rows regardless of the requested
count; more rows require more requests. A 1,000-row run is roughly 125 collection requests. Cursor pagination is the mitigation, not a larger page. - No cookieless Friends roster. Every logged-out Friends URL form, section ID, and collection ID returned Followers, an empty list, or a login wall.
friendsis a truthful legacy alias for Followers only. - Private vs nonexistent at HTTP 200. Facebook serves the same "This content isn't available at the moment / owner only shared it" page for both. Only a real HTTP 404 is
not_found; a 200 page staysprivate_or_restricted. The actor cannot distinguish the two without a 404. listSizeis approximate. It is Facebook's reported collection count, not a stop signal or a promise of enumerable rows. UsehasNextPage,paginationComplete, andpaginationStoppedReason.- Image URLs expire. Facebook CDN links are signed (observed ~4-day expiry); old links return 403. Rehost immediately for durable media.
- Subtitles are sparse. Only about 18–19% of rows expose a subtitle line; missing location/work/school values are source sparsity, not parser loss. The parser is conservative and keeps
subtitleRaw. - Counts and contact fields are conditional.
includeProfileDetailsfills what Facebook exposes (30/30 counts and category, 20/30 any contact in the Phase 6 probe) and omits the rest. It never fabricates values. mbasicis login-gated. The normalizer rejectsmbasic.facebook.comwith a clear status;m/mobile/touchare normalized towww.- Group members, private lists, and other login-gated surfaces are out of scope.
- The Console's URL-list editor requires a full URL. Bare usernames are accepted by the actor when supplied through the input JSON or API.
Python quickstart
Use a schema-derived input and the Python standard library to start a run. An Apify account and token are required for execution.
Source build Cd6CD1dQVf90O4egD. Documentation is regenerated from the default published build; private drafts are not included.