Skip to main content
Blog

Facebook events scraper for public event urls

Use the facebook events scraper to extract public event data, ticket links, hosts, and response counts from Facebook event URLs.

ScrapersFacebookMay 14, 2026
Facebook events scraper for public event urls

ScrapeNow's Facebook Events scraper extracts event ID, title, date, venue, host pages, ticket link, response counts, description, images, and suggested events from public Facebook event URLs. Event teams, ticketing teams, local data teams, and lead-gen pipelines use it to get structured event records without maintaining browser automation.

Use it when your input is a public Facebook Event page. The scraper returns one JSON object per submitted URL, with enough fields to dedupe records, track attendance signals, store ticket metadata, and discover related events.

How to use this scraper

Facebook Events Scraper job pipeline
The Facebook Events Scraper job pipeline, from input to stored output.

Use Extract Facebook event data when you already have event URLs. Use Find Facebook events by venue when you need events from a venue page.

The API flow has 3 calls:

  1. Start a scrape job with your input URLs.
  2. Poll the job until it finishes.
  3. Download results as JSON.

This pattern works well for queues, scheduled venue crawls, and one-off enrichment jobs. Keep the scrape job separate from your ingestion job so retries do not duplicate records in your database.

Get the event URL input

The url input must be a specific Facebook event URL. It must start with https://www.facebook.com/.

Open facebook.com.

Facebook home feed with Events link highlighted in left sidebar
Click the Events tab in the left sidebar to browse events

On the Home screen, click the Events tab in the left sidebar.

In the search bar, type the keyword for the event you want. This example uses Together Together.

Facebook Discover events page searching keyword together together
Type your event keyword in the search bar to find matching events

On the results page, click the event you want.

Facebook event search results listing Harry Styles Together Together shows
Select the target event from the search results list

Copy the URL from the address bar.

Facebook event page for Harry Styles Melbourne show with URL
Copy the event URL from the address bar for scraper input

For the scraper used in this example, the input is:

[
  {
    "url": "https://www.facebook.com/events/2067708210743589"
  }
]

Use the canonical event URL when you have it. Remove tracking parameters before storing the URL, since the event_id gives you the stable key.

Run the API request

Use this Python script. Replace YOUR_API_KEY with your ScrapeNow scraper API key.

"""
Configuration:
    - Set SCRAPER_SLUG to the scraper you want to run.
    - Set SCRAPER_INPUTS to the list of input dicts matching that scraper's schema.
    - Set API_KEY to your scraper API key.
"""

import sys
import time
import json
import requests
import os

API_KEY = "YOUR_API_KEY"

SCRAPER_SLUG = "facebook-events-extract-by-url"

SCRAPER_INPUTS = [
    {
        "url": "https://www.facebook.com/events/2067708210743589"
    }
]



BASE_URL = "https://api.scrapenow.io/api/v1/scraping"
TIMEOUT_SECONDS = 3600
POLL_INTERVAL = 5
SPINNER = "|/-\\"


def build_headers(api_key: str, content_type: str | None = None) -> dict:
    headers = {"Authorization": f"Bearer {api_key}"}
    if content_type:
        headers["Content-Type"] = content_type
    return headers


def trigger_scrape(slug: str, inputs: list[dict]) -> str:
    url = f"{BASE_URL}/scrape?scraper={slug}"
    response = requests.post(
        url,
        headers=build_headers(API_KEY, "application/json"),
        json={"inputs": inputs},
    )
    response.raise_for_status()
    return response.json()["data"]["job_id"]


def poll_until_done(job_id: str) -> str:
    start = time.time()
    i = 0
    while True:
        elapsed = time.time() - start
        if elapsed > TIMEOUT_SECONDS:
            print(f"\nTimeout after {TIMEOUT_SECONDS}s")
            sys.exit(1)
        response = requests.get(
            f"{BASE_URL}/jobs/{job_id}",
            headers=build_headers(API_KEY),
        )
        response.raise_for_status()
        data = response.json()
        status = data["data"]["status"]
        mins, secs = divmod(int(elapsed), 60)
        sys.stdout.write(
            f"\r[{SPINNER[i % 4]}] Waiting... {status} ({mins}m {secs:02d}s)  "
        )
        sys.stdout.flush()
        if status in ("completed", "failed"):
            print()
            return status
        time.sleep(POLL_INTERVAL)
        i += 1


def fetch_results(job_id: str) -> dict:
    response = requests.get(
        f"{BASE_URL}/jobs/{job_id}/results?format=json",
        headers=build_headers(API_KEY),
    )
    response.raise_for_status()
    return response.json()


