Facebook Search Scraper & Public Events avatar

Facebook Search Scraper & Public Events

Pricing

from $4.99 / 1,000 results

Go to Apify Store
Facebook Search Scraper & Public Events

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

Scraper Engine

Maintained by Community

Actor stats

0

Bookmarked

15

Total users

2

Monthly active users

5 days ago

Last modified

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 sendThe 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": 0Falls back to 10
"maxEventsPerPage": 0Falls 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 CategoryPublic, logged-outGated 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 tabDeeper 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 descriptionReliable 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 NameDescription
facebookUrlCanonical page URL the row was built from
pageUrlThe same URL again, kept for compatibility with the base page shape
pageIdNumeric Facebook page ID
facebookIdThe same numeric ID again
pageNameURL vanity slug, e.g. the copperstilltavern in the page URL
titlePage display name
categoriesArray of the page's category names
categoryFirst entry of categories, or null
introThe page's own "best description" text
about_meObject { "text": <intro> } โ€” the same text nested
infoArray of description fragments split out of the page meta description
addressStreet address as printed on the page
addressUrlA Bing Maps link built from address โ€” Facebook's own map link format, not a Google Maps URL
phonePhone number, taken from the page entity first and the intro card second
emailContact email from the intro card
websiteBare domain of the page's website, www. stripped
websitesArray holding the same website with an https:// scheme added
instagramArray of { "username", "url" } objects for a linked Instagram account
alternativeSocialMediaFirst non-Facebook external account URL found on the intro cards
messengerAlways null โ€” reserved key, never populated
profilePictureUrlProfile picture image URL
profilePhotoFacebook photo permalink for the profile picture, built as photo/?fbid=<id>
coverPhotoUrlPage cover photo image URL
servicesServices text from the intro card, dropped to null when it runs over 100 characters or matches known junk
business_servicesThe same services value
business_hoursOpening-hours text or an "Open now" / "Closed now" state
priceRangeNormalised price band, e.g. $$. Non-dollar currency symbols are preserved verbatim
business_price"Price Range ยท <priceRange>"
ratingRecommendation sentence, e.g. "94% recommend (312 Reviews)"
ratingsThe same sentence again
ratingOverallRecommendation percentage as an integer
ratingCountReview count as an integer, thousands separators handled
likesPage likes as an integer
followersFollowers as an integer, with 1.2K / 3.4M shorthand expanded
followingsAccounts the page follows, as an integer
creation_datePage 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
pageAdLibraryObject { "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 NameDescription
eventsArray of event objects, newest first, capped at maxEventsPerPage
eventCountNumber of events in events
upcomingEventCountHow many of them have isPast exactly false
pastEventCountHow many have isPast exactly true
hasPublicEventstrue when events is non-empty
hasUpcomingEventstrue when at least one event has isPast false โ€” this is what onlyPagesWithUpcomingEvents filters on
nextEventDateUTC timestamp of the soonest upcoming event, null if there is none
lastEventDateUTC timestamp of the most recent past event โ€” deliberately ignores future dates, so "this venue last ran an event in 2017" stays readable
firstEventDateEarliest start time across the collected events
latestEventDateLatest start time across the collected events
eventsPerMonthEvent 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
eventsWithTicketUrlHow many collected events carry a ticketUrl
eventsWithCoordinatesHow many carry latitude
eventTabsReadArray of the tabs that actually hydrated: events, past_hosted_events, or both
eventsSourceThe 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 NameDescription
eventIdNumeric Facebook event ID
nameEvent title
urlCanonical event URL, built as https://www.facebook.com/events/<eventId>/ when Facebook does not supply one
tabWhich tab produced this event: events or past_hosted_events
dayTimeSentenceFacebook's own rendered date sentence, in the venue's local wall-clock time
startTimestampStart time as Unix seconds โ€” an absolute instant
startDateThe same instant as a UTC ISO-8601 string, YYYY-MM-DDTHH:MM:SS.000Z
endTimestampEnd time as Unix seconds. Only ever populated from the event detail page, and only when Facebook ships it
endDateThe same end instant as a UTC ISO-8601 string
timezoneFacebook's timezone display name for the event, e.g. Eastern Daylight Time โ€” a human-readable label, not an IANA zone ID. Detail page only
isPastFacebook's own past flag
isUpcomingDerived as the inverse of isPast; null when isPast is null
isHappeningNowFacebook's live flag, as of when the tab was fetched
isCanceledCancelled flag
isOnlineFacebook's online-or-detected-online flag
eventKindFacebook's own event kind classifier
hostNameOrganiser name โ€” a Page name, or a person's name when an individual hosts
hostIdOrganiser's numeric Facebook ID
hostUrlOrganiser's Facebook URL, falling back to profile.php?id=<hostId>
venueNameVenue name, from the event's place object
venueAddressOne-line venue address. Detail page only
latitudeVenue latitude as a number. Detail page only
longitudeVenue longitude as a number. Detail page only
ticketUrlExternal ticket-purchase URL when the organiser set one. Detail page only
descriptionFull event description text. Detail page only
coverPhotoUrlEvent cover image URL. Detail page only
interestedCountPeople marked interested, as an exact integer โ€” see the caveat below
goingCountPeople marked going, as an exact integer. Can be null while interestedCount is set
attendanceSentenceThe same two numbers as a display string, e.g. "126 interested ยท 40 going"
detailStatusPer-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.

  1. Open Facebook Search Scraper & Public Events on Apify and click Try for free
  2. Fill in Categories (categories) โ€” the business types you want, such as Pub, Live Music Venue, Nightclub
  3. 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
  4. Set Results Limit (resultsLimit) โ€” the cap on business pages scraped across all queries, not per query
  5. 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
  6. 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 hasUpcomingEvents and sorts by eventsPerMonth to reach the pubs that actually programme events, then works phone and email straight off the same row.
  • ๐ŸŽŸ๏ธ An event promoter researching a city pulls Live Music Venue across five cities and uses nextEventDate, venueName, latitude and longitude to map who has something on and where, before booking a route.
  • ๐Ÿ“‰ A local-marketing analyst spots dormant venues by filtering for a lastEventDate older than a year with upcomingEventCount at 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 ticketUrl being non-null to find organisers already selling through an external platform, and reads hostName and hostUrl to identify who owns the relationship.
  • ๐Ÿค– An AI engineer building a local-events assistant indexes description, name and venueName into a vector store with startTimestamp, latitude and longitude as 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.

ParameterRequiredTypeDescriptionExample Value
categoriesNoarrayBusiness types to search (Pub, Bar, Restaurant...). Combined with locations, e.g. "Pub NYC", "Bar London". Default ["Pub"].["Pub", "Live Music Venue"]
locationsNoarrayWhere to search (NYC, London...) โ€” paired with each category. Default ["NYC"].["NYC", "Brooklyn"]
resultsLimitNointegerMax pages to scrape across all queries. Higher means more data and a longer run. Minimum 1, maximum 500. Default 10.60
proxyConfigurationNoobjectOptional. No proxy by default. Auto fallback to datacenter then residential if blocked. Prefilled {"useApifyProxy": false}.{"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]}
includeEventsNobooleanRead the page's upcoming events tab. Facebook returns up to 8 event nodes per page. Default true.true
includePastEventsNobooleanAlso read the page's past hosted events tab. Default true.true
includeEventDetailsNobooleanOne 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
maxEventsPerPageNointegerCap on events kept per business page, newest first. Minimum 1, maximum 16. Default 8.16
onlyPagesWithUpcomingEventsNobooleanKeep only pages that have at least one event with isPast false. Default false.false
cookiesNoobjectOptional 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:

  • onlyPagesWithUpcomingEvents is a post-fetch filter, and so is maxEventsPerPage. Neither is sent to Facebook. The page is fetched, both event tabs are read, the events are sorted newest-first and only then truncated to maxEventsPerPage; 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. locations is a word appended to the search query, so which city you actually get is whatever the search engine decides is relevant. Filter on startDate or startTimestamp after the run if you need a window.
  • maxEventsPerPage caps 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 NYC or NY, and resultsLimit is above 100, the Actor silently adds a second query with New York substituted. 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 ApifyClient
client = 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:
continue
for 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.

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.

Scraper NameWhat it extracts
Facebook Pages Recent Posts ScraperThe latest posts across a list of Facebook pages
Facebook Posts ScraperPosts and engagement from a single page or profile timeline
Facebook Group Discovery ScraperFacebook groups around a topic or location
Facebook Hashtag Search ScraperPosts published under a Facebook hashtag
Google Maps Scraper with Reviews Sentiment AnalysisThe same venues from Google Maps, with review sentiment
Instagram Location Scraper by Keyword and Hashtag FilterLocation-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.