📊 Facebook Search Scraper: Local Market Landscape & Pricing
Pricing
$19.99/month + usage
📊 Facebook Search Scraper: Local Market Landscape & Pricing
🔎 Facebook Search Scraper captures public Facebook search results—pages, groups, events & posts—returning titles, URLs, IDs, categories, followers & timestamps. ⚡ Fast, scalable CSV/JSON exports for social listening, market research & lead gen. ✅ TOS-compliant.
Pricing
$19.99/month + usage
Rating
0.0
(0)
Developer
ScraperForge
Maintained by CommunityActor stats
0
Bookmarked
14
Total users
1
Monthly active users
11 days ago
Last modified
Categories
Share
Facebook Search Scraper: Local Market Landscape & Pricing
Facebook Search Scraper: Local Market Landscape & Pricing finds Facebook business pages by industry and region — Pub + NYC, Restaurant + Chicago — scrapes each one into a structured business record, and adds two things a plain page-by-page scrape doesn't give you: a price tier (Budget/Mid/Premium) on every business, and a market-landscape rollup per industry × region cell — business count, price-tier mix, average rating, average followers. Every response is typed JSON, ready for a market-research report, a site-selection model, or a lead-generation pipeline. No Facebook account, login or cookie is required — every field is read from the public, logged-out page.
What is Facebook Search Scraper: Local Market Landscape & Pricing?
It's an Apify Actor that turns an industry and a region into a list of Facebook business pages, reads each page's public business profile, and layers two derived, run-level features on top: a price-tier label per business and a market-landscape summary row per search cell. It's built for anyone sizing up a local market — not just anyone listing individual businesses in one.
No Facebook account, login or cookie is required or accepted — every surface it reads was verified logged-out. Discovery runs through a Google search (<industry> <region> site:facebook.com), so you never need to know a page's URL in advance; the query controls are the industry and the place, not a page handle.
- 🔎 Discovers pages you don't already have URLs for — from an industry and a city, not a known page link
- 📊 Rolls up a market-landscape snapshot per cell — business count, price-tier mix, average rating, average followers, computed automatically per industry × region
- 💲 Labels every business with a price tier — Budget/Mid/Premium, parsed from the page's own price band, with a filter to scope a run to one tier
- 📇 Returns the full business profile — address, phone, email, website, category, intro text
- ⏰ Extracts opening hours, services and price band — data the closest competing listing does not document
- ⭐ Returns rating, review count and approximate followers for quick screening
- 🏷️ Runs a cartesian product of industries and regions in a single job —
["Pub","Bar"]×["NYC","London"]becomes four searches, four market-landscape rows - 🌐 No login required — every field comes from the logged-out public page
What data can you get with Facebook Search Scraper: Local Market Landscape & Pricing?
Two row shapes come out of the same dataset: business rows (one per discovered page) and market-landscape rows (one per industry × region cell). A type column tells them apart.
| Result Type | Extracted Fields | Primary Use Case |
|---|---|---|
| Identity & discovery (business row) | facebookUrl, pageUrl, pageId, facebookId, pageName, title, category, categories, intro, about_me, info, likes | Confirming you found the right page and reading how it describes itself |
| Contact details (business row) | address, addressUrl, phone, email, websites, website | Building a contactable local-business lead list |
| Ratings & commerce (business row) | rating, ratings, ratingOverall, ratingCount, business_hours, priceRange, business_price, priceTier, services, business_services | Screening businesses by quality, hours and price tier |
| Audience & imagery (business row) | followers, followings, profilePictureUrl, coverPhotoUrl, profilePhoto | Sizing an audience and pulling brand assets |
| Social & ad diagnostics (business row) | alternativeSocialMedia, instagram, messenger, ad_status, pageAdLibrary, creation_date | Cross-platform lookups and ad-activity signal |
| Market landscape (rollup row) | type, industry, region, businessCount, priceTierMix, averageRating, averageFollowers | A market snapshot for one industry in one place — density, price positioning, quality bar |
Market landscape rollup
Every business row already carries a rating, a follower count and a price band — this Actor aggregates them, per industry × region cell, into one extra dataset row so you don't have to pull the whole run into a spreadsheet to answer "what does this market actually look like":
{"type": "marketLandscape","industry": "Coffee Shop","region": "Chicago","businessCount": 14,"priceTierMix": { "Budget": 3, "Mid": 8, "Premium": 2, "Unknown": 1 },"averageRating": 88.4,"averageFollowers": 4210}
businessCount is the number of business rows this Actor actually scraped for that cell (not Google's raw indexed count). priceTierMix counts how many scraped businesses fell into each tier, including an honest Unknown bucket for pages that never published a price band. averageRating/averageFollowers are computed only from businesses where that field was non-null — a business with no rating doesn't drag the average toward zero.
Price-tier segmentation
priceRange/business_price already carry Facebook's own price-band symbols ($, $$, £, £££...). This Actor buckets the symbol count — not the symbol itself, since the glyph is locale-rendered by the responding exit, not the business's currency (see Output, below) — into a priceTier label on every business row:
{ "priceRange": "$$", "business_price": "Price Range · $$", "priceTier": "Mid" }
One symbol is Budget, two is Mid, three or four is Premium. A business that never published a price band gets priceTier: null — never a guessed default. Set priceTierFilter to scope a whole run to one tier (see Input, below).
Opening hours, services and price band
Checked on the Apify Store 2026-07-25: the leading competing Facebook search listing documents page name, categories, website, email, address, phone, followers, likes, social links, rating, Facebook ID, messenger link, creation date and ad status — but neither its feature table nor its output example includes opening hours, services, or a price band. This Actor reads all three off the page's intro card:
{"business_hours": "Opens at 11:00 AM","services": "Delivery, Outdoor seating, Takeout","business_services": "Delivery, Outdoor seating, Takeout","priceRange": "$$","business_price": "Price Range · $$"}
business_hours and services/business_services are the intro-card text as Facebook renders it — not parsed into a structured weekly schedule or a service enum. These are what separate a directory listing from a business-intelligence row — hours and price band are what a reader actually needs to qualify a lead, compare two businesses in the same category, or bucket a whole market by price tier.
Ratings, contact details and social links
Every page carries rating as Facebook's own display string ("90% recommend (2,699 Reviews)"), split into ratingOverall and ratingCount as separate numbers so you can sort, threshold, or average them (as the market-landscape rollup does) without parsing text. phone, email, address and websites are read from the same intro card, entity-scoped rather than pulled from a whole-document regex. instagram returns a structured [{username, url}] list when the page links an Instagram account; alternativeSocialMedia carries the first other linked account found.
Why not build this yourself?
Meta does not publish a general keyword-and-location search API for Facebook Pages open to third-party developers — as the leading competing listing's own FAQ states, checked on the Apify Store 2026-07-25. A DIY scraper is the practical alternative, and building your own market-landscape rollup on top adds a fourth problem beyond the usual three.
Discovery isn't direct. There is no Facebook search endpoint to call, so pages have to be found through Google (site:facebook.com), which means handling Google's own pagination (10 organic results per request, not the 50-result stride a naive implementation assumes) and filtering out the URLs that aren't business pages — post permalinks, photos, videos, events, groups, reels and personal profiles all show up in the same result set and have to be excluded before you scrape and pay for them.
The data isn't in the visible HTML. Facebook renders a business page from nested Relay/GraphQL JSON blobs embedded in <script type="application/json"> tags — delegate_page for identity fields, a ProfileCometTileContextListViewItem card list for contact and commerce fields. Reaching a value means locating the right script block, then walking an unstable path through a require/__bbox wrapper structure that shifts between page builds.
A wall doesn't look like a wall. Facebook's login wall answers with HTTP 200, not an error status, so a status-code check alone never catches it — you have to check whether the resolved URL landed on a login/verification path, or whether the expected data markers are simply absent from the response. The wall is also tied to the exit IP rather than the page: the same URL can read cleanly and then wall minutes later from the same connection, which is why a single retry isn't enough and an automatic retry sequence is needed instead.
Aggregation needs cell attribution, not just a flat list. A market-landscape rollup only works if every scraped business is correctly attributed to the industry × region cell that discovered it — trivial-sounding, but a naive global de-duplication step (needed anyway, so the same business isn't scraped and charged twice across overlapping queries) silently loses that attribution unless it's carried through the whole pipeline.
What's the difference between this and the base Facebook Search Scraper?
The base Facebook Search Scraper returns one business record per discovered page — the raw material. This Actor keeps every one of those fields (a run built for the base actor still works here unchanged, see Input) and adds a second layer: a priceTier label on every row, and a marketLandscape summary row per industry × region cell that turns the raw rows into a market snapshot — business count, price positioning, average rating, average audience size — without a manual pivot-table step. If you just want the per-page records, either Actor gives you the same data; if you want the market read as well as the pages, use this one.
How to scrape a local market's Facebook business landscape
- Open Facebook Search Scraper: Local Market Landscape & Pricing on the Apify Store and click Try for free
- Enter what you're searching for —
industries(business types, e.g.["Restaurant", "Coffee Shop"]) andregions(places, e.g.["Chicago"]) - Set
resultsLimitto the total number of business pages you want across all industry-region combinations (1–500, default10) - Leave
includeMarketLandscapeon (default) to get the per-cell rollup, and setpriceTierFilterif you only want one price tier - Click Start, then download the dataset as JSON, CSV, Excel, XML or HTML — or read it through the Apify API
{"industries": ["Restaurant", "Coffee Shop"],"regions": ["Chicago"],"resultsLimit": 50,"includeMarketLandscape": true,"priceTierFilter": "All"}
How to run multiple industries and regions in one job
industries and regions are both arrays, and the Actor searches every combination: ["Pub", "Bar"] × ["NYC", "London"] becomes four queries — "Pub NYC", "Bar NYC", "Pub London", "Bar London" — and, with includeMarketLandscape on, four market-landscape rows, one per cell. resultsLimit is a total across every combination, not a per-query figure — the Actor divides it across however many industry/region pairs your run produces, so five industries times five regions sharing a resultsLimit of 10 leaves almost nothing per query, and a landscape row computed from one or two businesses is a thin sample. Raise resultsLimit as you widen the grid.
⚠️ How this Actor handles walled or unreadable pages
Facebook's login wall is exit-IP driven, not page-specific — the same URL can serve a full page from one connection and redirect to a login/verification path from another minutes later. Because of that, the Actor treats an unreadable page as a retry-with-a-different-connection problem rather than a dead end, automatically retrying through an alternate connection path up to 4 attempts total before giving up and pushing an uncharged error row. If you supply your own proxyConfiguration, that proxy is used as the starting point instead of the no-proxy default.
Detection is based on the resolved landing URL and on whether the page actually contains the data markers the parser needs — not on keyword matching inside the page, since ordinary words that sound alarming show up in the JavaScript bundle of essentially every legitimate Facebook business page and would misclassify normal pages as unreadable.
Concurrency. Pages are fetched with up to 5 concurrent workers, a fixed internal limit rather than a configurable input, over a connection pool capped at 10 total connections and 3 per host, with a short pause staggered before each request.
⬇️ Input
Every field is optional and has a working default, so a run with no input at all still starts (searching Pub in NYC, up to 10 pages, market-landscape rollup on).
| Parameter | Required | Type | Description | Example Value |
|---|---|---|---|---|
industries | No | array | Business types/industries to search (Pub, Bar, Restaurant...). Combined with regions, e.g. "Pub NYC", "Bar London". Default ["Pub"]. Renamed from the base actor's categories — see Base Compatibility below. | ["Restaurant", "Coffee Shop"] |
regions | No | array | Where to search (NYC, London...) — paired with each industry. Default ["NYC"]. Renamed from the base actor's locations — see Base Compatibility below. | ["Chicago", "Austin"] |
resultsLimit | No | integer | Max pages to scrape across all queries. Higher = more data, longer run. Minimum 1, maximum 500, default 10. | 50 |
includeMarketLandscape | No | boolean | Adds one type: "marketLandscape" rollup row per industry × region cell: business count, price-tier mix, average rating, average followers. Default true. | true |
priceTierFilter | No | string | Scope results to one price tier: All, Budget, Mid, or Premium, parsed from each page's own price band. Default All. | "Mid" |
proxyConfiguration | No | object | Optional. No proxy by default. Automatically retries through an alternate connection path if a page is temporarily unreadable. Default {"useApifyProxy": false}. | {"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]} |
Base compatibility (legacy keys)
This Actor also accepts the base Facebook Search Scraper's original input shape unchanged: categories and locations work exactly as they did on the base actor, and are used whenever industries/regions are not set. An input JSON built for the base actor runs on this Actor with no changes.
| Parameter | Required | Type | Description |
|---|---|---|---|
categories | No | array | Legacy alias for industries. Default ["Pub"]. Used only when industries is absent. |
locations | No | array | Legacy alias for regions. Default ["NYC"]. Used only when regions is absent. |
Example input
{"industries": ["Pub", "Bar"],"regions": ["NYC", "London"],"resultsLimit": 40,"includeMarketLandscape": true,"priceTierFilter": "All","proxyConfiguration": { "useApifyProxy": false }}
Common pitfall: resultsLimit is split across every industry-region combination, not applied per combination — widen it before you widen the grid, or you'll get one or two pages per query (and a thin market-landscape sample) instead of the count you expected. Separately, if your run reduces to a single query (one industry, one region) and resultsLimit is above 100 and that region is NYC or NY, the Actor silently adds one extra query spelling it out as "New York" to catch pages indexed under the full name — a one-off expansion, not a general locale-alias feature.
⬆️ Output
Two row shapes share the same dataset, told apart by type. Every business row carries the same 38 base keys plus type: "business" and priceTier, whether or not Facebook published a value for each one — a field with nothing to report is null (or []/{} for structured fields), never a missing key. Every market-landscape row carries type: "marketLandscape" plus its own 6 rollup keys. Business rows are pushed live as pages are scraped; each cell's landscape row is pushed once every business in that cell has been scraped. Export as JSON, CSV, Excel, XML, HTML table, or read the dataset through the Apify API.
Pages that can't be read produce a separate, smaller shape — {facebookUrl, error, errorReason} — and are not billed: the Actor pushes them without a charged_event_name, while every real business row and every market-landscape row is pushed with charged_event_name: "row_result". Filter uncharged rows out of a dataset export with 'error' not in row, or keep only them with the presence of the errorReason key. errorReason is one of login_wall, access_restricted, facebook_unavailable, http_<code>, unsupported_page, empty_result or fetch_error. A business row that doesn't match an active priceTierFilter is also not billed and not pushed — it never leaves the fetch stage.
Scraped results
[{"type": "business","facebookUrl": "https://www.facebook.com/riverstonecoffeechi","pageUrl": "https://www.facebook.com/riverstonecoffeechi","title": "Riverstone Coffee Co.","pageName": "riverstonecoffeechi","pageId": "100064823019214","facebookId": "100064823019214","categories": ["Coffee Shop"],"category": "Coffee Shop","info": ["Riverstone Coffee Co., Chicago, Illinois. 6,204 likes", "312 were here. Small-batch roaster and neighborhood coffee bar..."],"likes": 6204,"intro": "Small-batch roaster and neighborhood coffee bar on the near west side.","about_me": { "text": "Small-batch roaster and neighborhood coffee bar on the near west side." },"address": "1140 W Fulton Market, Chicago, IL, United States, Illinois","addressUrl": "https://www.bing.com/maps/default.aspx?v=2&pc=FACEBK&mid=8100&where1=1140+W+Fulton+Market%2C+Chicago%2C+IL...","phone": "(312) 555-0148","email": "hello@riverstonecoffee.com","websites": ["https://riverstonecoffee.com"],"website": "riverstonecoffee.com","alternativeSocialMedia": "https://www.instagram.com/riverstonecoffeechi","instagram": [{ "username": "riverstonecoffeechi", "url": "https://www.instagram.com/riverstonecoffeechi" }],"rating": "96% recommend (188 Reviews)","ratings": "96% recommend (188 Reviews)","ratingOverall": 96,"ratingCount": 188,"business_hours": "Opens at 6:30 AM","services": "Delivery, Outdoor seating, Takeout","business_services": "Delivery, Outdoor seating, Takeout","priceRange": "$$","business_price": "Price Range · $$","priceTier": "Mid","followers": 6100,"followings": 214,"profilePictureUrl": "https://scontent-ord5-1.xx.fbcdn.net/v/t39.30808-1/riverstone_profile.jpg","coverPhotoUrl": "https://scontent-ord5-1.xx.fbcdn.net/v/t39.30808-6/riverstone_cover.jpg","profilePhoto": "https://www.facebook.com/photo/?fbid=1129384756201847","pageAdLibrary": { "is_business_page_active": false, "id": "739201548822901" },"messenger": null,"creation_date": null,"ad_status": null},{"type": "business","facebookUrl": "https://www.facebook.com/thecopperkettlechi","pageUrl": "https://www.facebook.com/thecopperkettlechi","title": "The Copper Kettle","pageName": "thecopperkettlechi","pageId": "100064119482073","facebookId": "100064119482073","categories": ["Restaurant"],"category": "Restaurant","info": ["The Copper Kettle, Chicago, Illinois. 2,847 likes"],"likes": 2847,"intro": null,"about_me": { "text": null },"address": "3312 N Halsted St, Chicago, IL, United States, Illinois","addressUrl": "https://www.bing.com/maps/default.aspx?v=2&pc=FACEBK&mid=8100&where1=3312+N+Halsted+St%2C+Chicago%2C+IL...","phone": "(773) 555-0192","email": null,"websites": [],"website": null,"alternativeSocialMedia": null,"instagram": [],"rating": "Not yet rated (2 Reviews)","ratings": "Not yet rated (2 Reviews)","ratingOverall": null,"ratingCount": 2,"business_hours": null,"services": null,"business_services": null,"priceRange": "$","business_price": "Price Range · $","priceTier": "Budget","followers": 2900,"followings": 58,"profilePictureUrl": "https://scontent-ord5-1.xx.fbcdn.net/v/t39.30808-1/copperkettle_profile.jpg","coverPhotoUrl": null,"profilePhoto": "https://www.facebook.com/photo/?fbid=1120938475620194","pageAdLibrary": { "is_business_page_active": false, "id": "681904772013845" },"messenger": null,"creation_date": null,"ad_status": null},{"type": "marketLandscape","industry": "Coffee Shop","region": "Chicago","businessCount": 14,"priceTierMix": { "Budget": 5, "Mid": 7, "Premium": 1, "Unknown": 1 },"averageRating": 89.2,"averageFollowers": 3840}]
An unusable page produces this shape instead, and is not charged:
{ "facebookUrl": "https://www.facebook.com/example", "error": "Page could not be read (login_wall).", "errorReason": "login_wall" }
Read before you trust a field:
- ⚠️
followersis approximate. Facebook publishes no exact follower count on this surface — only the rounded UI string is available (82M,2.3K).followers: 82000000means "82M as displayed," not an exact count. Values under 1,000 are shown unrounded and are exact.averageFollowerson a market-landscape row inherits this same approximation. priceRange/business_pricecarry a locale-rendered symbol, not a currency. The same business can return£from one exit and$from another — Facebook chooses the glyph based on the responding IP, not the business's actual currency.priceTierdeliberately buckets on the symbol count (1–4), not which symbol, so this doesn't affect tier classification — but don't read the raw symbol as a currency.priceTierisnull, never guessed, when a page never published a price band. A market-landscape row'spriceTierMix.Unknowncount is exactly how many scraped businesses in that cell had no price band to bucket.businessCountis what this Actor actually scraped for that cell, not Google's raw index size. A narrow industry in a small region can have a landscape row built from just 1-2 businesses — treat a smallbusinessCountas a thin sample, not a definitive market read.addressUrlis constructed, not scraped. It is a Bing Maps search link built from theaddresstext — Facebook never serves this URL, and it is not a verified coordinate. Latitude/longitude are out of scope for this Actor.messenger,creation_dateandad_statusare honest nulls on this surface, not scraping failures. Facebook'sdelegate_pageobject carries no page-creation timestamp or ad-status flag through the logged-out path this Actor reads. The keys stay present so the output shape never changes.rating/ratingCounthandle comma-formatted review counts — pages with 1,000+ reviews (e.g."37,835 reviews") are parsed correctly, not silently nulled by a digits-only pattern that would otherwise drop the field on exactly the popular businesses you most want rated.instagram/alternativeSocialMediaare conditional, not guaranteed. An Instagram-linked account only appears on the intro card for some pages; when the page never linked one,instagramreturns[]andalternativeSocialMediaisnull— that is the true state of the page, not a missed extraction.followingsis a genuinely populated field on most pages, decoded from the profile header's "X following" text rather than left as a placeholder — it is one of the more reliably present secondary fields alongsidelikesandfollowers.
How can I use the data extracted with Facebook Search Scraper: Local Market Landscape & Pricing?
- 🗺️ Site-selection and franchise-expansion teams compare
businessCountandpriceTierMixacross candidate regions to gauge competitive density and price positioning before committing to a location. - 📊 Market researchers use the
marketLandscaperows directly as a market snapshot — count, price mix, average rating, average audience size — for a category and city, without a manual pivot-table pass over the business rows. - 📞 Local lead generation teams filter business rows on
phone,emailorwebsitesto build a contactable prospect list for an industry and city, and usepriceTierFilterto target a specific price segment (e.g. onlyPremiumprospects). - 🏪 Local SEO and directory builders sweep an industry across many cities and use
address,business_hoursandpriceRangeto populate a structured local-business directory without manual data entry. - 🤖 AI engineers and LLM developers issue an industry + region query, receive typed JSON rows — including the market-landscape summary — and pass them directly into an agent's context or a RAG index, no HTML parsing between the Actor and the model.
How do you monitor a local market's Facebook business landscape over time?
Monitoring here means re-running the same industry-and-region set on a schedule and diffing the results against the previous run — at two levels: per business (keyed on facebookUrl, stable across runs) and per market (keyed on industry+region, the market-landscape row).
Per business, the fields worth diffing are business_hours and priceRange/business_price/priceTier for operational changes, rating/ratingOverall/ratingCount for reputation drift, and followers for audience growth. Per market, diff businessCount (a market gaining or losing competitors), priceTierMix (a market's price positioning shifting), and averageRating/averageFollowers (a market's overall quality bar or attention moving). A landscape row's businessCount dropping between two runs of the same query, or its priceTierMix shifting toward Premium, is a market-level signal a page-by-page diff alone won't surface as clearly.
A practical loop: save your industry/region set as a fixed input, schedule a weekly run, export each run's dataset, and diff both the business rows (keyed by facebookUrl) and the market-landscape rows (keyed by industry+region) against the prior week's export. Facebook Search Scraper: Local Market Landscape & Pricing has no scheduling of its own; use Apify Schedules to run it on a cron interval, and either poll the dataset through the Apify API or attach a webhook to fire when a run finishes so the diff step can run automatically.
Integrate Facebook Search Scraper: Local Market Landscape & Pricing and automate your workflow
This Actor works with any language or tool that can send an HTTP request, because it runs as a standard Apify Actor behind the Apify API.
REST API with Python
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("<YOUR_USERNAME>/facebook-search-scraper-local-market-landscape-and-pricing").call(run_input={"industries": ["Restaurant", "Coffee Shop"],"regions": ["Chicago"],"resultsLimit": 50,"includeMarketLandscape": True,})for item in client.dataset(run["defaultDatasetId"]).iterate_items():if item.get("type") == "marketLandscape":print(item["industry"], item["region"], item["businessCount"], item["priceTierMix"])elif "error" not in item:print(item["title"], item["priceTier"], item["rating"])
Works the same way in Node.js, Go, Ruby, or plain curl against the Apify API — call the Actor, poll or await the run, then read the dataset.
Scheduled monitoring and delivery
Set up a recurring run with Apify Schedules to sweep the same industries and regions on a cron interval — daily, weekly, or any custom cadence. Combine it with an Apify webhook to trigger a notification, a Zapier/Make/n8n workflow, or your own endpoint automatically whenever a run finishes, instead of polling for new data.
Is it legal to scrape Facebook business pages?
Scraping publicly accessible web data is broadly permitted in US law — hiQ Labs v. LinkedIn (9th Cir. 2019, on remand 2022) held that scraping data not protected by a login does not violate the Computer Fraud and Abuse Act. This Actor reads only what any logged-out visitor already sees on a public business page; it does not bypass a login, does not use a Facebook account, and does not access private or friends-only content.
Facebook business pages carry commercial contact data — address, phone, email of a business, not a private individual's profile — so this output is closer to a business or directory record than personal data, though the same legal caution applies as with any bulk-collected dataset: review Facebook's Terms of Service, and be aware that some published contact details may belong to a sole proprietor rather than a corporate entity. Scraping for internal monitoring, market research or lead generation carries a different risk profile than scraping to train or resell a dataset. Consult your legal team for commercial use cases involving bulk data storage.
❓ Frequently asked questions
Do I need a Facebook account, login or cookie?
No. This Actor does not accept a cookie or login credential in its input schema, and every field it returns comes from the page Facebook serves to a logged-out, anonymous visitor.
How does this Actor find business pages?
It searches Google for <industry> <region> site:facebook.com and keeps only URLs that resolve to a real business page — post permalinks, photos, videos, events, groups, reels and personal-profile URLs are filtered out before anything is scraped or charged.
What is the market-landscape rollup?
One extra dataset row per industry × region cell (type: "marketLandscape"), computed after every business in that cell has been scraped: how many businesses were found, how they split across Budget/Mid/Premium price tiers, and their average rating and average follower count. It's a market snapshot, not a per-business record.
How is priceTier calculated?
From the symbol count in the page's own priceRange/business_price field: one symbol is Budget, two is Mid, three or four is Premium. A business with no published price band gets priceTier: null, never a guessed value.
Can I only return businesses in one price tier?
Yes — set priceTierFilter to Budget, Mid, or Premium. Businesses outside the chosen tier are not pushed to the dataset and are not charged.
Does my existing base Facebook Search Scraper input still work?
Yes. categories and locations (the base actor's original input keys) are still accepted and used whenever industries/regions aren't set — an input JSON built for the base actor runs on this Actor unchanged.
How many results does it return per query?
Up to resultsLimit (maximum 500, default 10), split across every industry-region combination in your run. The practical ceiling per individual query is what Google's own index returns for site:facebook.com plus your industry and region terms — a very narrow industry in a small town simply has fewer indexed pages to find.
Does this Actor extract opening hours, services and price band?
Yes — business_hours, services/business_services and priceRange/business_price are read from the page's intro card on every successful row. They are null when the business itself never published that detail on its Facebook page; the Actor reports the absence rather than guessing.
What happens if a page is login-walled or otherwise unreadable?
It is pushed as a small {facebookUrl, error, errorReason} row and not charged — the Actor pushes it without a charged_event_name, unlike a real business row or market-landscape row, which are charged under row_result.
How does this Actor handle Facebook's anti-bot measures?
Because Facebook's login wall answers with HTTP 200 rather than an error status, an unreadable page is detected from the resolved landing URL or from the absence of the data markers the parser needs — not from keyword matching inside the page, which would flag legitimate pages as unreadable. Since the wall is tied to the exit IP rather than the page, an unreadable page automatically retries through an alternate connection path before it is given up on as an uncharged error row.
Can I run multiple industries and regions in one job?
Yes. industries and regions are both arrays, and the Actor runs every combination as a separate search query — and a separate market-landscape row when includeMarketLandscape is on — in the same run.
How do I monitor a market's business landscape over time?
Save your industry/region set, schedule the Actor with Apify Schedules, and diff both the business rows (business_hours, priceRange, rating, ratingCount, followers, keyed on facebookUrl) and the market-landscape rows (businessCount, priceTierMix, averageRating, averageFollowers, keyed on industry+region) against the previous run's export.
Does this Actor work with Claude, ChatGPT and other AI agent frameworks?
Yes. It is callable as a standard HTTP endpoint through the Apify API, so any agent framework that can make a request — LangChain, CrewAI, n8n, or a hand-written tool definition — can invoke it and receive typed JSON rows, including the market-landscape summary, directly.
How does this compare to other Facebook search scrapers?
Checked on the Apify Store 2026-07-25: the top-ranked competing listing (10,000+ users at the time checked) documents page name, categories, website, email, address, phone, followers, likes, social links, rating, Facebook ID, messenger and ad status, but neither its feature list nor its output example documents opening hours, services or price band — and none of the competing listings checked compute a price tier or a per-market rollup at all. This Actor's difference is turning the standard per-page fields into a market-level deliverable — price-tier segmentation and a business-count/price-mix/rating/audience rollup per industry and region — with no login required for any of it.
Can I use this Actor without managing proxies or Facebook credentials?
Yes. No Facebook account, cookie or login is accepted or needed. Connection handling is automatic too: the Actor runs with no proxy by default and only escalates to an alternate connection path if an exit gets login-walled. The one credential you supply is your own Apify account.
🔗 Related scrapers
This Actor extends the base Facebook Search Scraper (category/location business-page discovery) with a market-landscape rollup and price-tier segmentation. If you only need the per-page business record with no rollup, the base actor returns the same 38 core fields without the two added features.