def save_results(data: dict, slug: str) -> str:
    os.makedirs("output", exist_ok=True)
    filename = os.path.join("output", f"{slug}.json")
    with open(filename, "w", encoding="utf-8") as f:
        json.dump(data, f, indent=2, ensure_ascii=False)
    return filename


def main() -> None:
    print(f"Triggering scraper: {SCRAPER_SLUG}")
    job_id = trigger_scrape(SCRAPER_SLUG, SCRAPER_INPUTS)
    print(f"Job started: {job_id}")
    final_status = poll_until_done(job_id)
    if final_status != "completed":
        print(f"Job failed with status: {final_status}")
        sys.exit(1)
    print("Fetching results...")
    results = fetch_results(job_id)
    output_file = save_results(results, SCRAPER_SLUG)
    print(f"Results saved to: {output_file}")


if __name__ == "__main__":
    main()

The same API pattern works for the other scrapers in this group, including Find Facebook events by venue. Change the scraper slug and input values in the code for each scraper.

For batch jobs, send multiple input objects in SCRAPER_INPUTS. Keep batches small enough that a failed job is cheap to rerun.

Search events by venue

For venue-based extraction, the url input points to a venue’s Events page. It must start with https://www.facebook.com/.

Open facebook.com.

Facebook search bar suggesting Hollywood Bowl venue autocomplete options
Type the venue name in the Facebook search bar to find business pages

In the search bar, type the business or venue name. This example uses Hollywood Bowl.

On the search result page, choose the Pages filter in the left sidebar. This removes personal profiles, posts, and marketplace results from the page list.

Facebook Hollywood Bowl search results with Pages filter highlighted
Select the Pages filter to narrow results to business pages only

Choose the business page and open the profile.

Facebook Pages results listing Hollywood Bowl concert venue entries
Click the verified Hollywood Bowl page from the filtered results

In the profile navigation menu, click the Events section. Facebook often places it under the More tab on pages with crowded navigation.

Facebook Hollywood Bowl page More menu showing Events option
Click the More tab and select Events from the dropdown menu

Copy the URL from the address bar.

Facebook Hollywood Bowl page Events tab showing upcoming events
Copy the venue Events URL from the address bar for scraper input

The venue search scraper also accepts upcoming_only. Send it as the string "true" or "false" when using the API.

[
  {
    "url": "https://www.facebook.com/HollywoodBowl/events",
    "upcoming_only": "true"
  }
]

Use "true" for production venue tracking. Historical events add noise unless your use case needs archive data or promoter history.

Example JSON output

This is a trimmed response from the Facebook Events Extract by URL scraper.

[
  {
    "inputs": {
      "url": "https://www.facebook.com/events/2067708210743589"
    },
    "scrape_status": "success",
    "event_id": "2067708210743589",
    "url": "https://www.facebook.com/events/2067708210743589/",
    "main_image": null,
    "event_date": "2026-06-12T16:00:00.000Z",
    "title": "Harry Styles: Together, Together",
    "people_responded": 765,
    "event_by": null,
    "location": {
      "address": "Wembley Stadium connected by EE",
      "url": "https://facebook.com/WembleyStadium"
    },
    "access_level": [
      "PUBLIC_TYPE"
    ],
    "description": {
      "text": "There is a ticket limit of 8 per onsale (Album Pre-Order Pre-Sale, AMEX Presale and General Sale).Sale Dates and Times:Public Onsale : Fri, 30 Jan 2026 at 11:00 AMAmerican Express Presale : Mon, 26 Jan 2026 at 11:00 AMAlbum Pre-Order Pre-Sale : Mon, 26 Jan 2026 at 11:00 AM",
      "links": [],
      "hashtags": null
    },
    "hashtags": null,
    "hosts": [
      {
        "name": "Harry Styles",
        "url": "https://www.facebook.com/harrystyles",
        "verified": true
      },
      {
        "name": "Live Nation UK",
        "url": "https://www.facebook.com/LiveNationUK",
        "verified": true
      }
    ],
    "suggested_events": [
      {
        "name": "Niall Horan: Dinner Party Live On Tour",
        "url": "https://www.facebook.com/events/907629982271175/",
        "date": "2026-10-02T17:30:00.000Z",
        "people_interested": "296",
        "location": "The O2"
      },
      {
        "name": "Conan Gray: Wishbone World Tour With Special Guest Esha Tewari",
        "url": "https://www.facebook.com/events/1617562709223577/",
        "date": "2026-05-12T17:30:00.000Z",
        "people_interested": "80",
        "location": "The O2"
      },
      {
        "name": "Niagara Renaissance Faire",
        "url": "https://www.facebook.com/events/1918951075681093/",
        "date": "2026-05-16T14:00:00.000Z",
        "people_interested": "7755",
        "location": "Firemans Park"
      }
    ],
    "tickets": {
      "url": "https://ticketmaster.evyy.net/c/253158/2038758/24023?u=https%3A%2F%2Fwww.ticketmaster.co.uk%2Fharry-styles-together-together-london-12-06-2026%2Fevent%2F2300638CB03518FD&utm_medium=affiliate",
      "provider": "Ticketmaster",
      "min_price": null,
      "max_price": null,
      "currency": null
    },
    "duration": null,
    "main_image_downloadable": "https://scontent-yyz1-1.xx.fbcdn.net/v/t39.30808-6/619898733_1347924364042784_6083646616876453960_n.jpg?stp=dst-jpg_s960x960_tt6&_nc_cat=100&ccb=1-7&_nc_sid=75d36f&_nc_ohc=me7WqzIIm58Q7kNvwHv6xgP&_nc_oc=AdpXTenzHyWwVWUt8Qeksjua0xAob-IRI4AYcB7J0YP3VIPmdMSe86JidpAN16NBhg8&_nc_zt=23&_nc_ht=scontent-yyz1-1.xx&_nc_gid=ZLwG4Vn4JQpjUSt4c78w_Q&_nc_ss=79289&oh=00_Af5bZLDLPbZaYNVTyJsVtSygrgfC_0ZJjj4zdCkudngQsA&oe=6A08FB42",
    "responded_object": {
      "going": 212,
      "invited": 553,
      "maybe": null
    },
    "event_start_time": "2026-06-12T16:00:00.000Z"
  }
]

