Facebook Event Search Scraper: Upcoming, Past & Recurring avatar

Facebook Event Search Scraper: Upcoming, Past & Recurring

Pricing

from $5.99 / 1,000 results

Go to Apify Store
Facebook Event Search Scraper: Upcoming, Past & Recurring

Facebook Event Search Scraper: Upcoming, Past & Recurring

Facebook Event Search Scraper finds upcoming, past, and recurring Facebook events with key details such as event names, dates, locations, descriptions, organizers, and event URLs. Ideal for event discovery, competitor research, local marketing, audience analysis, and lead generation.

Pricing

from $5.99 / 1,000 results

Rating

0.0

(0)

Developer

Scrapier

Scrapier

Maintained by Community

Actor stats

0

Bookmarked

3

Total users

0

Monthly active users

3 days ago

Last modified

Share

Facebook Event Search Scraper β€” Recurring Dates Reconciled

Search public Facebook Events by topic and get back a clean, structured record for every match β€” with a start date that has been checked, not just copied. Facebook's own event cards list a fixed timestamp that is wrong on repeating events (it's the first occurrence of the series, not the next one); this Actor reconciles that timestamp against the event's own published schedule sentence and labels which source it trusted and by how many days they disagreed. Every response is structured JSON, ready to pass directly to an LLM, index into a vector store, or feed a monitoring pipeline. No Facebook login is required to run it.

What is Facebook Event Search Scraper: Upcoming, Past & Recurring?

It's an Apify Actor that searches Facebook's public Events search (facebook.com/events/search) for one or more topics and returns every matching event as a normalized row β€” title, URL, resolved start date, and a schedule audit trail. Its defining capability is reconciling recurring-event dates: rather than trusting Facebook's card timestamp or its "upcoming/past" label blindly (both are documented to be wrong on repeating series), it parses the event's own schedule sentence, compares the two, and reports startTimeSource, startTimeIsSeriesStart, and scheduleDiscrepancyDays on every row. No Facebook account or login is needed β€” the Actor reads only what a signed-out visitor can see.

  • 🎯 Search by any number of topics or phrases, one per line
  • πŸ“† Filter to a date window using absolute dates or relative offsets ("7 days", "+15 days")
  • ⏳ Split results into upcoming-only, past-only, or both β€” using the reconciled date, not Facebook's own marker
  • πŸ” Include, exclude, or isolate recurring/repeating events
  • 🧾 Optional schedule-breakdown columns (published schedule text, local start text, timezone label, occurrence count)
  • πŸ” Optional discrepancy-report columns showing exactly where the raw listing and the reconciled date disagreed

What data can you get with Facebook Event Search Scraper: Upcoming, Past & Recurring?

Every run produces one row per matching event, plus a single run-summary row that reports what each filter removed.

Result TypeExtracted FieldsPrimary Use Case
Search eventevent_id, title, url, query, startsAtUtc, isUpcoming, daysUntilEvent, isRecurring, inRequestedWindow, windowFilterApplied, scrapedAtEvent discovery, calendars, lead lists
Schedule breakdown (optional)scheduleText, startsAtLocalText, scheduleTimezoneLabel, additionalOccurrenceCount, isDateRange, nextOccurrenceTextShowing the published schedule as written
Schedule discrepancy report (optional)startTimeSource, startTimeIsSeriesStart, scheduleDiscrepancyDays, cardStartsAtUtc, timezoneReconciledAuditing which date source was trusted, and why
Run summaryappliedTiming, appliedRecurringPolicy, candidatesCollected, rowsEmitted, filteredOutByTiming, filteredOutByWindow, filteredOutByRecurring, undatedExcluded, recurringRowsEmitted, seriesStartCorrections, distinctTimezoneLabels, resultPagesWalked, topicsRequested, topicsWithResultsConfirming what a run actually did before trusting the results

Verified recurring event dates

On a repeating event, Facebook's search card carries the timestamp of the first occurrence in the series β€” not the next upcoming one β€” while the human-readable schedule sentence on the same card ("Sat, 20 Sep at 6:00 PM EDT and 3 more") usually names the real next date. The Actor never picks one silently: it parses the sentence into structured facts (day, month, year, clock time, timezone label), converts the card's Unix timestamp into UTC, and compares the two. If they land more than one calendar day apart β€” more than a timezone shift could explain β€” it trusts the sentence and flags startTimeIsSeriesStart: true. Every row then carries startTimeSource ("card_timestamp" or "schedule_sentence"), scheduleDiscrepancyDays (the gap in days), and timezoneReconciled (whether the timezone label on the card matched the offset implied by comparing both sources). This only runs when reportScheduleDiscrepancy is left on.

