Facebook Search Scraper & Public Events
Pricing
from $4.99 / 1,000 results
Facebook Search Scraper & Public Events
๐ Collect public data from Facebook search results based on your keywords. This Apify actor extracts page names, posts, links, and basic engagement data. Useful for research, trend tracking, and competitor analysis with clean, structured output ready to export.
Pricing
from $4.99 / 1,000 results
Rating
0.0
(0)
Developer
Scraper Engine
Maintained by CommunityActor stats
0
Bookmarked
15
Total users
2
Monthly active users
5 days ago
Last modified
Categories
Share
Facebook Search Scraper โ Public Events, Dates and Venue Details
Facebook Search Scraper & Public Events finds Facebook business pages by category and city, then enumerates the public events each page hosts: event name, start time as Unix seconds and a UTC timestamp, venue name and coordinates, ticket link, host and cover photo โ plus per-page roll-ups such as next event, last event and events per month. One typed JSON row per business page, exportable to JSON, CSV, Excel or XML. No Facebook login and no cookie needed.
โ ๏ธ Nothing in the input schema is required, and that is not the same as "nothing runs". An empty input starts a real, billable search for Pub in NYC. Read the next-but-one section before your first API call.
What is Facebook Search Scraper & Public Events?
Facebook Search Scraper & Public Events is an Apify Actor that discovers Facebook business page URLs from search results for your categories ร locations combinations, scrapes the full public business record from each page, and then reads that page's /events and /past_hosted_events tabs โ optionally opening each event's own detail page for description, venue coordinates, address, timezone, ticket URL and cover photo.
No Facebook account, login or cookie is required. Every surface the Actor reads โ the page, both event tabs and the event detail pages โ is read logged-out, which the source records as verified on 2026-07-23. The optional cookies input exists only for age-gated pages.
It is built for local-marketing and venue-lead teams, event and hospitality researchers, and developers who want venue activity signals as structured JSON rather than as a browsing session.
โ ๏ธ Every input is optional โ know what an empty run does
The schema's required array is empty, so the Apify API accepts {} as a valid run input. It does not produce an empty run. Inside the Actor the values are resolved with or fallbacks, so an empty or falsy field is replaced by a hardcoded default:
| You send | The Actor actually runs |
|---|---|
{} | categories: ["Pub"], locations: ["NYC"] โ one query, "Pub NYC", up to 10 pages scraped and charged |
"categories": [] | Falls back to ["Pub"]. An empty list does not mean "search nothing" |
"locations": [] | Falls back to ["NYC"]. There is no way to run a category with no city attached |
"resultsLimit": 0 | Falls back to 10 |
"maxEventsPerPage": 0 | Falls back to 8, then clamped into the 1โ16 range |
The Console hides this, because categories and locations are both prefilled with the same values and you see them on screen before pressing Start. An API caller sending {} or a partially-built object does not see them, and the first sign is a dataset full of New York pubs. Send the fields you mean explicitly.
What Facebook page and public event data is available without logging in?
A Facebook business page and its two event tabs are server-rendered for anonymous visitors: the page's own details, up to eight event nodes per tab, and each event's public detail page. What is never in those payloads is anything about the individual people attending.
| Data Category | Public, logged-out | Gated behind a Facebook login |
|---|---|---|
| Page title, category, intro text, profile and cover photo | โ Yes | โ |
| Address, phone, email, website, hours, price range from the intro cards | โ Yes | โ |
| Recommendation percentage, review count, followers, likes | โ Yes | โ |
| Upcoming events tab and past hosted events tab | โ Yes โ up to 8 event nodes per tab | Deeper pagination |
| Event name, start time, host, venue name, cancelled and online flags | โ Yes | โ |
| Event description, venue coordinates, full address, timezone, ticket link, cover photo | โ Yes โ from the event detail page | โ |
| Aggregate "interested" and "going" numbers | โ Sometimes โ only when Facebook prints them in the page meta description | Reliable counts |
| Who is attending โ the guest list | โ | Never exposed publicly |
| Private, invite-only or unlisted events | โ | Not on a public page tab |
| Age-restricted pages | โ | Requires a session cookie |
| Page posts, review text, the follower list | โ | Not read by this Actor at all |
Facebook Search Scraper & Public Events only returns publicly visible data โ what any logged-out visitor sees. Nothing behind a login wall.
What data can I extract with Facebook Search Scraper & Public Events?
Every successful row is one business page and carries 53 top-level keys: 38 describing the business, 15 rolling up its public events, one of which โ events โ is an array of event objects with 30 keys each.
Business page identity and contact fields
| Field Name | Description |
|---|---|
facebookUrl | Canonical page URL the row was built from |
pageUrl | The same URL again, kept for compatibility with the base page shape |
pageId | Numeric Facebook page ID |
facebookId | The same numeric ID again |
pageName | URL vanity slug, e.g. the copperstilltavern in the page URL |
title | Page display name |
categories | Array of the page's category names |
category | First entry of categories, or null |
intro | The page's own "best description" text |
about_me | Object { "text": <intro> } โ the same text nested |
info | Array of description fragments split out of the page meta description |
address | Street address as printed on the page |
addressUrl | A Bing Maps link built from address โ Facebook's own map link format, not a Google Maps URL |
phone | Phone number, taken from the page entity first and the intro card second |
email | Contact email from the intro card |
website | Bare domain of the page's website, www. stripped |
websites | Array holding the same website with an https:// scheme added |
instagram | Array of { "username", "url" } objects for a linked Instagram account |
alternativeSocialMedia | First non-Facebook external account URL found on the intro cards |
messenger | Always null โ reserved key, never populated |
profilePictureUrl | Profile picture image URL |
profilePhoto | Facebook photo permalink for the profile picture, built as photo/?fbid=<id> |
coverPhotoUrl | Page cover photo image URL |
services | Services text from the intro card, dropped to null when it runs over 100 characters or matches known junk |
business_services | The same services value |
business_hours | Opening-hours text or an "Open now" / "Closed now" state |
priceRange | Normalised price band, e.g. $$. Non-dollar currency symbols are preserved verbatim |
business_price | "Price Range ยท <priceRange>" |
rating | Recommendation sentence, e.g. "94% recommend (312 Reviews)" |
ratings | The same sentence again |
ratingOverall | Recommendation percentage as an integer |
ratingCount | Review count as an integer, thousands separators handled |
likes | Page likes as an integer |
followers | Followers as an integer, with 1.2K / 3.4M shorthand expanded |
followings | Accounts the page follows, as an integer |
creation_date | Page creation date formatted as "March 14, 2011" |
ad_status | "This Page is currently running ads." when Facebook reports an active ad, otherwise Facebook's own value or null |
pageAdLibrary | Object { "is_business_page_active", "id" } from the Ad Library block |
Six keys in this group are snake_case โ business_hours, business_price, business_services, creation_date, ad_status, about_me โ while everything else is camelCase. That is deliberate: it preserves the original page-record contract so an existing field mapping keeps working. Handle both cases in your ingest.
Page-level event roll-up fields
| Field Name | Description |
|---|---|
events | Array of event objects, newest first, capped at maxEventsPerPage |
eventCount | Number of events in events |
upcomingEventCount | How many of them have isPast exactly false |
pastEventCount | How many have isPast exactly true |
hasPublicEvents | true when events is non-empty |
hasUpcomingEvents | true when at least one event has isPast false โ this is what onlyPagesWithUpcomingEvents filters on |
nextEventDate | UTC timestamp of the soonest upcoming event, null if there is none |
lastEventDate | UTC timestamp of the most recent past event โ deliberately ignores future dates, so "this venue last ran an event in 2017" stays readable |
firstEventDate | Earliest start time across the collected events |
latestEventDate | Latest start time across the collected events |
eventsPerMonth | Event cadence over the collected window, rounded to 2 decimals. null when there are fewer than two dated events or when they span under a day |
eventsWithTicketUrl | How many collected events carry a ticketUrl |
eventsWithCoordinates | How many carry latitude |
eventTabsRead | Array of the tabs that actually hydrated: events, past_hosted_events, or both |
eventsSource | The same information as a string: events, past_hosted_events, events,past_hosted_events, not_requested when both tabs are switched off, no_tab_hydrated when they were read but returned nothing, or error:<ExceptionName> |
Inside the events array
Every event object carries all 30 keys, always. Values Facebook did not supply arrive as null rather than as a missing key.
| Field Name | Description |
|---|---|
eventId | Numeric Facebook event ID |
name | Event title |
url | Canonical event URL, built as https://www.facebook.com/events/<eventId>/ when Facebook does not supply one |
tab | Which tab produced this event: events or past_hosted_events |
dayTimeSentence | Facebook's own rendered date sentence, in the venue's local wall-clock time |
startTimestamp | Start time as Unix seconds โ an absolute instant |
startDate | The same instant as a UTC ISO-8601 string, YYYY-MM-DDTHH:MM:SS.000Z |
endTimestamp | End time as Unix seconds. Only ever populated from the event detail page, and only when Facebook ships it |
endDate | The same end instant as a UTC ISO-8601 string |
timezone | Facebook's timezone display name for the event, e.g. Eastern Daylight Time โ a human-readable label, not an IANA zone ID. Detail page only |
isPast | Facebook's own past flag |
isUpcoming | Derived as the inverse of isPast; null when isPast is null |
isHappeningNow | Facebook's live flag, as of when the tab was fetched |
isCanceled | Cancelled flag |
isOnline | Facebook's online-or-detected-online flag |
eventKind | Facebook's own event kind classifier |
hostName | Organiser name โ a Page name, or a person's name when an individual hosts |
hostId | Organiser's numeric Facebook ID |
hostUrl | Organiser's Facebook URL, falling back to profile.php?id=<hostId> |
venueName | Venue name, from the event's place object |
venueAddress | One-line venue address. Detail page only |
latitude | Venue latitude as a number. Detail page only |
longitude | Venue longitude as a number. Detail page only |
ticketUrl | External ticket-purchase URL when the organiser set one. Detail page only |
description | Full event description text. Detail page only |
coverPhotoUrl | Event cover image URL. Detail page only |
interestedCount | People marked interested, as an exact integer โ see the caveat below |
goingCount | People marked going, as an exact integer. Can be null while interestedCount is set |
attendanceSentence | The same two numbers as a display string, e.g. "126 interested ยท 40 going" |
detailStatus | Per-event fetch outcome: ok, not_requested when includeEventDetails is off, no_event_node, login_wall, http_<code>, or error:<ExceptionName> |
Two things worth reading twice. Attendance counts are exact when present but rarely present โ they are parsed out of the event page's meta description, and the source records a measurement of 5 out of 66 events carrying that clause on 2026-07-23. They are not bucketed or rounded; they are simply absent most of the time. And the eleven detail-page fields above are all null when includeEventDetails is false โ that switch is the difference between a listing-level record and a full event record.
Add-on: need more Facebook data?
Events are one signal a page emits. Facebook Pages Recent Posts Scraper shows what those same pages post between events, and Facebook Posts Scraper goes deeper on a single page's timeline. If you are mapping a local scene rather than a page list, Facebook Group Discovery Scraper finds the communities around it, and Google Maps Scraper with Reviews Sentiment Analysis returns the same venues from a second source with review sentiment attached.
Why not build this yourself?
Meta's Graph API exposes no public event search to third-party apps, and no endpoint that hands you the public events tab of an arbitrary business page โ check Meta's current Graph API reference before assuming otherwise. So the alternative is your own scraper, and the cost is in the failure modes, not the happy path.
Discovery is the first one. Facebook has no logged-out page search you can call, so pages have to be found through a search engine, and search-engine pagination has sharp edges: this Actor's source records that appending numPages=5 to the search request timed out on every attempt through the proxy it uses, while the same URL without it answered HTTP 200 in a few seconds; that num=100 is accepted but still returns ten organic results; and that stepping the start offset by 50 instead of 10 silently skips four of every five result pages. The result set then has to be filtered, because raw search hits include post permalinks, photo URLs, group links and personal profiles that look exactly like business pages until you have scraped and paid for them.
The second cost is knowing when you have been blocked. A Facebook login wall answers HTTP 200 after redirecting to /login, so raise_for_status() never fires and a naive scraper pushes โ and charges for โ a row of 38 nulls. Nor can you key on tokens: every logged-out Facebook bundle contains the strings checkpoint, blocked and rate limit somewhere in its JavaScript, so substring detection false-positives on perfectly good pages. Reliable detection means combining the HTTP status, the resolved URL, and the presence of the server-rendered container the parser needs.
The third is the events payload itself. Each event on a tab is split across two JSON node families that must be merged by event ID โ one carries the name, URL, place and creator, the other the start timestamp and the past, live and online flags. And an event detail page embeds three suggested events, so any extraction that walks the whole document for latitude, event_creator or an attendance string will happily return another event's venue and another event's numbers. Every field here is scoped to the requested event ID; the timezone and end-timestamp node is the single exception, because it is the page-level response and carries no ID of its own.
How to use Facebook Search Scraper & Public Events
The Actor runs on Apify. Start it from the Apify Console, or call it through the Apify API.
- Open Facebook Search Scraper & Public Events on Apify and click Try for free
- Fill in Categories (
categories) โ the business types you want, such asPub,Live Music Venue,Nightclub - Fill in Locations (
locations) โ the cities you want them in. Every category is paired with every location, so 3 categories and 2 cities is 6 search queries - Set Results Limit (
resultsLimit) โ the cap on business pages scraped across all queries, not per query - Leave the three event switches on for full event records, or turn Fetch each event's detail page off for a much faster, listing-only run
- Click Start, then export as JSON, CSV, Excel or XML
The log prints one line per page with its title, address, phone and event summary โ including the next or last event date โ so you can tell whether a category and city combination is worth a bigger run before committing to one.
How to scale to bulk page and event extraction
categories and locations are both lists, and the Actor takes their full cartesian product. Ten categories and five cities is fifty search queries in a single run, all writing into one dataset, with discovered page URLs deduplicated across every query by canonical URL โ so a venue that ranks for both Pub NYC and Bar NYC is scraped once.
The number to watch is resultsLimit, because it is divided, not multiplied: the per-query URL budget is resultsLimit // number_of_queries, floored at 1. Fifty queries with resultsLimit: 10 gives each query a budget of one URL, and the run total is then re-capped at resultsLimit anyway โ so you get ten pages, not fifty. Scale resultsLimit up with your query count, or you will collect a fraction of what you expected and wonder where the rest went.
What can you do with Facebook event and venue data?
- ๐ช A venue sales rep selling to hospitality filters rows on
hasUpcomingEventsand sorts byeventsPerMonthto reach the pubs that actually programme events, then worksphoneandemailstraight off the same row. - ๐๏ธ An event promoter researching a city pulls
Live Music Venueacross five cities and usesnextEventDate,venueName,latitudeandlongitudeto map who has something on and where, before booking a route. - ๐ A local-marketing analyst spots dormant venues by filtering for a
lastEventDateolder than a year withupcomingEventCountat zero โ a page that used to programme and stopped is a different pitch from one that never did. - ๐ซ A ticketing partnerships lead filters events on
ticketUrlbeing non-null to find organisers already selling through an external platform, and readshostNameandhostUrlto identify who owns the relationship. - ๐ค An AI engineer building a local-events assistant indexes
description,nameandvenueNameinto a vector store withstartTimestamp,latitudeandlongitudeas metadata filters, so the agent answers "what is on near me this weekend" against live listings instead of a stale export.
Every one of these is callable from an agent framework over the Apify API, since the Actor is a standard HTTP-triggered run.
How does it handle rate limits and blocking?
No browser is launched and no CAPTCHA is solved. The Actor makes plain HTTP requests with a full desktop Chrome header set and reads Facebook's server-rendered JSON payloads.
Two networking paths exist. Page discovery always goes through Apify's Google SERP proxy group โ that part is not optional, and if the proxy cannot be created the run logs a warning and finishes cleanly with zero rows rather than hammering the search engine directly. Page and event fetching starts on a direct connection with no proxy at all, which is the default, and only escalates if Facebook refuses: direct, then Apify datacenter, then Apify residential, up to four attempts per page, logging each switch. If you set proxyConfiguration yourself it is used as the starting rung instead.
A response counts as blocked on HTTP 403, 429, 500, 502 or 503, when the resolved URL lands on /login, /checkpoint or /authentication, or when the server-rendered container the parser needs is missing from a short response. Failures are typed, and the type decides the response: login_wall and blocked are treated as exit-dependent and retried on a new rung, while unsupported_page โ a URL that was never a business page โ stops immediately, because rotating an IP cannot make it one.
Pages are processed four at a time with a short delay before each, event detail pages four at a time within a page, and requests time out at 30 seconds for pages and tabs, 35 for event details and 45 for a search request. A page that never succeeds does not fail the run: it becomes an uncharged accounting row and the run continues.
โฌ๏ธ Input
Every parameter is optional. The table below matches the Actor's input schema exactly.
| Parameter | Required | Type | Description | Example Value |
|---|---|---|---|---|
categories | No | array | Business types to search (Pub, Bar, Restaurant...). Combined with locations, e.g. "Pub NYC", "Bar London". Default ["Pub"]. | ["Pub", "Live Music Venue"] |
locations | No | array | Where to search (NYC, London...) โ paired with each category. Default ["NYC"]. | ["NYC", "Brooklyn"] |
resultsLimit | No | integer | Max pages to scrape across all queries. Higher means more data and a longer run. Minimum 1, maximum 500. Default 10. | 60 |
proxyConfiguration | No | object | Optional. No proxy by default. Auto fallback to datacenter then residential if blocked. Prefilled {"useApifyProxy": false}. | {"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]} |
includeEvents | No | boolean | Read the page's upcoming events tab. Facebook returns up to 8 event nodes per page. Default true. | true |
includePastEvents | No | boolean | Also read the page's past hosted events tab. Default true. | true |
includeEventDetails | No | boolean | One extra request per event. Adds description, venue coordinates, full address, timezone, ticket link and cover photo. Turn off for a much faster, listing-only run. Default true. | true |
maxEventsPerPage | No | integer | Cap on events kept per business page, newest first. Minimum 1, maximum 16. Default 8. | 16 |
onlyPagesWithUpcomingEvents | No | boolean | Keep only pages that have at least one event with isPast false. Default false. | false |
cookies | No | object | Optional cookie jar, e.g. {"c_user": "...", "xs": "..."}. Not required โ every surface this Actor reads was verified logged-out. Supply it only to reach an age-gated page. Stored encrypted. | {"c_user": "100012345678901", "xs": "46%3AAbCdEf..."} |
Four honest notes on how these behave at runtime:
onlyPagesWithUpcomingEventsis a post-fetch filter, and so ismaxEventsPerPage. Neither is sent to Facebook. The page is fetched, both event tabs are read, the events are sorted newest-first and only then truncated tomaxEventsPerPage; the upcoming-events test runs last of all, on the finished row. The good news is that a filtered-out page is neither pushed nor charged โ the schema records that on a measured sample of 8 pages only 3 had a genuinely upcoming event, so expect the row count to shrink sharply when you switch it on. What you still spend is run time.- There is no date-range filter and no true location filter.
locationsis a word appended to the search query, so which city you actually get is whatever the search engine decides is relevant. Filter onstartDateorstartTimestampafter the run if you need a window. maxEventsPerPagecaps at 16 for a reason. Facebook serves at most 8 event nodes per tab, and the Actor reads two tabs, so 16 is the practical ceiling; the value is clamped into the 1โ16 range in code as well as in the schema. The schema also records that on 6 event-bearing pages, 3 returned a fully disjoint second set from the past tab while 3 returned the same events the first tab had already shown โ so two tabs does not reliably mean twice the events.- One undocumented query expansion exists. When your input resolves to exactly one search query, that query contains
NYCorNY, andresultsLimitis above 100, the Actor silently adds a second query withNew Yorksubstituted. It applies to no other city.
Example input
{"categories": ["Pub", "Live Music Venue"],"locations": ["NYC", "Brooklyn"],"resultsLimit": 60,"includeEvents": true,"includePastEvents": true,"includeEventDetails": true,"maxEventsPerPage": 16,"onlyPagesWithUpcomingEvents": false,"proxyConfiguration": { "useApifyProxy": false }}
โฌ๏ธ Output
Typed, normalized JSON with the same 53 top-level keys on every successful row, run after run, and the same 30 keys inside every event object. Rows are pushed as each page finishes, so a run you stop early still leaves you everything collected up to that point. Export as JSON, CSV, Excel or XML, or read the dataset through the Apify API. The default dataset view shows 18 columns; a second view, Flat Event List, unwinds events so each event becomes its own spreadsheet row.
Billing is per row: each successful page row is pushed with the row_result charged event. Pages that could not be read are pushed with no charged event at all โ they are free, and they carry only three keys, so they are trivial to exclude:
pages = [row for row in items if "error" not in row]
An uncharged row is { "facebookUrl", "error", "errorReason" } and nothing else โ the other 50 keys are omitted rather than nulled, which makes key presence a reliable test. errorReason is one of login_wall, blocked, http_<code>, unsupported_page, empty_result or fetch_error, and error carries the human-readable message. Pages dropped by onlyPagesWithUpcomingEvents produce no row at all and no charge.
Example output
One successful page row with two events. Every key shown is the real one.
{"facebookUrl": "https://www.facebook.com/copperstilltavern","categories": ["Pub"],"info": ["The Copper Still Tavern, New York, New York. 4,821 likes","312 were here. East Village pub, live sessions Wednesdays, quiz on Sundays."],"likes": 4821,"messenger": null,"priceRange": "$$","title": "The Copper Still Tavern","address": "312 E 9th St, New York, NY, United States, New York","pageId": "100064218839471","pageName": "copperstilltavern","pageUrl": "https://www.facebook.com/copperstilltavern","intro": "East Village pub pouring Irish whiskey since 2011. Live sessions, quiz nights and private hire.","websites": ["https://copperstilltavern.com"],"phone": "+1 212-555-0148","email": "events@copperstilltavern.com","alternativeSocialMedia": "https://www.instagram.com/copperstilltavern","website": "copperstilltavern.com","services": "Private hire, live music, quiz nights","rating": "94% recommend (312 Reviews)","followers": 5230,"followings": 41,"profilePictureUrl": "https://scontent-lga3-1.xx.fbcdn.net/v/t39.30808-1/419820113_122130234884145259_n.jpg","coverPhotoUrl": "https://scontent-lga3-1.xx.fbcdn.net/v/t39.30808-6/438901776_122148812002145259_n.jpg","profilePhoto": "https://www.facebook.com/photo/?fbid=122130234884145259","ratingOverall": 94,"ratingCount": 312,"category": "Pub","addressUrl": "https://www.bing.com/maps/default.aspx?v=2&pc=FACEBK&mid=8100&where1=312+E+9th+St%2C+New+York%2C+NY%2C+United+States%2C+New+York&FORM=FBKPL1&mkt=en-US","instagram": [{"username": "copperstilltavern","url": "https://www.instagram.com/copperstilltavern"}],"ratings": "94% recommend (312 Reviews)","business_hours": "Open now","business_price": "Price Range ยท $$","business_services": "Private hire, live music, quiz nights","creation_date": "March 14, 2011","ad_status": "This Page is currently running ads.","about_me": {"text": "East Village pub pouring Irish whiskey since 2011. Live sessions, quiz nights and private hire."},"facebookId": "100064218839471","pageAdLibrary": {"is_business_page_active": true,"id": "1449158455201923"},"events": [{"eventId": "1284099376512843","name": "Wednesday Trad Session - August Residency","url": "https://www.facebook.com/events/1284099376512843/","tab": "events","dayTimeSentence": "Wednesday, August 5, 2026 at 7:00 PM EDT","startTimestamp": 1785970800,"startDate": "2026-08-05T23:00:00.000Z","endTimestamp": 1785985200,"endDate": "2026-08-06T03:00:00.000Z","timezone": "Eastern Daylight Time","isPast": false,"isUpcoming": true,"isHappeningNow": false,"isCanceled": false,"isOnline": false,"eventKind": "PUBLIC","hostName": "The Copper Still Tavern","hostId": "100064218839471","hostUrl": "https://www.facebook.com/copperstilltavern","venueName": "The Copper Still Tavern","venueAddress": "312 E 9th St, New York, NY 10003","latitude": 40.72817,"longitude": -73.98614,"ticketUrl": "https://www.eventbrite.com/e/trad-session-august-residency-tickets-884120117","description": "Weekly session in the back bar. Players welcome from 7pm, no cover, kitchen open until 10.","coverPhotoUrl": "https://scontent-lga3-1.xx.fbcdn.net/v/t39.30808-6/441207883_122150004412145259_n.jpg","interestedCount": 126,"goingCount": 40,"attendanceSentence": "126 interested ยท 40 going","detailStatus": "ok"},{"eventId": "1119874462203118","name": "Summer Whiskey Tasting","url": "https://www.facebook.com/events/1119874462203118/","tab": "past_hosted_events","dayTimeSentence": "Friday, June 12, 2026 at 7:00 PM EDT","startTimestamp": 1781305200,"startDate": "2026-06-12T23:00:00.000Z","endTimestamp": null,"endDate": null,"timezone": "Eastern Daylight Time","isPast": true,"isUpcoming": false,"isHappeningNow": false,"isCanceled": false,"isOnline": false,"eventKind": "PUBLIC","hostName": "Maura Devlin","hostId": "100091447752310","hostUrl": "https://www.facebook.com/profile.php?id=100091447752310","venueName": "The Copper Still Tavern","venueAddress": "312 E 9th St, New York, NY 10003","latitude": 40.72817,"longitude": -73.98614,"ticketUrl": null,"description": "Six pours, six stories. Hosted by our whiskey buyer.","coverPhotoUrl": "https://scontent-lga3-1.xx.fbcdn.net/v/t39.30808-6/429113550_122144098762145259_n.jpg","interestedCount": null,"goingCount": null,"attendanceSentence": null,"detailStatus": "ok"}],"eventCount": 2,"upcomingEventCount": 1,"pastEventCount": 1,"hasPublicEvents": true,"hasUpcomingEvents": true,"nextEventDate": "2026-08-05T23:00:00.000Z","lastEventDate": "2026-06-12T23:00:00.000Z","firstEventDate": "2026-06-12T23:00:00.000Z","latestEventDate": "2026-08-05T23:00:00.000Z","eventsPerMonth": 1.13,"eventsWithTicketUrl": 1,"eventsWithCoordinates": 2,"eventsSource": "events,past_hosted_events","eventTabsRead": ["events", "past_hosted_events"]}
An uncharged accounting row, written when a page could not be read:
{"facebookUrl": "https://www.facebook.com/somegatedvenue","error": "login_wall (HTTP 200)","errorReason": "login_wall"}
Limits, caps and known gaps
Worth knowing before you plan a run.
Event dates are absolute; local times are text. startTimestamp is Unix seconds and startDate is the same instant in UTC, so ordering, windowing and diffing across cities all work correctly with no timezone handling at all. Rendering the event in the venue's own local time is the harder half: timezone holds Facebook's display name for the zone, such as Eastern Daylight Time, not an IANA identifier like America/New_York, so it cannot be handed to a date library to compute an offset. When you need local wall-clock time, use dayTimeSentence, which is Facebook's own rendered sentence. timezone is also detail-page-only, so it is null for every event when includeEventDetails is off.
End times are frequently missing. endTimestamp and endDate come from a single page-level node on the event detail page, and many events simply do not publish an end time. Treat them as optional, never as a guaranteed duration.
Eight per tab is Facebook's cap, not the Actor's. The server-rendered events tab hydrates up to 8 event nodes; reading both tabs is what makes 16 the practical ceiling. There is no deeper pagination logged-out, so a venue with two hundred past events still yields at most 8 past ones.
Attendance is sparse. interestedCount and goingCount are exact when Facebook prints them in the event page's meta description and absent otherwise โ measured in the source at 5 of 66 events on 2026-07-23. Do not build a ranking that assumes they are always populated.
eventsPerMonth is a sample cadence. It is computed from the events actually collected โ at most 16 โ over the span between the earliest and the latest of them. It is a useful "is this venue active" signal, not a lifetime average.
A tab can be empty even when the page runs events. The schema records that on 8 measured business pages, 6 rendered events and 2 returned an empty tab. Those pages still produce a full, charged row, with eventCount: 0 and eventsSource set to no_tab_hydrated.
Aliased keys. pageUrl duplicates facebookUrl, facebookId duplicates pageId, ratings duplicates rating, business_services duplicates services, and about_me.text duplicates intro. They are kept for compatibility. messenger is always null.
How does it work?
The run has two phases. First, discovery: each categories and locations pair becomes a search query restricted to site:facebook.com, issued through Apify's Google SERP proxy group and paginated ten organic results at a time. Every Facebook link is resolved out of its redirect wrapper, normalised to a canonical page URL, deduplicated, and filtered down to single-segment vanity page URLs โ photo, video, group, post-permalink and personal-profile shapes are discarded before anything is fetched.
Second, extraction. Each surviving page URL is fetched over plain HTTP with a desktop Chrome header set, and the business record is read out of the <script type="application/json"> payloads Facebook server-renders into the page. The Actor then requests that page's /events and /past_hosted_events tabs, merges the two JSON node families each event is split across, sorts newest-first, truncates to maxEventsPerPage, and โ when detail fetching is on โ opens each event's own page and extracts only the nodes whose ID matches the requested event, so a suggested event's venue can never leak into your row.
Because it reads Facebook's structured payloads rather than rendered markup, a front-end redesign does not change your field names. Only publicly visible data is returned, and no cookie is used unless you supply one.
Integrations
Facebook Search Scraper & Public Events is an Apify Actor, so it works with anything that can call the Apify API or consume a dataset.
Calling Facebook Search Scraper & Public Events from Python
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("<YOUR_USERNAME>/facebook-search-scraper-public-events").call(run_input={"categories": ["Pub", "Live Music Venue"],"locations": ["NYC", "Brooklyn"],"resultsLimit": 60,"includeEventDetails": True,"maxEventsPerPage": 16,})for row in client.dataset(run["defaultDatasetId"]).iterate_items():if "error" in row:continuefor event in row["events"]:print(row["title"], event["name"], event["startDate"], event["venueName"])
Works in Go, Ruby, Node.js, cURL โ any language that can make an HTTP request. Skip rows where error is present to keep uncharged accounting rows out of your pipeline.
Scheduling and webhooks
Event data goes stale by definition, so this is a good Actor to schedule. Run the same categories and locations weekly with an Apify schedule, attach a webhook on run success to POST the dataset to your endpoint, and diff eventId sets between runs to surface only newly announced events.
No-code tools (n8n, Make, LangChain)
In n8n, use the Apify node โ or an HTTP Request node pointed at the Apify run endpoint with your token โ and pass the same JSON input shown above; an IF node on hasUpcomingEvents splits active venues from dormant ones. In Make, the Apify module supports run-and-wait, so a weekly venue sweep can feed a Google Sheets or Airtable step directly, and the Flat Event List dataset view gives you one spreadsheet row per event. In LangChain, wrap the run endpoint as a tool and hand the typed rows to the agent unchanged โ no parsing step is needed.
Is it legal to scrape Facebook public events?
Scraping publicly visible listings is broadly treated as permissible where nothing is accessed behind an authentication barrier, and this Actor reads only what a logged-out visitor sees. Most of what it returns is public-notice business data: a venue's name, category, address, opening hours, price band, rating summary, and the events it has publicly announced with their dates, venues and ticket links. For that material the regime that actually bites is contractual and database-related โ Facebook's Terms of Service, and database or unfair-competition rules covering the systematic reuse of a compiled dataset. Read Meta's current terms and decide for yourself.
A narrow part of the output is personal data, and it is worth naming precisely rather than waving at. An event's organiser can be an individual rather than a Page, in which case hostName, hostId and hostUrl identify a real person; the page-level email and phone can be a named individual's contact details on a sole-trader page; and description is authored text that can itself contain names. Under GDPR you need a lawful basis before you store or process those specific fields โ legitimate interests is the usual route for market research, and it requires a documented balancing test, a retention limit, and a way to honour deletion and objection requests. CCPA/CPRA treats the same fields as personal information about California residents. No attendee, guest-list or reviewer identity is returned at all: interestedCount and goingCount are aggregate numbers with no individuals attached.
Data minimisation is the easy control here. If you only need venue-activity signals, drop hostName, hostId, hostUrl, email, phone and description at ingest and keep the counts, the timestamps and the coordinates. Consult legal counsel if your use case involves bulk storage of personal data or outreach to identified individuals.
โ Frequently asked questions
What Facebook event fields does Facebook Search Scraper & Public Events return?
The five most used are name, startDate, venueName, ticketUrl and hostName, inside the events array on each page row. Each event object carries 30 keys and each page row carries 53 top-level keys, including the roll-ups eventCount, nextEventDate, lastEventDate and eventsPerMonth. See the data fields tables above for all of them.
Does Facebook Search Scraper & Public Events require a Facebook account, login or cookies?
No. Every surface it reads โ the business page, both event tabs and the event detail pages โ is read logged-out, which the source records as verified on 2026-07-23. The cookies input exists so you can reach an age-gated page if you need to, and it is stored encrypted; leaving it empty is the normal case. If Facebook does serve a login wall for a particular page, that page becomes an uncharged row with errorReason set to login_wall rather than failing the run.
How many pages and events can I extract in one run?
resultsLimit accepts 1 to 500 business pages per run, and maxEventsPerPage accepts 1 to 16 events per page. The 16 is a Facebook ceiling rather than an Actor preference: the server-rendered events tab hydrates at most 8 event nodes, and the Actor reads two tabs. Remember that resultsLimit is divided across your queries, so raise it as you add categories and cities.
What happens if my search returns zero events, or zero pages?
Nothing fails, and nothing is charged for the gap. If a page has no public events you still get a full page row with eventCount: 0, hasPublicEvents: false and eventsSource set to no_tab_hydrated โ that is a real, useful answer about a venue, and it is charged as a page row. If the search itself returns no Facebook business page URLs at all, the Actor logs "Search returned 0 Facebook business page URLs" and exits cleanly with an empty dataset; broaden your categories or locations, or check the log for a search-proxy warning. If an individual page cannot be read, it becomes an uncharged row: filter those out with [row for row in items if "error" not in row] and branch on row["errorReason"] for the rest.
Can I search multiple categories and cities at once?
Yes. categories and locations are both lists and the Actor takes their full cartesian product, so 10 categories and 5 cities is 50 queries in one run. Discovered page URLs are deduplicated across every query, so a venue matching two categories is scraped once. Scale resultsLimit with the query count โ it is a run total that gets divided per query, not a per-query budget.
What timezone are the event dates in, and can I convert them to local time?
startTimestamp is Unix seconds and startDate is the same instant in UTC, so both are unambiguous and sort correctly across cities without any timezone handling. Local wall-clock time is the caveat: timezone holds Facebook's display name for the zone, such as Eastern Daylight Time, not an IANA identifier like America/New_York, so it cannot be handed to a date library to compute an offset. For a human-readable local time, use dayTimeSentence โ Facebook's own rendered sentence. Both timezone and endTimestamp come only from the event detail page, so they are null when includeEventDetails is off.
Does it return past events or only upcoming ones?
Both, and by default both tabs are read. includeEvents reads the upcoming tab, includePastEvents reads the past hosted events tab, and every event carries isPast, isUpcoming and tab so you can tell them apart. Be aware that the upcoming tab also serves years-old events on some pages โ which is exactly why onlyPagesWithUpcomingEvents filters on isPast being false rather than on "has any events". On a measured sample of 8 pages recorded in the schema, only 3 had a genuinely upcoming event.
Does Facebook Search Scraper & Public Events work with Claude, ChatGPT and other AI agent tools?
Yes. It is callable as a standard HTTP endpoint through the Apify API, so LangChain, CrewAI, n8n or a hand-written tool definition can invoke it and receive typed JSON with no parsing step. Give the agent the same run input shown above and have it skip rows where error is present.
How does Facebook Search Scraper & Public Events compare to other Facebook search scrapers?
Checked on the Apify Store on 25 July 2026: apify/facebook-search-scraper is the most widely used and covers the same discovery job โ pages by keyword and location โ returning the page contact record (facebookUrl, categories, info, likes, title, address, pageId, phone, email, website, rating, ratingOverall, ratingCount, followers, creation_date, ad_status, pageAdLibrary); event extraction is not documented in its README. danek/facebook-search-ppr documents global search across posts, pages and places with location and date-range filtering plus an n8n node, and states that free users are limited to 5 results. curious_coder/facebook-post-scraper documents posts from groups, pages and search URLs with optional cookie authentication, noting that Facebook search URLs require cookies; its documented output is post fields, not events. What this Actor documents that those three do not: per-page enumeration of the public events tabs with venue coordinates and ticket links, the page-level activity roll-ups (nextEventDate, lastEventDate, eventsPerMonth), uncharged accounting rows with a typed errorReason, and a logged-out read path with no cookie requirement.
What happens when Facebook changes its layout or anti-bot system?
The scraper is maintained, and because it reads the JSON payloads Facebook server-renders into its pages rather than parsing rendered markup, a visual redesign generally does not affect it. Your field names and types stay the same on your end regardless. The genuine exposure is Facebook's internal node shapes and its login-wall behaviour, which is why block detection here keys on the HTTP status, the resolved URL and the presence of the expected container rather than on brittle string matching.
Which fields work best for AI training data and RAG indexing?
For RAG indexing: description is the high-information field and chunks cleanly on its own, with name, venueName, address and intro as useful metadata for filtering and startTimestamp for recency ranking. For training data and analytics: startTimestamp, isPast, latitude, longitude, eventCount, upcomingEventCount, pastEventCount and eventsPerMonth are the most structurally consistent fields across records, since every event object carries all 30 keys with explicit nulls rather than dropping them. All values are returned as typed primitives, arrays or plain nested objects, so no normalization pass is needed before indexing.
๐ Related scrapers
| Scraper Name | What it extracts |
|---|---|
| Facebook Pages Recent Posts Scraper | The latest posts across a list of Facebook pages |
| Facebook Posts Scraper | Posts and engagement from a single page or profile timeline |
| Facebook Group Discovery Scraper | Facebook groups around a topic or location |
| Facebook Hashtag Search Scraper | Posts published under a Facebook hashtag |
| Google Maps Scraper with Reviews Sentiment Analysis | The same venues from Google Maps, with review sentiment |
| Instagram Location Scraper by Keyword and Hashtag Filter | Location-tagged Instagram posts for the same venues |
๐ฌ Your feedback
Found a bug, or need a field that is in Facebook's event payload but not in the output? Open an issue on the Actor's Issues tab and it will be looked at. Reports that include the exact categories and locations values and the run ID are the fastest to reproduce and fix.