What data you get back

Facebook Events Scraper output schema
Facebook Events Scraper output fields grouped by category.

The response is an array. Each object maps one input URL to one extracted Facebook event record.

Field Type Use it for
inputs.url string Trace the result back to your submitted URL
scrape_status string Filter successful and failed rows
event_id string Primary key for deduplication
url string Canonical Facebook event URL
event_date ISO datetime Scheduling, sorting, upcoming filters
title string Event name
people_responded integer Total response count
responded_object.going integer Attendee intent tracking
responded_object.invited integer Invite count tracking
location.address string Venue display name or address
location.url string Venue or location page URL
hosts array Host names, URLs, and verification flags
tickets object Ticket URL, provider, and price fields
suggested_events array Related event discovery
main_image_downloadable string Image URL suitable for download
description.text string Event details, sale notes, rules, and copy

Use event_id as your stable ID. Facebook URLs include extra path parts and tracking parameters, so raw URL keys create duplicate records.

event_date and event_start_time are ISO timestamps. Store them as timestamps in your warehouse. This keeps date filters, timezone conversion, and weekly rollups clean.

tickets.min_price, tickets.max_price, and tickets.currency can be null. Treat ticket metadata as optional fields because many Facebook events link to external ticketing pages without price data.

people_responded gives you a single top-level engagement count. Use responded_object when you need separate going, invited, and maybe counts.

suggested_events gives you discovery edges from one event to related events. Store these rows separately if you build a recommendation graph or venue-level event map.

Ready to get this data? Extract Facebook event data.

Production tips

Validate URLs before sending jobs

Invalid inputs consume credits and produce failure rows that do not help your pipeline. Reject URLs that do not start with https://www.facebook.com/. Reject non-event URLs for extract-by-URL jobs.

from urllib.parse import urlparse

def validate_facebook_event_url(url: str) -> None:
    parsed = urlparse(url)

    if parsed.scheme != "https":
        raise ValueError(f"Invalid scheme: {url}")

    if parsed.netloc != "www.facebook.com":
        raise ValueError(f"Invalid host: {url}")

    if not parsed.path.startswith("/events/"):
        raise ValueError(f"Expected Facebook event URL: {url}")


urls = [
    "https://www.facebook.com/events/2067708210743589",
    "https://www.facebook.com/events/907629982271175/"
]

for url in urls:
    validate_facebook_event_url(url)

For venue scraping, validate the host and accept Facebook page URLs that point to a business profile or events section. Send upcoming_only as "true" when your pipeline needs future events.

Add validation before you call the API.

Normalize URLs before storage

Facebook URLs collect tracking parameters from shares, ads, and browser sessions. Normalize them before you persist inputs or compare records.

from urllib.parse import urlparse

def normalize_facebook_url(url: str) -> str:
    parsed = urlparse(url)
    path = parsed.path.rstrip("/")

    return f"https://www.facebook.com{path}/"


raw_url = "https://www.facebook.com/events/2067708210743589/?acontext=%7B%7D"
print(normalize_facebook_url(raw_url))

This keeps your queue smaller and reduces duplicate submissions. The final dedupe step should still use event_id, since the scraper returns the canonical event identifier.

Deduplicate by event ID

The stable key is event_id. If it is missing, extract the numeric ID from the canonical URL as a fallback.

import re

