Use Facebook Business Page Recommendation & Review Scraper with Python
Get Facebook business page reviews by direct URL, business name search, or Page ID, with Recommended/Not Recommended status, owner replies, and filters for date, keyword, and recommendation status.
1. Choose the data you need
Review the published README for supported collection modes, examples, and limits. The recipe below uses the current published input definition.
2. Prepare a source-based input
This JSON contains published prefills and required-field defaults. Review every value for your task before running it; it is not a promise of successful collection.
{
"failOnEmpty": false,
"includePageSummary": true,
"includeReplies": false,
"maxItems": 5,
"maxRuntimeSecs": 1800,
"onlyNew": false,
"pageIds": [],
"recommendation": "all",
"requestDelayMs": 600,
"startUrls": [
{
"url": "https://www.facebook.com/mcdonalds/reviews"
}
]
}
Download input.example.json and save your reviewed copy as input.json.
| Field | Type | Required | Description |
|---|---|---|---|
startUrls | array | optional | Facebook business page URLs or /reviews URLs (e.g. https://www.facebook.com/mcdonalds/reviews). /reviews is added automatically when missing. Invalid or dead page URLs fail fast instead of being guessed. |
pageIds | array | optional | Numeric Facebook Page IDs (e.g. 100064458773195) or page usernames (e.g. mcdonalds). Use this when you do not have a full URL. |
maxItems | integer | optional | Maximum number of review rows to return across all pages (1-2000). Facebook returns roughly 2-3 reviews per request (about 2.7 on average) even when a larger count is requested, so 100 reviews needs about 35-40 requests. |
maxPerPage | integer | optional | Optional cap per Facebook page for multi-page runs. Leave empty to share the global Max Reviews cap across pages. |
includeReplies | boolean | optional | Include the visible top comments and business owner replies inside each review row. When disabled, commentCount is still returned. |
includePageSummary | boolean | optional | Emit one recordType=page row per page with the recommendation percentage, total review count, followers, and talking-about count. This adds no extra requests. |
onlyNew | boolean | optional | Return only reviews not seen in the saved state for this page. The state is stored in a named key-value store that persists across runs, so scheduled runs can monitor for new reviews. |
stateKey | string | optional | Optional prefix for the key-value store key used by monitor mode. Each page's state is stored separately. Leave empty to use the page ID. |
recommendation | string | optional | Only return reviews with the selected recommendation status. Facebook uses binary Recommended / Not Recommended, not star ratings. |
dateFrom | string | optional | Only return reviews posted on or after this date. Reviews are newest-first, so collection stops when it passes this date. |
dateTo | string | optional | Only return reviews posted on or before this date. |
keyword | string | optional | Only return reviews whose text contains this word or phrase (case-insensitive). |
requestDelayMs | integer | optional | Pause between review requests. 500-800 ms is a polite pace and keeps long runs stable. |
maxRuntimeSecs | integer | optional | Stop collecting after this many seconds and return what has been collected so far. |
failOnEmpty | boolean | optional | Mark the run as failed when no review rows are returned. Monitor-mode runs that legitimately have no new reviews never fail. |
proxyConfiguration | object | optional | Optional. Leave empty to let the actor choose automatically: it tries a direct connection first and only escalates through Apify Proxy if Facebook blocks the request. Change this only if you need a specific proxy setup. |
cookies | array | optional | Optional Facebook cookies exported from a logged-in browser. They are not required and do not increase how many reviews Facebook serves per request; the actor returns real reviews without them. |
3. Start an Actor run
Set APIFY_TOKEN in your environment. Python 3.10 or newer is sufficient; this recipe uses no third-party packages.
Execution may incur charges. Review current pricing on Apify. The request asks for a 120-second timeout and a charge limit; these settings are subject to Apify’s pricing and do not guarantee an all-inclusive spending cap.
import json
import os
import urllib.request
# Save the schema-derived example as input.json; review and edit it first.
with open("input.json", encoding="utf-8") as stream:
actor_input = json.load(stream)
token = os.environ["APIFY_TOKEN"]
request = urllib.request.Request(
"https://api.apify.com/v2/acts/kHf019KL2WuzR6BFE/runs?timeout=120&maxTotalChargeUsd=0.50",
data=json.dumps(actor_input).encode("utf-8"),
headers={"Authorization": "Bearer " + token, "Content-Type": "application/json"},
method="POST",
)
# This starts a potentially billable run. Do not automatically retry this POST.
with urllib.request.urlopen(request, timeout=40) as response:
run = json.load(response)["data"]
print("Run ID:", run["id"])
print("Status:", run["status"])
# Inspect and download results in Apify Console once the run succeeds.
4. Inspect the actual results
Open the run in Apify Console and wait for a successful completion. Use the published output schema to locate results and the dataset schema to understand declared views. Missing or optional record fields must not be treated as guaranteed values.
Review rows with recommendation status, author details, reactions, tags, comments and owner replies, plus one page-summary row per page and error rows.
Questions before you run
Which inputs are required?
No top-level fields are marked required in the published schema. Conditional requirements can still apply; check the README.
What does this guide verify?
The fields, prefills, and schema links are rendered from the published build. This is a usage recipe, not a paid live extraction test. Source availability and collection results can vary.
What if the run request times out?
Check Apify Console before retrying. The first request may already have created a run. Do not automatically repeat a run-creation POST.
Generated from source build mOLyljzUlnPCEScHZ and its README and schemas. Source changes regenerate this guide; no third-party editorial claims are inferred.