# Facebook Page Review & Rating Scraper (`fanndev/facebook-page-reviews-scraper`) Actor

Get a Facebook business page's recommendation rating and its individual reviews: percent who recommend, total review count, and per review the author, the recommend vote, the written text, Facebook's experience tags, the timestamp and the engagement. Missing pages are reported as errors.

- **URL**: https://apify.com/fanndev/facebook-page-reviews-scraper.md
- **Developed by:** [Faisal Ahdan naufal](https://apify.com/fanndev) (community)
- **Security:** Limited permissions
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.30 / 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 Page Review & Rating Scraper

Get a Facebook business page's rating and the reviews behind it: the headline
percentage, the total review count, and for each review the author, the
recommend / do-not-recommend vote, the written text, Facebook's own experience
tags, the timestamp and the engagement.

No login, no cookies to paste, no proxy required.

### The rating is a percentage, not stars

Facebook retired star ratings for pages. What a page has now is a recommendation
vote, and its summary line looks like this:

> `30% recommend (12,651 reviews)`

So `recommendPercent: 30.0` and `reviewCount: 12651` **are** the rating. There is
no 4.3-out-of-5 hidden in the payload, and this actor does not manufacture one
from the percentage.

### What you get

**One `PAGE_RATING` row per page**

| Field | Example |
| --- | --- |
| `ratingSummaryText` | `30% recommend (12,651 reviews)` |
| `recommendPercent` | `30.0` |
| `reviewCount` | `12651` |
| `reviewsEnabled` | `true` |
| `reviewsCollected` | how many this run actually kept |

**One `REVIEW` row per review**

| Field | Example |
| --- | --- |
| `reviewerName`, `reviewerId`, `reviewerUrl` | `Mike Phipps`, … |
| `isRecommended` | `false` |
| `reviewTitle` | `Mike Phipps doesn't recommend Domino's Pizza.` |
| `reviewText` | `Every time I order, there is something that goes wrong…` |
| `tags` | `["Disappointing food", "Casual"]` |
| `createdAtIso` | `2026-09-20T00:03:50Z` |
| `reactionCount`, `commentCount` | engagement on the review itself |
| `reviewUrl` | permalink |

Plus a `RUN_SUMMARY`: reviews collected, how many recommend, how many do not,
how many carry written text, and the sample's own recommend percentage next to
the page's official one.

### Input example

```json
{
  "pages": ["dominos", "https://www.facebook.com/starbucks"],
  "maxReviewsPerPage": 100,
  "onlyNotRecommended": true,
  "onlyWithText": true,
  "exportFormats": ["xlsx"]
}
```

That is the complaints workflow: every negative review with text, for two pages,
in a spreadsheet.

### Pacing

Facebook streams the reviews tab about **three reviews per request**. 100 reviews
is roughly 34 requests, so set `maxReviewsPerPage` to what you actually need. The
rating summary is read in the very first request and is returned no matter how
low you set the cap.

### Honest limits

- `isRecommended` is read from Facebook's own sentence ("… doesn't recommend …"),
  because the payload carries no boolean for it. The actor requests `en-US` so
  that sentence is in English; if it ever comes back in another language the
  vote is reported as `null` rather than guessed.
- A page with reviews switched off returns `reviewsEnabled: false` and no review
  rows. That is the real answer, not a failure.
- A slug that does not exist returns an `ERROR` row saying so. Facebook answers
  HTTP 200 for missing pages, so a status-code check would report every typo as a
  successful empty scrape; this actor checks the payload instead.
- Replies from the business are comments on the review post. The `commentCount`
  includes them; their text is not collected.
- `reviewerId` is an opaque `pfbid…` token for newer accounts. That is
  Facebook's own identifier format.

### Running it on Apify: use a residential proxy for pagination

Facebook serves the **rendered first page** to any IP, including Apify's. But the
**first pagination request from a datacenter IP** comes back with
`Rate limit exceeded`, so a platform run with no proxy stops at the first few reviews.

The rate limit is on Facebook's GraphQL endpoint, which every actor in this
family uses for its second page onwards. It was measured on 2026-09-20 with the
Ad Library actor, three runs of the same search within a minute:

| Run | Result |
| --- | --- |
| Apify, no proxy | 30 results, 1 page — log: `Rate limit exceeded` |
| Apify, `RESIDENTIAL` proxy | 60 results, 4 pages |
| Local machine, no proxy | 70 results, 5 pages |

So: switch the Apify proxy on and pick the **RESIDENTIAL** group whenever you
want more than the first page. Running from your own machine needs no proxy at
all.

The actor logs a warning naming the rate limit when it hits one, so a short run
is never silently mistaken for a short result set.

# Actor input Schema

## `pages` (type: `array`):

One page per line: a vanity slug ('dominos') or a full Facebook page URL. Both forms reduce to the same page.

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

Page URLs, for pasting a list straight out of another actor's dataset.

## `maxReviewsPerPage` (type: `integer`):

Facebook streams the reviews tab about three reviews at a time, so 100 reviews costs roughly 34 requests. The rating summary (percent recommend and total review count) is read in the very first request and is returned whatever this is set to.

## `onlyRecommended` (type: `boolean`):

Keep only reviews that recommend the page.

## `onlyNotRecommended` (type: `boolean`):

Keep only reviews that do not recommend the page - the complaints view. Cannot be combined with 'Only positive reviews'.

## `onlyWithText` (type: `boolean`):

Drop votes that carry no written review.

## `textContains` (type: `string`):

Case-insensitive substring match against the review body.

## `postedAfter` (type: `string`):

YYYY-MM-DD. Reviews with no timestamp are dropped.

## `emitSummary` (type: `boolean`):

Append one RUN_SUMMARY record: reviews collected, how many recommend, how many do not, and how many carry written text.

## `exportFormats` (type: `array`):

Also write the results to the key-value store in these formats. The dataset is always produced regardless.

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

Recommended on the Apify platform if you want more than the first few reviews. Facebook serves the rendered first page fine from any IP, but on Apify's datacenter IPs it answers the very first pagination request with "Rate limit exceeded" - measured 2026-09-20. Switching the Apify proxy on with the RESIDENTIAL group restored full pagination in the same test (60 ads over 4 pages against 30). Running from your own machine, no proxy is needed at all. The actor logs a warning naming this when it happens, so a short run is never silently mistaken for a short result set.

## Actor input object example

```json
{
  "pages": [
    "dominos"
  ],
  "maxReviewsPerPage": 50,
  "onlyRecommended": false,
  "onlyNotRecommended": false,
  "onlyWithText": false,
  "emitSummary": true,
  "exportFormats": [],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

Rating summary and individual reviews per page, plus the run summary and error rows.

# 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 = {
    "pages": [
        "dominos"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("fanndev/facebook-page-reviews-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 = { "pages": ["dominos"] }

# Run the Actor and wait for it to finish
run = client.actor("fanndev/facebook-page-reviews-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 '{
  "pages": [
    "dominos"
  ]
}' |
apify call fanndev/facebook-page-reviews-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,fanndev/facebook-page-reviews-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/3aJMH4bkvhSUZJALV/builds/BXrUDwC3iPr9XN6lz/openapi.json
