# Facebook Pages & Contacts — Search by Keyword & City, No Login (`memo23/facebook-pages-and-contacts-scraper`) Actor

Search Facebook itself for pages by keyword and city, and get each page's About-tab contact details in the same run: email, phone, address, website, social handles, open/closed status, price range, rating, exact likes, verified badge. No login, no cookies, no browser.

- **URL**: https://apify.com/memo23/facebook-pages-and-contacts-scraper.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** Lead generation, Social media
- **Stats:** 186 total users, 81 monthly users, 69.0% runs succeeded, 3 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.40 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

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

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Facebook Pages & Contacts Scraper

Search Facebook itself for pages by keyword and city, and get each page's About-tab contact details in the same run — email, phone, address, website, social handles, open/closed status, price range, rating, exact likes and verified badge. No login, no cookies, no browser.

### Why Use This Scraper?

- **Facebook's own search, not Google's.** Results come from `facebook.com/search`, the same list a Facebook user sees. Actors built on a Google `site:facebook.com` query return whatever Google indexed, go dark when Google changes a page, and cannot filter by city.
- **A city filter that works.** `"Kansas City, Missouri"` is resolved to Facebook's own place before anything is charged, and the search is filtered on it. Measured: 12 of 12 results for two trade keywords carried 816 area codes.
- **Contacts in the same run.** Each page's About tab is read for email, phone, address, website, Instagram and other handles, hours status, price range and rating — the fields most page scrapers leave blank because they read the page root instead.
- **No cookies.** The previous version of this actor required your own logged-in Facebook cookies. Both halves now run as a logged-out guest.
- **Exact likes.** `1275`, not `1.2K`, from the page head.
- **Failures say why.** An unknown city ends the run with a message rather than unfiltered rows; a page whose About tab cannot be read still returns its search result with `detailsError` set.

### Overview

One row per page found. Two keywords at `maxPages: 20` give up to 40 rows, each carrying `searchQuery` and `searchLocation` so a batch stays attributable.

### Supported Inputs

| Input | Example |
|---|---|
| Keyword | `siding and windows` |
| Keyword + city | `dentist` with `location: "Manchester, New Hampshire"` |
| Facebook place UID | `locationUid: "108591349161413"` — skips the city lookup |
| Legacy search URL | `https://www.facebook.com/search/pages/?q=roofing` in `startUrls` |

### Use Cases

- **Local lead lists** — every roofer, dentist or gym Facebook knows in a city, with phone and email.
- **Territory research** — page count, rating and likes per keyword per city.
- **Agency prospecting** — pages with no website listed, or no email, in a given trade and town.
- **Social graph** — the Instagram and TikTok handles local businesses list on Facebook.

### How It Works

A guest session is opened on Facebook and the city, if given, is resolved to a Facebook place id through Facebook's own typeahead. Each keyword is then searched as `pages` with that place as a filter, ten results per request, following the cursor until `maxPages` is reached. For every page found, its About tab is fetched as a stream and cut the moment the contact section has arrived — about 100 KB per page instead of the 2 MB the full tab weighs — and read from Facebook's `field_type` enum, which is stable across languages. The page head is fetched for the profile picture and exact like count.

Pure HTTP over a residential proxy. No browser, no login.

### Input Configuration

| Field | Type | Default | Notes |
|---|---|---|---|
| `searchQueries` | array | — | Keywords, one per line. Facebook search URLs accepted. |
| `location` | string | — | City, ideally with state or country. Resolved to a Facebook place before charging. |
| `locationUid` | string | — | Facebook place id, from `facebook.com/places/<uid>/`. Skips the lookup. |
| `includePageDetails` | boolean | `true` | Fetch the About tab for each page. Off = search results only. |
| `maxPages` | integer | `20` | Pages per keyword. |
| `language` | string | `en-US` | `Accept-Language`. Changes localised free text such as category names, not what is extracted. |
| `startUrls` | array | — | Legacy: search URLs. Merged with `searchQueries`. |
| `proxy` | object | — | Non-paying sample runs only. Paying runs use a built-in residential proxy. |

#### Example input

```json
{
  "searchQueries": ["siding and windows", "roofing"],
  "location": "Kansas City, Missouri",
  "maxPages": 20
}
```

### Output Overview

One flat row per page, 36 fields when details are on. Search-result fields are always present; About-tab fields are `null` with `detailsError` set when the tab could not be read.

### Output Sample

A real row from a live run:

```json
{
  "searchQuery": "roofing",
  "searchLocation": "Kansas City, Missouri",
  "pageName": "Collins Roofing",
  "pageUrl": "https://www.facebook.com/CollinsRoofing",
  "pageLink": "https://www.facebook.com/CollinsRoofing",
  "pageId": "100063613988618",
  "isVerified": false,
  "category": "Roofing Service",
  "tag": null,
  "subtitle": null,
  "description": "Collins Roofing is a full service roofing company. 40+ Years Experience Lifetime Warranties Availabl",
  "locationDescriptor": null,
  "photoURL": "https://scontent.fupn3-1.fna.fbcdn.net/v/t39.30808-1/696846527_1568020308661754_827356433326780853_n.jpg?…",
  "profilePictureUrl": "https://scontent.fupn3-1.fna.fbcdn.net/v/t39.30808-1/696846527_1568020308661754_827356433326780853_n.jpg?…",
  "scrapedAt": "2026-09-15T11:45:34.738Z",
  "email": "collinsroofingkc@yahoo.com",
  "phone": "+1 816-252-3088",
  "address": "501 Nw 5th St, Blue Springs, MO, United States, 64014",
  "website": "http://www.collinsroofingkc.com/",
  "websites": [],
  "socialLinks": [],
  "instagram": null,
  "hoursStatus": "Closed now",
  "priceRange": "$$",
  "rating": "98% recommend (34 reviews)",
  "ratingPercent": 98,
  "reviewCount": 34,
  "likes": 1275,
  "talkingAbout": 52,
  "wereHere": 120,
  "followers": "1.2K followers",
  "following": "22 following",
  "intro": "Collins Roofing is a full service roofing company. \n40+ Years Experience\nLifetime Warranties Availabl",
  "fieldsFound": [
    "category",
    "address",
    "profile_phone",
    "profile_email",
    "website",
    "ratings",
    "business_hours",
    "business_price"
  ],
  "detailsError": null,
  "pageDetails": {
    "Category": "Roofing Service",
    "address": "501 Nw 5th St, Blue Springs, MO, United States, 64014",
    "Mobile": "+1 816-252-3088",
    "Email": "collinsroofingkc@yahoo.com",
    "Website": "http://www.collinsroofingkc.com/",
    "Ratings": "98% recommend (34 reviews)",
    "Hours": "Closed now",
    "Price Range": "$$"
  }
}
```

### Key Output Fields

| Field | Notes |
|---|---|
| `searchQuery` / `searchLocation` | Which keyword and resolved city produced this row. |
| `pageName` / `pageUrl` / `pageId` | The page as Facebook lists it in search. |
| `category` / `subtitle` / `description` / `locationDescriptor` | From the search result. |
| `email` / `phone` / `address` / `website` / `websites` | From the About tab's contact section. `null` where the page lists none. |
| `socialLinks` / `instagram` | Platform, handle and a built URL for each social handle listed. |
| `hoursStatus` | `Open now` / `Closed now` / `Always open`. The per-day schedule is not served to a logged-out client and is not guessed. |
| `priceRange` / `rating` / `ratingPercent` / `reviewCount` | `$$`, `98% recommend (34 reviews)` and its parts. |
| `likes` / `talkingAbout` / `wereHere` | **Exact** counts from the page head. |
| `followers` / `following` | Facebook's rounded labels. |
| `isVerified` / `intro` / `profilePictureUrl` | Badge, intro line, signed CDN image link. |
| `fieldsFound` | Which contact field types the page rendered — distinguishes "not listed" from "not extracted". |
| `detailsError` | `null` on success; `not_found`, `login_wall`, `no_about_tab` or `blocked` when the About tab could not be read. The search-result half of the row is still delivered. |
| `pageDetails` | The nested shape the previous version emitted, kept so an existing pipeline keeps working. |
| `error` / `message` | Present only on a query-level failure row: `no_results`, `search_failed`. |

### FAQ

**Do I need a Facebook account or cookies?**
No. Both the search and the About tab are read as a logged-out guest. Every figure in this README came from such a run.

**Why do I get pages from outside my city?**
The filter is Facebook's own, applied to Facebook's own search, and it is not perfect — a multi-location business or one that tagged the wrong place can appear. `address` tells you where each page actually is.

**Why does one row have `detailsError: "not_found"`?**
The search result linked to a page whose About tab Facebook does not serve to a guest — usually a personal profile or a page that has since been renamed. The search fields for that row are still there.

**Can I get opening hours by day?**
Not logged out. Facebook renders only the current open/closed status server-side.

**How is this different from the Facebook Search Scraper?**
That one returns search results across every type — people, pages, posts, places — and stops there. This one searches pages only and enriches each with its About tab in the same run.

### Support

Open an issue on the Actor's Issues tab. Bug reports with a run id, the keyword and the city get looked at first.

### Explore More Scrapers