{
"startTimeSource": "schedule_sentence",
"startTimeIsSeriesStart": true,
"scheduleDiscrepancyDays": 14,
"cardStartsAtUtc": "2026-09-06T22:00:00.000Z",
"timezoneReconciled": true
}

Run summary row

Every completed run pushes one extra row (type: "scheduleSummary") reporting how many candidate events were found across all topics, how many were kept, and how many were removed by each individual filter β€” timing, date window, recurring policy, or missing a date entirely. It's the audit trail for trusting a run's output without re-checking it by hand: candidatesCollected vs rowsEmitted, plus a per-filter breakdown of the difference.

Why not build this yourself?

Meta's Graph API does not expose a public, keyword-based search over Facebook Events to third-party developers β€” the old FQL-based event search was deprecated years ago, and general third-party access to the Events edge has been further restricted since (observed via Meta's own developer documentation and community reports on deprecated event-search tooling, checked 2026-08-15). There is no supported official endpoint where you can send a topic string and get back matching public events. Building this yourself means reverse-engineering Facebook's internal GraphQL search API, handling its cursor-based pagination, keeping a TLS/browser fingerprint that doesn't get flagged, and β€” the harder problem β€” writing and maintaining your own date-reconciliation logic for recurring events, since Facebook's own timestamp field is not reliable for that case. This Actor already does all of that and re-exposes it as a single Apify Actor input.

How to scrape Facebook events with Facebook Event Search Scraper: Upcoming, Past & Recurring?

  1. Open the Actor on its Apify Store listing and click Try for free (or Start) to open the input form in the Apify Console.
  2. Enter one or more search phrases in Event topics (eventTopics) β€” one per line, e.g. "conference", "yoga retreat".
  3. Set the real query controls that matter to you: Earliest/latest event date (dateFrom / dateTo), Upcoming or past (eventTiming), and Repeating events (recurringPolicy).
  4. Click Start to run the Actor.
  5. Download the results as JSON, CSV, Excel, or any other format Apify's dataset export supports, or pull them programmatically via the Apify API / apify-client.

How to run multiple queries in one job

eventTopics accepts a list β€” every line is searched independently and all results land in the same dataset, tagged with the query field that produced each row. There is no documented concurrency setting in this Actor; topics are walked one after another within a single run.

⬇️ Input

