# Facebook Reviews Scraper - $0.35 per 1,000 Reviews (`dami_studio/facebook-reviews-scraper`) Actor

Stars are gone from Facebook Pages; what a Page carries now is Recommendations, yes or no, plus a percentage. One row per review: reviewer, recommends or not, text, date, reactions, tags and photos. No login needed from you. $0.35 per 1,000.

- **URL**: https://apify.com/dami\_studio/facebook-reviews-scraper.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.35 / 1,000 review scrapeds

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 Reviews Scraper

Paste public Facebook Page URLs and get their Recommendations back, one row per review: who wrote it, whether they recommend the Page, the review text, the exact date, the reaction and comment counts, the recommendation tags Facebook attaches, and any photos the reviewer posted with it.

You never sign in and you never paste a cookie. See [About logging in](#about-logging-in) for what the run actually does.

- Many Pages in one run. Each review is its own flat row, with the Page it belongs to on it.
- Positive and negative are read from the recommendation marker itself rather than guessed from the wording, so the flag stays right on a Page that renders in another language.
- Every row also carries the Page-level summary: what percentage of readers recommend it and how many reviews there are in total.
- Date and positive/negative filters are free. Anything filtered out is dropped before it's charged.
- Empty input returns one labelled sample row, free.

### Price

**$0.35 per 1,000 reviews.** Plus **$0.0005 per run** as the platform start fee, billed per gigabyte of run memory, so exactly that on the default 1 GB.

No volume tiers, no minimum spend, no subscription.

| Reviews | Total |
|---|---|
| 100 | $0.0355 |
| 1,000 | $0.3505 |
| 10,000 | $3.5005 |
| 100,000 | $35.0005 |

#### What gets charged

- One `review-scraped` event per review row written to the dataset. Nothing else is metered per row.
- Free: the sample row an empty run returns, and every diagnostic row. They all carry `"charged": false`.
- Reviews dropped by the date filter or the positive/negative filter are never charged.
- A Page that has switched Recommendations off returns one free diagnostic row, not an invoice.
- A run that finds nothing costs the start fee and nothing else.
- Rows never leave the dataset without a charge and are never charged without a row. The billed event is a named one, so nothing is quietly attached to `apify-default-dataset-item`.

### Input

```json
{
  "startUrls": [
    "https://www.facebook.com/Dominos",
    "https://www.facebook.com/KFC"
  ],
  "resultsLimit": 10,
  "recommendationType": "all"
}
```

| Field | What it does |
|---|---|
| `startUrls` | Public Facebook Page URLs, one per line. A bare handle (`Dominos`), a Reviews-tab URL, a `/pg/` URL, a `/p/` URL and a `profile.php?id=` URL all work. Up to 50 Pages per run. |
| `resultsLimit` | How many recent reviews to take from each Page, newest first. Default 10, maximum 200. Keep it low while testing, since you pay per review. |
| `recommendationType` | `all` (default), `positive` to keep only "recommends", or `negative` to keep only "doesn't recommend". Filtering is free. |
| `onlyReviewsNewerThan` | Drop reviews older than this. Accepts `2026-08-01`, a full ISO timestamp, an epoch, or a relative window like `7 days`. Free. |
| `onlyReviewsOlderThan` | Drop reviews newer than this. Same formats. Also free. |
| `proxyUrls` | Leave empty. Only for callers who want traffic to leave through proxy servers they already pay for, as `http://user:pass@host:port`. |
| `sessionCookies` | Leave empty unless you're running heavily. See [About logging in](#about-logging-in). |

Run it with empty input and you get one labelled sample row, free.

### Output

One row per review. This is a real row from a real run:

```json
{
  "ok": true,
  "charged": true,
  "recordType": "review",
  "inputUrl": "https://www.facebook.com/Dominos",
  "pageName": "Domino's Pizza",
  "pageId": "100064795448235",
  "pageUrl": "https://www.facebook.com/Dominos/",
  "pageIsVerified": true,
  "pageProfilePicture": "https://lookaside.fbsbx.com/lookaside/crawler/media/?media_id=100064795448235",
  "pageRecommendPercent": 30,
  "pageReviewsCount": 12661,
  "pageRatingSummary": "30% recommend (12,661 Reviews)",
  "reviewId": "10164211035477496",
  "reviewUrl": "https://www.facebook.com/randyfgarciaLV/posts/pfbid05YabRqiEtRu7e4Lqm1uDpYtjWhzTAEJj4DCemp6BywqxnkyjDAh9aFXCKMKE3Aujl",
  "isRecommended": true,
  "recommendation": "positive",
  "text": "Nice place. I would come back for more of that sweet pizza anytime.",
  "textLength": 67,
  "tags": ["Fast delivery", "Child-friendly"],
  "date": "2026-08-09T09:19:05.000Z",
  "timestamp": 1786267145,
  "reviewerName": "Randy F Garcia",
  "reviewerId": "562052495",
  "reviewerUrl": "https://www.facebook.com/randyfgarciaLV",
  "reviewerProfilePicture": "https://lookaside.fbsbx.com/lookaside/crawler/media/?media_id=562052495",
  "likesCount": 0,
  "commentsCount": 1,
  "sharesCount": 0,
  "photos": [],
  "photoCount": 0,
  "scrapedAt": "2026-08-16T13:33:50.062Z"
}
```

#### Field notes

- `isRecommended` is true when the reviewer recommends the Page and false when they don't.
- `recommendation` is the same fact as a string, `"positive"` or `"negative"`, for spreadsheets and pivot tables.
- `pageRecommendPercent` is the Page-level figure Facebook shows in place of an average rating: the share of reviewers who recommend it. Null on a Page Facebook labels "Not yet rated".
- `pageReviewsCount` is how many reviews the Page has in total, which is almost always far more than one run fetches.
- `tags` are the chips Facebook offers a reviewer alongside the text, like "Fast delivery" or "Poor presentation". Empty on most reviews, because most reviewers pick none.
- `photos` are direct links to images and video stills attached to the review. Empty on the majority. Roughly one review in sixteen carries media.
- `likesCount` counts reactions on the review itself, not on the Page. Most reviews sit at zero.
- `reviewerId` is an opaque `pfbid…` identifier for most profiles and a numeric one for older accounts. Both come back as strings, exactly as the platform returns them.
- `reviewUrl` points at the reviewer's own profile, because a review is a post there. It's the canonical permalink for that review.
- `date` is normalised to ISO 8601 UTC. `timestamp` is the same instant in epoch seconds.

Real rows carry `"charged": true`. Sample rows carry `"_sample": true`, diagnostic rows carry `"_diagnostic": true` and an `errorCode`. Neither is billed.

### How it works

It reads the Page's public Recommendations tab the way an indexing crawler does and parses the structured data the page already ships. Nothing is rendered and no headless browser starts.

Reviews past the first render are followed through the tab's own paging cursor, three at a time on the same connection, so depth costs time rather than extra page loads. Requests leave through a rotating pool of addresses, so a per-address rate limit is answered by moving instead of waiting.

Positive and negative come from the structured marker Facebook attaches to each recommendation rather than from the English sentence.

### About logging in

You never supply a Facebook account and you never paste a cookie. Reads go out logged out, and that's what answers a normal run.

When Facebook answers with a login interstitial on address after address, the run continues with a Facebook session that ships inside the Actor. That session is shared by everyone running this Actor, and Facebook rate-limits per account rather than per caller, so under heavy load it's a small shared budget.

If you'd rather that fallback used your own account, paste your cookie into `sessionCookies` as `c_user=<value>; xs=<value>`, one line per account. Treat those values like a password. Anyone holding them can act as that account, and Facebook may sign the session out or restrict an account it decides is automated.

### Recommendations, not stars

Facebook retired 1-5 star ratings for Pages in 2018. What a Page has now is Recommendations: each reader either recommends it or doesn't, and writes free text underneath. There's no per-review star value stored anywhere, so this Actor doesn't publish one. A `stars: 4` column here would be a number somebody made up.

What you get instead:

- `isRecommended`, a clean boolean per review, plus `recommendation` as `"positive"` or `"negative"`.
- `pageRecommendPercent`, the Page-level share of reviewers who recommend it. This is the figure Facebook itself shows in place of an average.
- `pageReviewsCount`, the total number of reviews the Page has.

If you need a star-shaped number for an existing dashboard, `pageRecommendPercent / 20` gives you a 0-5 scale from a real figure rather than an invented one.

### What people use it for

- Reputation monitoring for a brand with many location Pages. Schedule the same list daily, filter on `recommendation == "negative"`, route the new ones to whoever answers complaints.
- Competitive research. Pull recommendations for a set of Pages in one run and compare `pageRecommendPercent` and the language people actually use.
- Feeding a review widget or testimonials page with real, attributed, dated quotes instead of hand-copied ones.
- Support triage. Negative reviews arrive with a permalink and the reviewer's name, so a reply can be written straight from the row.
- Product analytics. `tags` plus the free text is a labelled dataset of what customers praise and complain about.
- Due diligence on a supplier, franchise or venue. Total review count, percentage recommending, and the twenty most recent opinions in one run.

### Reading the output

- **Real rows** carry `"charged": true` and `"recordType": "review"`. One billed event each.
- **The sample row** carries `"_sample": true` and `"charged": false`. There's exactly one, only when the input had no Page URLs.
- **Diagnostic rows** carry `"_diagnostic": true`, `"charged": false` and an `errorCode`: `REVIEWS_DISABLED` when the Page has no public Recommendations tab, `NO_REVIEWS` when the tab exists but is empty or everything fell outside your filters, `NOT_FOUND` when no Page lives at that URL, `BAD_INPUT` when the URL isn't a Facebook Page, `BLOCKED` when Facebook answered with a login interstitial from every address tried, `NETWORK` when it couldn't be reached, `TIME_BUDGET` when time ran out. Each carries a plain-English `details` and the `inputUrl` it belongs to.

Filter on `charged == true` and you have exactly the rows you paid for.

### Limits

- Reviews are optional on Facebook. A Page owner can switch Recommendations off, and a lot of large brand Pages have. Those Pages return a free `REVIEWS_DISABLED` row rather than reviews. It's a property of the Page, not a failure of the run, and nothing is charged for it.
- There's no 1-5 star rating to return.
- Only what a logged-out reader can see is returned. Reviews restricted by the reviewer's own privacy settings are invisible without an account.
- Reviews come newest first, and the depth available per Page is finite. Asking for 200 on a Page that has published forty publicly returns forty.
- Reaction, comment and share counts are a snapshot at read time.
- Most reviews carry no photos and no tags, because most reviewers add neither. Those fields are an empty array rather than null.
- Photo links are Facebook CDN URLs. The stable `lookaside` form keeps working. The signed `scontent` form expires after some hours, so download anything you need to keep rather than storing the link.
- A reviewer profile URL is only published when Facebook publishes one. Where it doesn't, the link is derived from the review permalink, which points at the same profile.
- Hard ceilings: 50 Pages per run and 200 reviews per Page.
- Facebook rate-limits this surface per address. The run answers by moving to another address, but a very large single run can still take a few minutes.

### Questions

**Do I need a Facebook account, a cookie, or a token?**

No. You supply nothing. Everything comes from what Facebook serves a logged-out reader, which is also why reviews a reviewer has made private don't appear.

**Why does one of my Pages return no reviews?**

Almost always because that Page has Recommendations switched off. It's a setting the Page owner controls, and many big brand Pages disable it. You get one free `REVIEWS_DISABLED` row and no charge for that Page.

**Where is the star rating?**

It doesn't exist any more. Facebook replaced 1-5 stars for Pages in 2018 with recommend / doesn't recommend, so every row carries `isRecommended` instead, and `pageRecommendPercent` takes the place of an average.

**Can I get only the bad reviews?**

Yes. Set `recommendationType` to `negative`. The filter runs before billing, so the positive reviews that get dropped cost nothing.

**Will the run fail if something goes wrong?**

No. A blocked, empty or missing Page produces an uncharged diagnostic row and the run still finishes as succeeded. A failed run would still bill the start fee, which would mean paying to be told something went wrong.

**Can I run it on a schedule?**

Yes. Nothing is held between runs. Use `reviewId` to work out which rows are new since last time.

# Actor input Schema

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

Public Facebook Page URLs, one per line. A bare handle (Dominos), a Reviews-tab URL, a /pg/ URL, a /p/ URL and a profile.php?id= URL all work. Up to 50 Pages per run. Reviews are optional on Facebook: a Page whose owner switched Recommendations off returns a free diagnostic row instead of reviews.

## `resultsLimit` (type: `integer`):

How many recent reviews to take from each Page, newest first. Default 10, maximum 200. The first one to three arrive in the page render itself; past that the Actor follows the Recommendations tab's own paging cursor, three reviews at a time, so depth costs time rather than extra page loads. Keep it low while testing - you pay per review.

## `recommendationType` (type: `string`):

Optional. Keep only positive recommendations, only negative ones, or all of them. Filtering is free - the reviews you drop are never charged.

## `onlyReviewsNewerThan` (type: `string`):

Optional. Drop reviews older than this. Accepts 2026-08-01, a full ISO timestamp, an epoch, or a relative window such as "7 days". Filtering is free - dropped reviews are never charged.

## `onlyReviewsOlderThan` (type: `string`):

Optional. Drop reviews newer than this. Same formats as above. Also free.

## `proxyUrls` (type: `array`):

Leave this empty. By default the run rotates a large pool of addresses that cost you nothing per gigabyte. Fill it in only if you specifically want the traffic to leave through proxy servers you already pay for, in the form http://user:pass@host:port.

## `sessionCookies` (type: `array`):

Leave this empty unless you need it. Runs are logged out by default and that is enough for public Facebook content. Facebook shows some things only to a signed-in account, and it limits how fast any one account may read; supplying your own cookie uses your account and your own rate limit, shared with nobody. In Chrome: open facebook.com while signed in, press F12, then Application > Cookies > https://www.facebook.com, and paste the values as "c\_user=<value>; xs=<value>". One line per account. Treat these like a password: anyone with them can act as that account, and Facebook may sign the session out or restrict the account for automated use.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.facebook.com/Dominos",
    "https://www.facebook.com/KFC"
  ],
  "resultsLimit": 10,
  "recommendationType": "all"
}
```

# Actor output Schema

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

Every row in the default dataset: inputUrl, pageName, pageId, pageUrl, pageIsVerified, pageProfilePicture, pageRecommendPercent, pageReviewsCount, pageRatingSummary, reviewId, reviewUrl, isRecommended, recommendation, text, textLength, tags, date, timestamp, reviewerName, reviewerId, reviewerUrl, reviewerProfilePicture, likesCount, commentsCount, sharesCount, photos, photoCount. An empty, blocked or unmatched run returns a single uncharged row explaining what happened instead.

# 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/Dominos",
        "https://www.facebook.com/KFC"
    ],
    "resultsLimit": 10,
    "recommendationType": "all"
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/facebook-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 = {
    "startUrls": [
        "https://www.facebook.com/Dominos",
        "https://www.facebook.com/KFC",
    ],
    "resultsLimit": 10,
    "recommendationType": "all",
}

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/facebook-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 '{
  "startUrls": [
    "https://www.facebook.com/Dominos",
    "https://www.facebook.com/KFC"
  ],
  "resultsLimit": 10,
  "recommendationType": "all"
}' |
apify call dami_studio/facebook-reviews-scraper --silent --output-dataset

```

## MCP server setup

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