- **[Facebook Page Contact Details](https://apify.com/memo23/facebook-page-contact-detail-scraper)** — the same About-tab details for page URLs you already have
- **[Facebook Search Scraper](https://apify.com/memo23/facebook-search-scraper)** — people, pages, posts, places and events by keyword
- **[Facebook Reels Scraper](https://apify.com/memo23/facebook-reels-scraper)** — every reel on a page with play counts
- **[Facebook Comments Scraper](https://apify.com/memo23/facebook-comments-scraper)** — comment threads on any public post

### 🤖 For AI Agents & LLM Apps

**Purpose:** Find Facebook pages by keyword and city using Facebook's own search, and return each page's contact details — email, phone, address, website, social handles, hours status, price range, rating, exact likes, verified badge — in one row.

**Minimal tested input:**

```json
{ "searchQueries": ["roofing"], "location": "Kansas City, Missouri", "maxPages": 10 }
```

**Output:** array of flat objects, one per page. Fields: `searchQuery`, `searchLocation`, `pageName`, `pageUrl`, `pageId`, `isVerified`, `category`, `subtitle`, `description`, `locationDescriptor`, `profilePictureUrl`, `email`, `phone`, `address`, `website`, `websites`, `socialLinks`, `instagram`, `hoursStatus`, `priceRange`, `rating`, `ratingPercent`, `reviewCount`, `likes`, `talkingAbout`, `wereHere`, `followers`, `following`, `intro`, `fieldsFound`, `detailsError`, `pageDetails`, `scrapedAt`, plus legacy aliases `pageLink`, `tag`, `photoURL`.

**Agent-relevant behaviour:** a null contact field means the page does not list it — check `fieldsFound`. `detailsError` non-null means the search half is present but the About tab was unreadable; do not treat it as a missing page. `likes` is exact, `followers` is rounded. An unknown `location` ends the run with `exitCode 0` and a status message rather than returning unfiltered rows. `pageDetails` duplicates the flat contact fields in the older nested shape.

**Billing:** per delivered row. Query-level failure rows are free.

### ⚠️ Disclaimer

This Actor reads only publicly available Facebook page information — no login, no cookies, no private content. You are responsible for using the output lawfully, including under the GDPR, the CCPA and Facebook's terms where they apply to you. Nothing here is legal advice.

### SEO Keywords

facebook page search scraper, facebook pages by keyword, facebook business search by city, facebook local business scraper, facebook page contact scraper, facebook page email phone, facebook lead generation, facebook business leads by location, facebook pages with contact details, facebook search pages contacts, facebook page finder, facebook business directory scraper, facebook about tab scraper, facebook page scraper no login, apify facebook pages contacts

# Actor input Schema

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

What to search for, one keyword per line — e.g. "wood fired pizza", "dentist", "siding contractor". This is Facebook's own page search, not a Google query, so results are what a Facebook user would see. A Facebook search URL (facebook.com/search/pages/?q=…) is accepted too.

## `location` (type: `string`):

Optional. Restrict results to a city — "Kansas City, Missouri", "Manchester, New Hampshire", "London, UK". Add the state or country for common names. Resolved to Facebook's own place before anything is charged; an unknown city ends the run with a clear message rather than unfiltered rows.

## `locationUid` (type: `string`):

Optional. Facebook's numeric place id, from facebook.com/places/<uid>/. Skips the city lookup when set.

## `includePageDetails` (type: `boolean`):

Fetch each page's About tab and add email, phone, address, website, social handles, open/closed status, price range, rating, exact likes and verified badge to the row. Off = search results only (name, url, category, description), which is faster and cheaper.

## `maxPages` (type: `integer`):

How many pages to return for EACH keyword. Facebook serves 10 per request; the run follows the cursor until it has this many or results run out. Default 20.

## `language` (type: `string`):

Accept-Language for the requests. Contact extraction is locale-independent; this changes only free-text values Facebook localises, such as the category name.

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

Older way to give the keyword: Facebook search URLs such as https://www.facebook.com/search/pages/?q=medical. Still accepted; merged with Search keywords.

## `proxy` (type: `object`):

Nothing to configure: every run uses a built-in residential proxy, because Facebook serves a login wall to datacenter IPs. On a free sample run, your own proxy URLs or Apify RESIDENTIAL proxy are used instead if you set them here.

## Actor input object example

```json
{
  "searchQueries": [
    "siding and windows"
  ],
  "location": "Kansas City, Missouri",
  "includePageDetails": true,
  "maxPages": 20,
  "language": "en-US",
  "proxy": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `contacts` (type: `string`):

All pages in the contacts view.

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

All pages in the search results view.

## `results` (type: `string`):

Every row with every field.

# 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 = {
    "searchQueries": [
        "siding and windows"
    ],
    "location": "Kansas City, Missouri"
};

// Run the Actor and wait for it to finish
const run = await client.actor("memo23/facebook-pages-and-contacts-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 = {
    "searchQueries": ["siding and windows"],
    "location": "Kansas City, Missouri",
}

# Run the Actor and wait for it to finish
run = client.actor("memo23/facebook-pages-and-contacts-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 '{
  "searchQueries": [
    "siding and windows"
  ],
  "location": "Kansas City, Missouri"
}' |
apify call memo23/facebook-pages-and-contacts-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,memo23/facebook-pages-and-contacts-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/Bal41kh6y2QswJeZn/builds/k4Qd3oImXNl8Egm3i/openapi.json
