# Facebook Pages & Business Leads Scraper (`coregent/facebook-pages-leads-scraper`) Actor

Find public Facebook Pages by keyword and location, or scrape Page URLs you already have. One lead-ready row per Page: category, description, followers, likes, address, website, public email and phone, social links and price range. Optional website enrichment. No Facebook login or cookies needed.

- **URL**: https://apify.com/coregent/facebook-pages-leads-scraper.md
- **Developed by:** [Delowar Munna](https://apify.com/coregent) (community)
- **Categories:** Lead generation, Social media, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 page results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Facebook Pages & Business Leads Scraper

![Facebook Pages & Business Leads Scraper](https://raw.githubusercontent.com/coregentdevspace/facebook-pages-leads-scraper-assets/main/thumbnail-facebook-pages-leads-scraper.png)

Find public Facebook Pages by keyword and location or scrape Page URLs you already have. Get one clean lead-ready record per Page with business category, description, followers, likes, address, website, public email/phone, social links and price range where available. Optional website enrichment is separate, and you never need to provide Facebook login credentials or cookies.

***

### What this Actor does

One Actor for the whole Facebook lead workflow — no stitching three tools together:

1. **Resolve** any Facebook Page URL, `@handle` or numeric Page ID you give it.
2. **Discover** more Pages from keywords and locations (see [Discovery](#discovery) — this needs a search provider key).
3. **Normalize** both paths through the same schema, so a Page found by search and a Page you named look identical in the output.
4. **Deduplicate** on Page ID — the same business is returned once and charged once, however many of your inputs found it.
5. **Filter** by email, phone, website, followers, category, verification, reviews and ad status.
6. **Enrich** each business's own website, optionally.

**No Facebook login, cookies, session IDs, browser profiles or access tokens are ever requested from you.** Only publicly visible Page data is collected.

***

### Scrape known Page URLs and handles

Paste anything that identifies a Page. All of these work and all resolve to the same record:

```
https://www.facebook.com/russanddaughters
https://www.facebook.com/RussAndDaughters/about
https://m.facebook.com/russanddaughters
facebook.com/russanddaughters
@russanddaughters
100068826391918
```

Numeric IDs cost one extra request, because Facebook only resolves an ID from the bare Page URL, not from the `/about` tab.

**Personal profiles are not supported.** A `/profile.php?id=…` or `/people/…` URL is reported as invalid rather than scraped — this Actor is for business Pages. Group, event, post and Marketplace URLs are rejected the same way, with the reason named in the log.

***

### Discovery

**Facebook publishes no logged-out Page search.** Measured 2026-08-24 against live Facebook:

| Surface | Result |
|---|---|
| `/search/pages/?q=…` | HTTP 404 |
| `/search/top/?q=…` | HTTP 404 |
| `/pages/category/…` | HTTP 404 |
| `/public?query=…` | Login required |
| `/directory/pages/…` | Login required |
| `/biz/directory/` | Login required |
| `/places/` | Login required |

So keyword discovery works through a **web-search provider** instead — searching the open web for indexed Facebook Pages. That involves no Facebook session and no credentials, so the no-login guarantee above is unaffected.

Discovery is **configured and working** — just supply keywords and, optionally, locations. Each query is combined with each location, and results are round-robined so one broad query cannot use up your whole result limit.

#### How many Pages one keyword can find

**A single broad keyword has a hard ceiling, and it is the search index's, not this Actor's.** Measured: `"pub"` with no location returns about **66 Facebook Pages** across 12 provider requests, then genuinely runs out. Asking for `maxResults: 100` will not produce 100 from that one word.

**Use locations to multiply the pool.** Each keyword is paired with each location, and every pair is a fresh search with its own index depth. `["pub"] × ["Boston MA", "Austin TX", "Denver CO"]` reaches roughly three times as many Pages as `["pub"]` alone — and returns better-qualified leads, because a local query surfaces local businesses rather than the largest brands.

The run log tells you which limit you hit:

| Log line | Meaning |
|---|---|
| `(no-more-results)` | The search index ran out. Add locations or more keywords. |
| `(wanted-reached)` | You got everything you asked for. |
| `(page-budget-reached)` + a warning | This Actor's per-query request budget stopped it. Raise `SEARCH_MAX_PAGES_PER_QUERY`. |

#### ⚠️ Breadth beats depth, and the difference is large

Raising the result limit on one keyword makes the Actor page deeper into the search index — and **deep index entries are mostly Pages that no longer exist.** Measured on the same query, same day:

| How the Pages were found | Unavailable |
|---|---:|
| First few search pages | **4%** |
| Paged to ~7 requests | 37–41% |
| Paged to ~14 requests | **55%** |
| Same keyword across **3 cities** | **18%** |

The three-city run returned **more** Pages than the deep run, at a third of the failure rate. So when a run comes back short, **add locations or keywords rather than raising the result limit** — each keyword-and-location pair is a fresh search with its own good first pages, where paging deeper is not.

Pages that cannot be read are **never charged**, so this costs you nothing but time. By default they are left out of your results entirely, keeping the list clean; the run log, the status message and `RUN_SUMMARY` all report how many and why, and the `RETRY_INPUT` record hands back the ones that were merely blocked as an input you can re-run later. Switch on **Include unavailable Pages as empty rows** if you would rather have them in the file.

Results also include some Facebook groups and posts, which are discarded automatically and never charged. For exhaustive coverage of a known set of businesses, supply Page URLs directly.

***

### Lead filters

All filters live under **Advanced** — none is on by default.

| Filter | Keeps |
|---|---|
| Minimum followers | Pages at or above a follower count |
| Categories | Pages whose category matches, partially and case-insensitively |
| Verified only | Pages with the verified badge |
| Currently running ads | Pages the transparency tab reports as advertising |
| Minimum recommend % / review count | Pages at or above a review threshold |

**Contact filters via the API.** `hasEmail`, `hasPhone` and `hasWebsite` are not in the input form — they kept a whole section occupied for options most runs leave off — but they still work when supplied programmatically, in a saved task or an API call:

```json
{ "searchQueries": ["plumber"], "locations": ["Sydney NSW"], "hasEmail": true, "hasPhone": true }
```

Each is a boolean, defaulting to false. They behave exactly like the filters above, including the over-fetch note below.

**Unknown values are kept, not dropped.** A Page whose follower count Facebook does not publish passes a follower filter — an unknown count is not a low count, and dropping those would quietly bias your results toward Pages that happen to publish metrics.

The single exception is **"currently running ads"**, which drops Pages whose ad status could not be read. That filter promises advertisers, and returning Pages that might not be advertising would break the promise its name makes.

#### The over-fetch note (please read before costing a run)

Facebook does not let anyone search *for* Pages that have an email. So `hasEmail`, `hasPhone`, `hasWebsite`, `minFollowers` and every other filter run **after** each Page has been fetched. A narrow filter over a broad input list means fetching many Pages to keep a few.

**You are never charged for a filtered-out Page.** Only Pages actually delivered to your dataset are charged. The cost of over-fetching is runtime, not per-result charges.

***

### Output

One row per unique Page. Every sample below is a **real record from a live run**, not an illustration.

![Output — all fields, table view](https://raw.githubusercontent.com/coregentdevspace/facebook-pages-leads-scraper-assets/main/facebook-pages-leads-scraper-output-all-fields-table-view.png)

**What a real run returns.** From the run pictured above — a keyword search with both add-ons on,
18 Pages found, 14 delivered with data:

| Field | Fill rate |
|---|---:|
| Phone | 93% |
| Address (incl. coordinates) | 93% |
| Website | 86% |
| Reviews / recommend % | 86% |
| Email | 57% |

The 4 Pages that returned nothing were not charged, and by default are not in the file at all —
switch on **Include unavailable Pages as empty rows** to see them. Fill rates vary by niche and location — trades and restaurants publish contact details far
more often than national brands do.

Four dataset views are provided. Pick the tab that matches the job.

<details open>
<summary><b>Pages</b> — the complete normalized record</summary>

```json
{
  "source": {
    "inputType": "search",
    "queriesMatched": [
      "Pub"
    ],
    "locationsMatched": [],
    "detailSource": "facebook-public-page",
    "searchProvider": "serper"
  },
  "recordType": "facebook_page",
  "pageId": "100063657036934",
  "url": "https://www.facebook.com/williamstonpub",
  "username": "williamstonpub",
  "name": "Williamston Pub & Grill",
  "category": "American Restaurant",
  "categories": [
    "American Restaurant"
  ],
  "description": "Welcome to The Williamston Pub & Grill!!!!",
  "isVerified": false,
  "metrics": {
    "followers": 2500,
    "followersApproximate": true,
    "following": 24,
    "followingApproximate": false,
    "likes": 2573,
    "talkingAbout": 4,
    "checkIns": 4649,
    "rating": null,
    "recommendPercent": 96,
    "reviewCount": 386
  },
  "contacts": {
    "emails": [
      "williamstonpub132@gmail.com"
    ],
    "phones": [
      "(517) 992-5060"
    ],
    "whatsapp": null,
    "website": "http://www.williamstonpub.com/",
    "websites": [
      "http://www.williamstonpub.com/"
    ],
    "messengerUrl": "https://m.me/williamstonpub",
    "addressUrl": "https://www.bing.com/maps/default.aspx?v=2&pc=FACEBK&mid=8100&where1=132+W+Grand+River+Ave%2C+Williamston%2C+MI%2C+United+States%2C+48895&FORM=FBKPL1&mkt=en-US",
    "address": {
      "street": "132 W Grand River Ave",
      "city": "Williamston",
      "region": "MI",
      "postalCode": "48895",
      "country": "United States",
      "countryCode": "US",
      "formatted": "132 W Grand River Ave, Williamston, MI, United States, 48895",
      "latitude": 42.689302673334,
      "longitude": -84.283966673334
    }
  },
  "business": {
    "hours": null,
    "hoursStatus": null,
    "priceRange": "$$",
    "priceTier": 2,
    "services": [
      "Takeout",
      "Dine-in"
    ],
    "serviceAreas": [],
    "isRunningAds": false,
    "adStatus": "This Page isn't currently running ads.",
    "adLibraryId": "423350807792987",
    "adLibraryUrl": "https://www.facebook.com/ads/library/?view_all_page_id=423350807792987",
    "creationDate": "November 20, 2013",
    "adminCountries": []
  },
  "images": {
    "profile": "https://scontent-dfw6-1.xx.fbcdn.net/v/t39.30808-1/302156025_512131424252088_8868690132033137412_n.jpg?…",
    "cover": "https://scontent-dfw6-1.xx.fbcdn.net/v/t39.30808-6/484052006_1250355910429632_3139013801400803085_n.jpg?…"
  },
  "socialLinks": {
    "instagram": null,
    "youtube": null,
    "x": null,
    "linkedin": null,
    "tiktok": null,
    "pinterest": null
  },
  "websiteEnrichment": {
    "status": "ok",
    "finalUrl": "https://www.williamstonpub.com/",
    "domain": "williamstonpub.com",
    "title": "Williamston Pub & Grill - Williamston, MI",
    "description": "Welcome to Williamston Pub &amp; Grill, a cherished local gathering place in the heart of Williamston, MI. Since…",
    "emails": [],
    "phones": [],
    "socialLinks": {
      "facebook": "https://www.facebook.com/williamstonpub"
    },
    "pagesFetched": 1,
    "durationMs": 773,
    "usable": true
  },
  "scrapedAt": "2026-08-25T05:14:48.447Z",
  "unavailableReason": null
}
```

</details>

<details>
<summary><b>Leads</b> — flattened and CSV-friendly, the view to export for outreach research</summary>

```json
{
  "name": "Williamston Pub & Grill",
  "category": "American Restaurant",
  "url": "https://www.facebook.com/williamstonpub",
  "contacts.emails": [
    "williamstonpub132@gmail.com"
  ],
  "contacts.phones": [
    "(517) 992-5060"
  ],
  "contacts.website": "http://www.williamstonpub.com/",
  "contacts.addressUrl": "https://www.bing.com/maps/…",
  "contacts.address.formatted": "132 W Grand River Ave, Williamston, MI, United States, 48895",
  "contacts.address.city": "Williamston",
  "contacts.address.country": "United States",
  "contacts.address.latitude": 42.689302673334,
  "contacts.address.longitude": -84.283966673334,
  "metrics.followers": 2500,
  "metrics.likes": 2573,
  "metrics.following": 24,
  "metrics.recommendPercent": 96,
  "metrics.reviewCount": 386,
  "isVerified": false,
  "business.priceRange": "$$",
  "business.services": [
    "Takeout",
    "Dine-in"
  ],
  "business.serviceAreas": [],
  "business.isRunningAds": false,
  "business.adLibraryUrl": "https://www.facebook.com/ads/library/?view_all_page_id=423350807792987",
  "business.creationDate": "November 20, 2013",
  "pageId": "100063657036934"
}
```

</details>

<details>
<summary><b>Websites</b> — business website enrichment results</summary>

```json
{
  "name": "Williamston Pub & Grill",
  "url": "https://www.facebook.com/williamstonpub",
  "contacts.website": "http://www.williamstonpub.com/",
  "websiteEnrichment": {
    "status": "ok",
    "finalUrl": "https://www.williamstonpub.com/",
    "domain": "williamstonpub.com",
    "title": "Williamston Pub & Grill - Williamston, MI",
    "description": "Welcome to Williamston Pub &amp; Grill, a cherished local gathering place in the heart of Williamston, MI. Since…",
    "emails": [],
    "phones": [],
    "socialLinks": {
      "facebook": "https://www.facebook.com/williamstonpub"
    },
    "pagesFetched": 1,
    "durationMs": 773,
    "usable": true
  }
}
```

</details>

<details>
<summary><b>Search matches</b> — which query found each Page, and how its detail was obtained</summary>

```json
{
  "name": "Williamston Pub & Grill",
  "url": "https://www.facebook.com/williamstonpub",
  "pageId": "100063657036934",
  "source": {
    "inputType": "search",
    "queriesMatched": [
      "Pub"
    ],
    "locationsMatched": [],
    "detailSource": "facebook-public-page",
    "searchProvider": "serper"
  }
}
```

</details>

#### Public contact data behaviour

- **Empty array means checked and none published. `null` means unknown or not checked.** That distinction is kept deliberately so you can tell "this business publishes no email" from "we could not look".
- **Contacts are never fabricated.** Every email, phone and address comes from what the Page itself publishes.
- **`metrics.rating` is always `null`, and that is Facebook, not a gap here.** Facebook retired 5-star Page ratings in favour of a recommend percentage — `recommendPercent` and `reviewCount` carry the real signal. A star rating is not derived from the percentage, because that would be inventing a number no source published.
- **`followersApproximate: true`** means Facebook published the follower count only in abbreviated form ("920K"), so the number is rounded. `likes` is exact.
- **`business.hours` is always `null`; `hoursStatus`** carries the open/closed status Facebook does publish on this surface.
- **`isRunningAds`, `adStatus`, `creationDate` and `adLibraryId` need "Add Page transparency details"**, in the Enrichment section. They come from a separate Facebook tab, so it is one extra request per Page and its own charged event; they stay `null` unless you switch it on.
- **`business.adLibraryId` is NOT the same number as `pageId`.** Facebook's Ad Library uses its own Page ID, so both are returned; `adLibraryUrl` links straight to that Page's ads.
- **`contacts.address.latitude` / `longitude`** are the business's own coordinates, for plotting leads on a map or filtering by radius. They are returned only when the Page carries exactly one location — an ambiguous page yields `null` rather than a coordinate that might belong to somewhere else.
- **`contacts.whatsapp`** is a separate channel from `contacts.phones` — a business may publish either or both, and for trades the WhatsApp number is often the one that gets answered.
- **`business.serviceAreas`** lists the areas a business will travel to ("Regina Beach, SK, Canada", "Moose Jaw, SK, Canada"), which is distinct from `contacts.address` — where they are based. Common on trades and service businesses.
- **`business.adminCountries` is always empty for now.** Facebook shows admin countries only on some Pages, and the field we can read on ordinary Pages contains explanatory boilerplate rather than a country list — so it is left empty rather than filled with a heading.
- **`contacts.website` is the business's real site; `contacts.websites` lists every link the Page publishes**, including social profiles.
- **`priceRange` currency symbols reflect the requesting region, not the business's.** Only the NUMBER of symbols is meaningful — use `priceTier` (1–4).

***

### Optional website enrichment

Switch on **Enrich business websites** and each Page that publishes a website has that site's home page plus up to two contact/about pages fetched, returning the resolved final URL, page title and meta description, any additional emails and phones published there, and social links.

- **Charged once per unique domain, not per Page.** A franchise with fifty Pages sharing one website is one charge.
- **Failed or empty enrichment is never charged.** A site that is unreachable, blocked or publishes nothing costs you nothing, and the Facebook Page row is returned regardless.
- Fetching is bounded: at most three pages per domain, with size and time caps, and private/internal addresses are refused.

***

### Incremental runs

Supply `skipPageIds` or `skipUrls` from a previous run. Skipped Pages are never fetched and never charged. Deduplication also runs within every run, so overlapping queries cost nothing extra.

***

### Pricing and charging

This Actor uses Apify's **pay-per-event** pricing. This README deliberately quotes no figures — live prices are on the Actor's Pricing tab, and a copy here would go stale.

| Event | Charged | Default |
|---|---|---|
| `page-result` | Once per unique Page returned with business data | always |
| `page-transparency` | Once per Page returned **with** transparency details (creation date, ad status, Ad Library ID) | **off** |
| `website-enrichment` | Once per unique website **domain** successfully enriched | **off** |

Both add-ons are off by default, so a standard run charges exactly one event per Page.

**Never charged:**

- duplicate Pages — one business found by two queries, or supplied directly *and* discovered, is charged once;
- Pages removed by any of your filters;
- Pages on your skip lists;
- Pages that were unavailable, deleted, not public or blocked;
- **Pages returned with no data at all** — no event ever fires for these. They are withheld from your results by default (see **Include unavailable Pages as empty rows**), and reported in the log and `RUN_SUMMARY` either way;
- failed or empty website enrichment;
- **transparency fetches for Pages you never receive.** The "currently running ads" filter has to fetch every Page's transparency tab before it can tell which ones qualify, so most of that work is discarded — and discarded work is free. Verified: a run that fetched 6 tabs and delivered 1 Page charged 2 events, not 6.

There is **no Actor-start fee and no rental fee**. A run that returns nothing charges nothing.

#### Your spending limit is respected

If you set a **maximum charge for the run**, this Actor stops when it is reached rather than continuing to work unpaid:

- Pages beyond what your limit can pay for are **not fetched further, not enriched, and not returned** — the limit is checked *before* the expensive enrichment stage, not discovered one charge at a time.
- Nothing beyond the limit is ever charged.
- The run finishes **successfully**, not as a failure. `RUN_SUMMARY.stopReason` reads `chargeLimitReached`, `pagesDroppedByChargeLimit` tells you how many Pages were left behind, and the status message says so.

Raise the limit and re-run to get the rest. Everything already paid for is in the dataset.

#### Free plan

Nothing in this Actor is restricted on the Apify Free plan — every feature, including keyword discovery and both add-ons, works exactly the same. Page detail comes from Facebook's own public pages and costs no third-party API credits, so there is no per-row upstream cost that would need gating.

***

### Proxy

#### 🚦 Proxy policy

Use **Apify Datacenter** proxy (the default) or **no proxy**. Both work reliably for public Facebook Pages at this Actor's concurrency — the gate on this surface is the request fingerprint, not the IP address.

**Apify Residential proxy is not supported. The run will fail at startup** if `apifyProxyGroups` includes `RESIDENTIAL`, before any request is made and before anything is charged.

Two reasons, and the second is why switching to residential would not help even if it were free:

- **Cost.** Residential bandwidth is billed to the Actor developer, not to your run. Facebook Page responses are large — about 1.67 GB per 1,000 Pages — which at residential rates costs several times what the results are priced at.
- **It buys nothing.** Facebook decides what to serve on the coherence of the request fingerprint, not on IP reputation. Residential IPs return the same data as datacenter ones.

If you genuinely need residential routing, supply your own provider through the proxy editor's **Custom proxy URLs** field. That traffic goes through your provider account, is unaffected by this policy, and is honoured in full:

```
http://user:pass@proxy.iproyal.com:12321
http://user:pass@proxy.brightdata.com:22225
http://user:pass@proxy.oxylabs.io:7777
```

***

### Availability and limitations

- Only **public** Page data is collected. Anything Facebook shows exclusively to logged-in users is out of reach by design.
- Some Pages are unavailable, deleted or geo-restricted. Those are never charged, and are withheld from your results unless you switch on **Include unavailable Pages as empty rows** — the count and reason are always reported.
- **Keyword discovery requires a search provider key** and reaches only indexed Pages. Direct Page URLs have neither limitation.
- Facebook changes its templates. `RUN_SUMMARY` includes `sectionSurface` and `parseYield`, which compare what was on the page against what was extracted, and the run warns loudly if a field stops parsing rather than letting a column quietly empty.

***

### Run summary

Every run writes a `RUN_SUMMARY` record to the key-value store: input counts, unique Pages resolved, duplicates collapsed, per-reason filter and unavailability breakdowns, contact-completeness counts, charged-event totals, an estimated bandwidth figure and upstream call accounting.

***

### Input examples

**1. Scrape Pages you already have** — the simplest run. No keys, no add-ons, one event per Page.

```json
{
  "startUrls": [
    "https://www.facebook.com/williamstonpub",
    "https://www.facebook.com/copperkettleyqr",
    "@russanddaughters"
  ],
  "maxResults": 100
}
```

**2. Find local businesses by keyword and location** — the lead-generation run.

```json
{
  "searchQueries": ["plumber", "emergency plumbing"],
  "locations": ["Sydney NSW", "Parramatta NSW"],
  "maxResults": 500,
  "minFollowers": 100
}
```

**3. Everything on** — discovery, both paid add-ons, filters, and an incremental skip list.

```json
{
  "searchQueries": ["dentist"],
  "locations": ["Regina SK", "Saskatoon SK"],
  "startUrls": ["https://www.facebook.com/somebusiness"],
  "maxResults": 500,
  "includeTransparency": true,
  "includeWebsiteEnrichment": true,
  "minFollowers": 250,
  "categories": ["dentist", "dental"],
  "verifiedOnly": false,
  "minRecommendPercent": 80,
  "skipPageIds": ["100063657036934"],
  "maxResultsPerQuery": 200
}
```

**4. Contact filters via the API** — `hasEmail`, `hasPhone` and `hasWebsite` are not in the input form, but still work when supplied programmatically.

```json
{
  "searchQueries": ["electrician"],
  "locations": ["Saskatoon SK"],
  "maxResults": 200,
  "hasEmail": true,
  "hasPhone": true
}
```

### Privacy and platform disclaimer

This Actor collects only publicly accessible Facebook Page/business information and public website information. You are responsible for complying with applicable privacy, anti-spam and data-protection law, and with platform terms, in your use of the results. Public availability does not automatically make every downstream use lawful. This Actor is not an outreach or bulk-messaging tool and must not be marketed or used as one.

# Actor input Schema

## `startUrls` (type: `array`):

Public Facebook Page URLs, @handles, or numeric Page IDs — one per line. Accepts every common form: https://www.facebook.com/burgerking, facebook.com/burgerking/about, m.facebook.com/burgerking, @burgerking, or a bare handle. Personal profiles, groups, posts and events are not Pages and are reported as invalid rather than scraped. Leave empty if you only want to discover Pages by keyword.

## `searchQueries` (type: `array`):

Business, category or niche keywords to discover Pages with — for example 'plumber' or 'vegan bakery'. Each keyword is combined with each location below, and results are shared fairly between them so one broad keyword cannot use up the whole result limit. Leave empty if you only want the Pages you listed above.

## `locations` (type: `array`):

Optional city, region or country context for the search queries — for example 'Sydney NSW'. Each query is combined with each location. Ignored when no search query is given: locations on their own are not a source of Pages.

## `maxResults` (type: `integer`):

Maximum number of unique Pages this run returns IN TOTAL — your direct Page URLs and every search combined, not per keyword and not search-only. The run stops as soon as this many unique Pages have been emitted. For a per-keyword cap, use "Max results per search query" under Advanced.

## `includeWebsiteEnrichment` (type: `boolean`):

For each Page that publishes a website, fetch that site's home page and a contact/about page to pull out the final resolved URL, page title and meta description, any extra emails and phone numbers published there, and social links. Charged as a separate 'website-enrichment' event, once per unique domain — two Pages sharing a domain are charged once. Adds runtime. Off by default.

## `includeUnavailableRows` (type: `boolean`):

Off by default. When a Page cannot be read — blocked, deleted, or not a business — it is left OUT of your results so your lead list stays clean. What was missed is still reported: the run log and status message give the count and reason, RUN\_SUMMARY breaks it down, and RETRY\_INPUT gives you the blocked-but-alive ones as an input you can re-run. Switch this on to get those Pages back as empty rows carrying a URL and an unavailableReason. Empty rows are never charged either way.

## `includeTransparency` (type: `boolean`):

Fetch each Page's transparency tab to add its creation date, current ad-running status, Ad Library ID and admin-country list. This costs ONE EXTRA REQUEST per Page and is charged as a separate 'page-transparency' event, once per Page actually returned — Pages removed by your filters or that could not be read are never charged. Switched on automatically when the 'currently running ads' filter is used, because that filter cannot be applied without it.

## `minFollowers` (type: `integer`):

Keep only Pages with at least this many followers. Pages whose follower count Facebook does not publish are KEPT, not dropped — an unknown count is not a low count. NOTE: Facebook publishes follower counts only in abbreviated form ("3.2K"), so this compares against a rounded number; `metrics.followersApproximate` tells you when that happened. Leave at 0 for no follower filter.

## `categories` (type: `array`):

Keep only Pages whose Facebook category matches one of these, case-insensitively — for example 'Plumber' or 'Bagel shop'. Partial matches count, so 'restaurant' keeps 'Italian restaurant'. Leave empty for no category filter.

## `verifiedOnly` (type: `boolean`):

Keep only Pages carrying Facebook's verified badge.

## `runningAdsOnly` (type: `boolean`):

Keep only Pages that Facebook's Page Transparency section reports as currently running ads. NOTE: this forces 'Add Page transparency details' on, so it costs an extra request per Page and charges the 'page-transparency' event for each Page returned. Pages whose ad status cannot be read are dropped by this filter, because keeping them would silently break the promise the filter makes.

## `minRecommendPercent` (type: `integer`):

Keep only Pages recommended by at least this percentage of reviewers. NOTE: Facebook retired 5-star Page ratings in favour of a recommend percentage, so this filters on 'X% recommend', not on stars. Pages with no reviews are KEPT — no reviews is not a bad score. Leave at 0 for no filter.

## `minReviewCount` (type: `integer`):

Keep only Pages with at least this many reviews. Pages that publish no review count are kept.

## `maxResultsPerQuery` (type: `integer`):

Safety cap on how many Pages any single search query may contribute, so one broad query cannot use up the whole global limit. 0 means no per-query cap — the global 'Max results' still applies.

## `skipPageIds` (type: `array`):

Numeric Facebook Page IDs to skip — for incremental runs where you already have these Pages. Skipped Pages are never fetched and never charged.

## `skipUrls` (type: `array`):

Page URLs or handles to skip, for incremental runs. Matched after canonicalization, so any URL form works. Skipped Pages are never fetched and never charged.

## `proxyConfiguration` (type: `object`):

Apify Datacenter proxy is used by default and is what this Actor is tuned for. Apify RESIDENTIAL proxy is not supported and is removed from this setting if selected — its bandwidth is billed to the Actor developer rather than to your run, and Facebook Page pages are large. If you need residential routing, supply your own provider through 'Custom proxy URLs'; that traffic goes through your account and is honoured in full.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.facebook.com/russanddaughters"
  ],
  "searchQueries": [],
  "locations": [],
  "maxResults": 100,
  "includeWebsiteEnrichment": false,
  "includeUnavailableRows": false,
  "includeTransparency": false,
  "minFollowers": 0,
  "categories": [],
  "verifiedOnly": false,
  "runningAdsOnly": false,
  "minRecommendPercent": 0,
  "minReviewCount": 0,
  "maxResultsPerQuery": 0,
  "skipPageIds": [],
  "skipUrls": [],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `pages` (type: `string`):

Complete normalized record for every Page returned, with nested metrics, contacts, business and social fields.

## `leads` (type: `string`):

Flat, CSV-friendly lead list: name, category, address, email, phone, website and followers.

## `websites` (type: `string`):

Business website enrichment results — resolved URL, title, description and any contacts published on the site.

## `searchMatches` (type: `string`):

Provenance: which query and location found each Page, and how its detail was obtained.

# 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 = {
    "startUrls": [
        "https://www.facebook.com/russanddaughters"
    ],
    "searchQueries": [],
    "locations": [],
    "maxResults": 100,
    "includeTransparency": false,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("coregent/facebook-pages-leads-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 = {
    "startUrls": ["https://www.facebook.com/russanddaughters"],
    "searchQueries": [],
    "locations": [],
    "maxResults": 100,
    "includeTransparency": False,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("coregent/facebook-pages-leads-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 '{
  "startUrls": [
    "https://www.facebook.com/russanddaughters"
  ],
  "searchQueries": [],
  "locations": [],
  "maxResults": 100,
  "includeTransparency": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call coregent/facebook-pages-leads-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,coregent/facebook-pages-leads-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/PRjk6Ca25sxLLdpoj/builds/ZMO9pVnOuquO4l8Xn/openapi.json