ParameterRequiredTypeDescriptionExample Value
eventTopicsNoarray (stringList)Topics or phrases to search, one per line β€” for example "conference", "yoga retreat", "art exhibition". Short, specific phrases work best.["conference", "workshop"]
resultsPerTopicNointeger (min 1, max 100000)How many MATCHING events to keep for each topic. Filters are applied to the resolved date, so a narrow window keeps looking further down the results until it has this many matches or the topic runs out.50
dateFromNostring (datepicker, absolute or relative)Keep only events starting on or after this date. Accepts a date such as 2026-09-01, a distance into the past such as "7 days", or a signed distance such as "+15 days" to start the window in the future."2026-09-01"
dateToNostring (datepicker, absolute or relative)Keep only events starting on or before this date. Accepts a date such as 2026-09-30, a distance into the future such as "30 days", or a signed distance such as "-1 day" to end the window in the past."30 days"
eventTimingNostring (enum)Split the results by whether the resolved start date is still ahead or already behind. This uses the reconciled date, not the listing's own past/upcoming marker, which is wrong on repeating events. Values: both (Both β€” upcoming and past), upcoming (Upcoming only), past (Past only)."both"
recurringPolicyNostring (enum)A repeating event publishes a next date plus a count of further occurrences. Choose whether those events are kept, removed, or the only ones you want. Values: include (Include repeating events), exclude (Exclude repeating events), only (Only repeating events)."include"
emitScheduleBreakdownNobooleanAdds the published schedule line, the local start text, the timezone label as written, the number of further occurrences, and whether the entry covers a range of days.true
reportScheduleDiscrepancyNobooleanAdds the columns that show which source the start date came from, the listing's own raw date, whether that raw date was the first date of a repeating series, and how many days the two sources disagreed by.true
startUrlsNoarray (stringList)Same meaning as Event topics. Used only when Event topics is left empty. (Original input key, kept for backward compatibility.)["conference"]
maxEventsNointeger (min 1, max 100000)Same meaning as Matching events per topic. Used only when that field is left empty. (Original input key, kept for backward compatibility.)50
proxyConfigurationNoobject (proxy)Choose how the run connects. Leaving it switched off keeps the run on a direct connection.{"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]}

No parameter is required β€” running with no input at all fails, though: the Actor needs at least one topic in either eventTopics or startUrls.

Example JSON input:

{
"eventTopics": ["jazz festival", "startup networking", "yoga retreat"],
"resultsPerTopic": 50,
"dateFrom": "2026-09-01",
"dateTo": "60 days",
"eventTiming": "upcoming",
"recurringPolicy": "include",
"emitScheduleBreakdown": true,
"reportScheduleDiscrepancy": true,
"proxyConfiguration": {
"useApifyProxy": true,
"apifyProxyGroups": ["RESIDENTIAL"]
}
}

Common pitfall: a plain calendar date in dateFrom / dateTo covers the entire day it names β€” a dateTo of today's date still includes everything happening later today, it isn't clamped to "right now." Leave both dates empty to keep every date instead of guessing a wide window.

⬆️ Output

Each run pushes typed, normalized JSON rows to the Apify dataset β€” one row per matching event, plus one run-summary row at the end. Export as JSON, CSV, Excel, HTML, or the other formats Apify's dataset export supports. The dataset ships two views: a default Events view and a Run summary view; both read from the same dataset, filtered by the type field ("search_event" vs "scheduleSummary").

Scraped results

[
{
"type": "search_event",
"isChild": false,
"event_id": "1284739201038475",
"title": "Downtown Jazz Festival 2026",
"url": "https://www.facebook.com/events/1284739201038475/",
"query": "jazz festival",
"startsAtUtc": "2026-09-19T23:00:00.000Z",
"isUpcoming": true,
"daysUntilEvent": 35,
"isRecurring": false,
"inRequestedWindow": true,
"windowFilterApplied": true,
"scrapedAt": "2026-08-15T09:12:03.441Z",
"scheduleText": "Sat, 19 Sep at 7:00 PM EDT",
"startsAtLocalText": "2026-09-19 19:00",
"scheduleTimezoneLabel": "EDT",
"additionalOccurrenceCount": null,
"isDateRange": false,
"nextOccurrenceText": "Sat, 19 Sep at 7:00 PM EDT",
"startTimeSource": "card_timestamp",
"startTimeIsSeriesStart": false,
"scheduleDiscrepancyDays": 0,
"cardStartsAtUtc": "2026-09-19T23:00:00.000Z",
"timezoneReconciled": true
},
{
"type": "search_event",
"isChild": false,
"event_id": "9938271004456621",
"title": "Weekly Startup Networking Mixer",
"url": "https://www.facebook.com/events/9938271004456621/",
"query": "startup networking",
"startsAtUtc": "2026-09-24T22:30:00.000Z",
"isUpcoming": true,
"daysUntilEvent": 40,
"isRecurring": true,
"inRequestedWindow": true,
"windowFilterApplied": true,
"scrapedAt": "2026-08-15T09:12:11.902Z",
"scheduleText": "Thu, 24 Sep at 6:30 PM EDT and 11 more",
"startsAtLocalText": "2026-09-24 18:30",
"scheduleTimezoneLabel": "EDT",
"additionalOccurrenceCount": 11,
"isDateRange": false,
"nextOccurrenceText": "Thu, 24 Sep at 6:30 PM EDT",
"startTimeSource": "schedule_sentence",
"startTimeIsSeriesStart": true,
"scheduleDiscrepancyDays": 21,
"cardStartsAtUtc": "2026-09-03T22:30:00.000Z",
"timezoneReconciled": true
},
{
"type": "search_event",
"isChild": false,
"event_id": "5512093847210099",
"title": "Sunrise Yoga Retreat β€” Coastal Weekend",
"url": "https://www.facebook.com/events/5512093847210099/",
"query": "yoga retreat",
"startsAtUtc": "2026-10-02T12:00:00.000Z",
"isUpcoming": true,
"daysUntilEvent": 48,
"isRecurring": false,
"inRequestedWindow": true,
"windowFilterApplied": true,
"scrapedAt": "2026-08-15T09:12:19.207Z",
"scheduleText": "Fri, 2 Oct-4 Oct",
"startsAtLocalText": "2026-10-02",
"scheduleTimezoneLabel": null,
"additionalOccurrenceCount": null,
"isDateRange": true,
"nextOccurrenceText": "Fri, 2 Oct-4 Oct",
"startTimeSource": "card_timestamp",
"startTimeIsSeriesStart": false,
"scheduleDiscrepancyDays": 0,
"cardStartsAtUtc": "2026-10-02T12:00:00.000Z",
"timezoneReconciled": null
},
{
"type": "scheduleSummary",
"isChild": false,
"query": "jazz festival, startup networking, yoga retreat",
"appliedTiming": "upcoming",
"appliedRecurringPolicy": "include",
"windowFilterApplied": true,
"windowFromUtc": "2026-09-01T00:00:00.000Z",
"windowToUtc": "2026-10-14T23:59:59.999Z",
"earliestEventInRun": "2026-09-19T23:00:00.000Z",
"latestEventInRun": "2026-10-02T12:00:00.000Z",
"candidatesCollected": 47,
"rowsEmitted": 3,
"filteredOutByTiming": 6,
"filteredOutByWindow": 34,
"filteredOutByRecurring": 0,
"undatedExcluded": 4,
"recurringRowsEmitted": 1,
"seriesStartCorrections": 1,
"distinctTimezoneLabels": 1,
"resultPagesWalked": 9,
"topicsRequested": 3,
"topicsWithResults": 3,
"scrapedAt": "2026-08-15T09:12:20.115Z"
}
]

isChild is present on every row for schema stability but is always false in this Actor's current output β€” no child-event rows are ever emitted. Filter it out with row.type !== "scheduleSummary" if you only want event rows.

How can I use the data extracted with Facebook Event Search Scraper: Upcoming, Past & Recurring?

  • πŸ“… Event marketers and community managers: build a curated calendar of upcoming events for a topic or niche, and use startsAtUtc / isUpcoming instead of Facebook's raw label, which the source data shows to be unreliable on recurring listings.
  • πŸ€– AI engineers and LLM developers: feed the query in, receive structured JSON back, and pass title, scheduleText, and url directly into a retrieval-augmented agent or event-recommendation assistant.
  • πŸ“Š Market and community researchers: track how many events match a topic over time, and use recurringRowsEmitted and seriesStartCorrections from the run summary to gauge how much of a category is recurring programming versus one-off events.
  • 🧭 Product and growth teams at event platforms: use scheduleDiscrepancyDays and startTimeIsSeriesStart as a data-quality signal when cross-referencing Facebook listings against your own event database.

How do you monitor recurring event dates over time?

Facebook event listings change: a recurring series adds occurrences, a one-off event's date sentence gets edited, or a new event matching your topic gets published. Because every row is keyed on event_id, you can run the same eventTopics list on a schedule and diff each run's startsAtUtc, scheduleDiscrepancyDays, and additionalOccurrenceCount against the previous run's values for the same event_id. A jump in scheduleDiscrepancyDays or a flip in startTimeIsSeriesStart means the organizer changed the schedule since the last run; a new event_id appearing means a new event now matches your topic. The scheduleSummary row's candidatesCollected vs rowsEmitted gap is a quick sanity check that the filters didn't silently drop something between runs. To automate this, set up an Apify schedule to run the Actor on an interval, then compare each run's dataset against the last one β€” either by pulling both datasets via the Apify API and diffing them yourself, or by piping results into a webhook-triggered comparison step.

Integrate Facebook Event Search Scraper: Upcoming, Past & Recurring and automate your workflow

Facebook Event Search Scraper: Upcoming, Past & Recurring works with any language or tool that can send an HTTP request, through the Apify API.

REST API with Python

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_API_TOKEN>")
run_input = {
"eventTopics": ["jazz festival", "startup networking"],
"resultsPerTopic": 50,
"eventTiming": "upcoming",
"recurringPolicy": "include",
}
run = client.actor("scrapier/facebook-event-search-scraper-upcoming-past-and-recurring").call(
run_input=run_input
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
if item.get("type") == "search_event":
print(item["title"], item["startsAtUtc"])

Scheduled monitoring and delivery

Set an Apify schedule to re-run the Actor with the same input on an interval, and attach an Apify webhook on the ACTOR.RUN.SUCCEEDED event to push each run's dataset to your own endpoint automatically instead of polling for it.

Yes β€” scraping publicly accessible Facebook Events is generally lawful; this Actor reads only what a signed-out visitor can already see on facebook.com/events/search, no login required. Courts in the United States have held that scraping data a website makes publicly available does not itself violate the Computer Fraud and Abuse Act β€” see hiQ Labs, Inc. v. LinkedIn Corp., 9th Circuit, 2019. This Actor's output is public event listing data (titles, schedules, URLs) rather than personal profile data, so it falls under Facebook's own Terms of Service and general database-rights considerations rather than GDPR/CCPA, which govern personal data specifically. ⚠️ Scraping for bulk archival or AI training carries a different risk profile than one-off operational monitoring β€” consult your legal team for commercial use cases involving bulk data storage.

❓ Frequently asked questions

Does this Actor require a Facebook login?

No. It fetches the public facebook.com/events/search page and Facebook's public search GraphQL endpoint directly β€” no Facebook account, cookies, or login credentials are used or requested anywhere in the input.

How does the date filter differ from Facebook's own "upcoming/past" label?

eventTiming filters on the reconciled start date this Actor computes β€” not Facebook's own upcoming/past marker on the card, which the input schema documents as wrong on repeating events (it reflects the first occurrence of a series, not the next one). If you set eventTiming to upcoming, you get events whose reconciled date is still ahead of now, even if Facebook's own UI would have labeled the card differently.

Does Facebook Event Search Scraper: Upcoming, Past & Recurring extract recurring-event schedule data?

Yes. Every row carries isRecurring and additionalOccurrenceCount by default. With reportScheduleDiscrepancy left on (its default), each row also carries startTimeSource, startTimeIsSeriesStart, scheduleDiscrepancyDays, cardStartsAtUtc, and timezoneReconciled, showing exactly which date source was trusted and how far the two disagreed. These discrepancy fields are only present when reportScheduleDiscrepancy is true.

How many results does it return per query?

resultsPerTopic (or the legacy maxEvents key) controls how many matching events are kept per topic, from 1 up to 100000, defaulting to 50 if left empty. Internally, the search walk for one topic is capped at a 25-page safety ceiling (20 raw candidate events per page, so roughly 500 raw candidates read before filters), which exists only to stop a runaway loop β€” in practice Facebook's own result list for a topic runs dry well before that ceiling is reached.

How does this Actor handle Facebook's anti-bot measures?

It fetches pages through curl_cffi with a Chrome TLS/browser fingerprint rather than a generic HTTP client, retries a failed page load up to 3 times with a short delay, and detects rate-limited responses by error code or message text. When a rate limit is detected (and when Apify Proxy is enabled), it rotates to a fresh sticky proxy session β€” bounded to 3 rotations per run β€” and never abandons or swaps the network selection the buyer made in proxyConfiguration.

How do I monitor a topic's events over time?

Run the same eventTopics list on an Apify schedule, then compare each run's rows by event_id against the previous run's β€” watch for new event_ids, changes in startsAtUtc, or a change in scheduleDiscrepancyDays / startTimeIsSeriesStart that signals the organizer edited the schedule.

Does this Actor work with Claude, ChatGPT, and AI agent frameworks?

It doesn't expose an MCP server, but it is callable as a standard HTTP endpoint through the Apify API and apify-client libraries from any language, so any agent framework that can issue an HTTP request and read JSON back can call it as a tool to ground a response in live Facebook event search results.

Can I use this Actor without managing proxies or Facebook credentials?

Yes. No Facebook credentials are ever required. Leaving proxyConfiguration switched off runs the Actor on a direct connection; turning it on hands proxy selection, session management, and rotation-on-failure to the Actor, using whichever Apify Proxy group you selected (residential is prefilled by default).

What happens if a search topic can't be loaded at all?

If none of the requested topics load successfully, the run is reported as failed, not as an empty success β€” this distinguishes "there genuinely were no matching events" (which still produces a completed run with a summary row) from "the search itself couldn't be reached this time" (which fails the run outright, with no partial data pushed).

Can I still use the original startUrls / maxEvents input keys?

Yes. startUrls is used only when eventTopics is left empty, and maxEvents only when resultsPerTopic is left empty β€” both exist so input built for the original actor keeps working unchanged.

πŸ’¬ Your feedback

Found a bug or missing a field? Let us know β€” open an issue through the Actor's page on the Apify Store or contact Scrapier through Apify Console support, and we'll take a look.