def event_key(row: dict) -> str | None:
    if row.get("event_id"):
        return row["event_id"]

    url = row.get("url", "")
    match = re.search(r"/events/(\d+)", url)
    return match.group(1) if match else None


def dedupe_events(rows: list[dict]) -> list[dict]:
    seen = set()
    output = []

    for row in rows:
        key = event_key(row)

        if not key:
            continue

        if key in seen:
            continue

        seen.add(key)
        output.append(row)

    return output

Run dedupe after every batch. Suggested events can surface URLs you already scraped through venue pages.

Store a stable schema

Do not flatten every nested object into one wide table in the first version. Events, hosts, tickets, and suggested events have different update patterns.

A production warehouse layout uses 4 tables:

Table Primary key Notes
facebook_events event_id One row per event
facebook_event_hosts event_id, host_url Multiple hosts per event
facebook_event_tickets event_id One ticket object per event
facebook_suggested_events source_event_id, suggested_event_url Related event graph

For facebook_events, keep these columns:

CREATE TABLE facebook_events (
  event_id TEXT PRIMARY KEY,
  url TEXT NOT NULL,
  title TEXT,
  event_date TIMESTAMP,
  event_start_time TIMESTAMP,
  people_responded INTEGER,
  going_count INTEGER,
  invited_count INTEGER,
  maybe_count INTEGER,
  location_address TEXT,
  location_url TEXT,
  access_level TEXT,
  description_text TEXT,
  main_image_url TEXT,
  scraped_at TIMESTAMP NOT NULL
);

Keep raw JSON too. Store scraped_at on every row so you know which version of each record you are reading.

Treat nulls as normal data

Public Facebook event pages vary. Some events have ticket links, some have price ranges, and some have a downloadable image.

Some pages hide host metadata or expose only a venue name. Your parser should accept those records and leave missing fields as null.

Use safe accessors:

def normalize_event(row: dict) -> dict:
    responded = row.get("responded_object") or {}
    location = row.get("location") or {}
    description = row.get("description") or {}
    tickets = row.get("tickets") or {}

    return {
        "event_id": row.get("event_id"),
        "url": row.get("url"),
        "title": row.get("title"),
        "event_date": row.get("event_date"),
        "event_start_time": row.get("event_start_time"),
        "people_responded": row.get("people_responded"),
        "going_count": responded.get("going"),
        "invited_count": responded.get("invited"),
        "maybe_count": responded.get("maybe"),
        "location_address": location.get("address"),
        "location_url": location.get("url"),
        "description_text": description.get("text"),
        "ticket_url": tickets.get("url"),
        "ticket_provider": tickets.get("provider"),
        "main_image_url": row.get("main_image_downloadable"),
        "scrape_status": row.get("scrape_status")
    }

Do not fail a full batch because one event has tickets: null or main_image: null. Treat nulls as part of the contract.

Split retries by failure type

Check both job-level status and row-level scrape_status. Retry failed inputs with a cap of 2 attempts.

def split_results(rows: list[dict]) -> tuple[list[dict], list[dict]]:
    successes = []
    failures = []

    for row in rows:
        if row.get("scrape_status") == "success" and row.get("event_id"):
            successes.append(row)
        else:
            failures.append(row)

    return successes, failures

Schedule venue jobs with a fixed cadence

For active venues, run venue search daily and dedupe by event_id. For low-volume venues, weekly is enough. Use event extraction after venue discovery when you need the full event object.

Choosing the right Facebook Events scraper

Use this table when adding jobs to a pipeline.

Job type Scraper Input Best use
Extract one known event Extract Facebook event data Event URL Enriching event links from a queue
Extract events from one venue Find Facebook events by venue Venue Events URL Tracking upcoming events for a venue
Extract Facebook Page content Extract Facebook page posts Page URL Pulling posts from an organizer page
Extract Marketplace listing details Get Marketplace listing data Listing URL Enriching individual Marketplace items

If your pipeline covers more than Events, Browse all 86+ scrapers across 14 platforms. For Facebook event work, start with Events Extract by URL and add venue search when you need discovery.

A common setup uses venue search as the source of new URLs. The pipeline then sends those URLs to extract-by-URL, normalizes the results, and writes them into the event tables.

Pricing

ScrapeNow charges per returned row. One row costs one credit, starting at $0.04 per credit for small runs and dropping with volume. No monthly contracts, no proxy fees, no charges for failed rows. See the pricing page for current rates.

Run the Python script above with your API key and one public event URL. If you already have URLs, use Extract Facebook event data. If your source is a venue page, use Find Facebook events by venue and set upcoming_only to "true" for future events.

Related articles

View all

Start collecting data in under five minutes.

Free credits included - no credit card required.

Free credits included - no credit card required