Alfalfa

Facebook events to Google Calendar in 2026: what actually works

Facebook's old calendar export disappeared years ago. You can still move public events into Google Calendar: import once from CSV, use a calendar feed, or run a daily workflow that refreshes existing entries.

Updated September 2026.

Short version. There is no Facebook export button any more. For a daily Google Calendar update, download the n8n workflow. For a one-time import, use CSV. For a read-only subscribed calendar, publish a regenerated .ics file at a fixed URL. All three paths are below.

Why the old instructions fail

If you have been sent here by a forum thread, you have probably already tried these:

Look for a current data source when following old export tutorials. They rely on links or permissions that are no longer available.

Step 1: get the events as data

You need the events as records before a calendar can read them. The Facebook Events Scraper takes a search term, a city, a page URL or a single event URL and returns one record per event. No Facebook account is involved.

The fields that matter for a calendar are these:

FieldWhat it holds
nameEvent title
utcStartDate, utcEndDateStart and end in UTC, ISO 8601. Use these rather than the display strings.
address, location.nameVenue name and one-line address
descriptionEvent description
urlLink back to the event on Facebook
childEventsEvery date of a recurring event, each with its own start and end

Run it once and you have a dataset you can download as JSON, CSV or Excel, or read through the API.

Step 2a: one-off, using Google Calendar's CSV import

Google Calendar accepts a CSV with a fixed set of column names. Export the dataset as CSV, then map the columns like this:

Google Calendar columnValue to use
Subjectname
Start Datedate part of utcStartDate, as MM/DD/YYYY
Start Timetime part of utcStartDate, as hh:mm AM/PM
End Date, End Timesame from utcEndDate
Locationaddress, or location.name when the address is empty
Descriptiondescription, plus the url so you can get back to the event

Then open Google Calendar, go to Settings, choose Import and export, pick the file and the destination calendar. Two things to watch: the times you import are read in the calendar's own time zone, so convert from UTC first if your calendar is not set to UTC, and Google will happily create duplicates if you import the same file twice.

Daily Google Calendar updates without writing code

The downloadable n8n workflow runs a public event search through Facebook Events Scraper each morning, then creates or refreshes Google Calendar entries. Each recurring occurrence gets its own calendar entry. A Facebook event ID keeps the same entry on later runs, including when its title or time changes.

  1. Import the JSON file into n8n. Add your Apify API credential to both Apify nodes and a Google Calendar credential to both Calendar nodes.
  2. In Settings, set searchQuery for a city/topic, or set startUrl to a public Facebook page's /upcoming_hosted_events URL and clear searchQuery. Set maxEvents. In both Calendar nodes, choose the same destination calendar. Start with a separate calendar so you can review the first run.
  3. Run it once manually and check the entries. Run it a second time to confirm that existing entries update, then activate the daily schedule. The default is 06:00 in your n8n workflow time zone.

A 50-event search every day for 30 days can return up to 1,500 charged events: about $4.50 on Apify Free at $3 per 1,000, including events already on your calendar. Organizer email lookup is not enabled. Events without a start time or usable ID are skipped; cancellations still returned by Facebook are marked CANCELLED. If a search stops returning an event, the workflow leaves its existing calendar entry in place. Your n8n and Google accounts have their own terms.

Step 2b: keep a calendar in sync with an .ics feed

This works if you want a subscribed, read-only feed for a venue or community site. Schedule the scraper, convert each run's output into an .ics file at a stable URL, and subscribe to that URL once. Google refreshes subscribed calendars on its own schedule; changes may take time to appear.

The script below reads a finished run's dataset straight from the Apify API and writes events.ics. It handles the escaping rules that trip people up, expands recurring events into their individual dates, and skips anything without a start time.

#!/usr/bin/env python3
"""Turn a Facebook Events Scraper dataset into an .ics calendar file."""
import json, re, urllib.request
from datetime import datetime, timezone

DATASET_URL = "https://api.apify.com/v2/datasets/<DATASET_ID>/items?clean=true&format=json&token=<TOKEN>"

def ics_time(iso):
    # "2026-04-24T22:30:00.000Z" -> "20260424T223000Z"
    return datetime.strptime(iso[:19], "%Y-%m-%dT%H:%M:%S").strftime("%Y%m%dT%H%M%SZ")

def esc(text):
    # commas, semicolons, backslashes and newlines are special in iCalendar
    return (str(text or "").replace("\\", "\\\\").replace(";", "\\;")
            .replace(",", "\\,").replace("\r\n", "\\n").replace("\n", "\\n"))

def fold(line):
    # iCalendar lines must not exceed 75 octets; continuation lines start with a space
    out, raw = [], line.encode("utf-8")
    while len(raw) > 75:
        cut = 75
        while cut > 0 and (raw[cut] & 0xC0) == 0x80:  # never split a UTF-8 character
            cut -= 1
        out.append(raw[:cut].decode("utf-8"))
        raw = b" " + raw[cut:]
    out.append(raw.decode("utf-8"))
    return "\r\n".join(out)

def occurrences(event):
    """One entry per date: recurring events carry every occurrence in childEvents."""
    children = event.get("childEvents") or []
    if children:
        for child in children:
            if child.get("utcStartDate"):
                child_id = child.get("id") or (re.search(r"/events/(\d+)", child.get("url") or "") or [None, None])[1]
                uid = child_id or "%s-%s" % (event.get("id") or event.get("url") or "event", child["utcStartDate"])
                yield child["utcStartDate"], child.get("utcEndDate"), uid
    elif event.get("utcStartDate"):
        yield event["utcStartDate"], event.get("utcEndDate"), event.get("id")

events = json.load(urllib.request.urlopen(DATASET_URL))
stamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
lines = ["BEGIN:VCALENDAR", "VERSION:2.0", "PRODID:-//alfalfa//facebook-events//EN",
         "CALSCALE:GREGORIAN", "METHOD:PUBLISH"]

for event in events:
    place = event.get("address") or (event.get("location") or {}).get("name") or ""
    body = event.get("description") or ""
    if event.get("url"):
        body = (body + "\n\n" + event["url"]).strip()
    for start, end, uid in occurrences(event):
        lines += ["BEGIN:VEVENT",
                  fold("UID:%s@alfalfa.cz" % (uid or start)),
                  "DTSTAMP:" + stamp,
                  "DTSTART:" + ics_time(start)]
        if end:
            lines.append("DTEND:" + ics_time(end))
        lines += [fold("SUMMARY:" + esc(event.get("name"))),
                  fold("LOCATION:" + esc(place)),
                  fold("DESCRIPTION:" + esc(body))]
        if event.get("url"):
            lines.append(fold("URL:" + event["url"]))
        lines.append("END:VEVENT")

lines.append("END:VCALENDAR")
with open("events.ics", "w", encoding="utf-8", newline="") as handle:
    handle.write("\r\n".join(lines) + "\r\n")
print("wrote events.ics with %d events" % len(events))

Put events.ics somewhere with a fixed public address, then in Google Calendar choose Other calendars, From URL, and paste it. Give the file the same name every time you regenerate it, otherwise the subscription breaks.

Details that will bite you

Step 2c: Apple Calendar and iPhone

The same .ics file works on a Mac and an iPhone, so the iPhone question has the same answer. One-off: open events.ics in Apple Calendar (File, Import on a Mac; on an iPhone open the file from Mail or Files and tap Add All). Kept in sync: in Apple Calendar choose File, New Calendar Subscription and paste the file's URL; on an iPhone go to Settings, Calendar, Accounts, Add Account, Other, Add Subscribed Calendar. iCloud then pushes the subscription to every device signed in with the same Apple ID, so the phone updates on its own.

If you would rather not maintain the script above, the same converter is published as a small open-source tool, facebook-events-to-ics: one file, no dependencies, reads a dataset file or the dataset URL and writes the .ics, recurring dates included.

Which one should you use

SituationMethod
You want a handful of events in your own calendar todayCSV import, step 2a
You want Google Calendar entries to update every morningDownload the n8n workflow above
You run a venue, club or festival and want a subscribed feedScheduled run plus .ics subscription, step 2b
You live in Apple Calendar or on an iPhoneSame .ics file, step 2c
You publish a what's-on site or newsletterScheduled run, then read the dataset from the API and render it yourself

Questions people ask

Can you still export Facebook events to Google Calendar?

Not with a button on Facebook. The export was removed. The public event data can still be imported once or synced through the workflow above.

Can I sync a Facebook page's events automatically?

Yes. Set the workflow's startUrl to the page's public /upcoming_hosted_events URL and clear its example search query, or regenerate an .ics file at a stable public URL for a subscribed calendar. The workflow refreshes events by ID when they appear in a new run; it does not remove events that disappear from the results.

How do I get Facebook events on my iPhone calendar?

Convert the events to an .ics file (step 2b) and either open the file on the phone and tap Add All, or subscribe to its URL under Settings, Calendar, Accounts, Add Subscribed Calendar. Subscribed calendars refresh on their own. Step 2c has the details.

Why did my Facebook calendar feed stop working?

The feed endpoints were retired. An old subscription keeps whatever it already downloaded and never receives anything new, so it looks alive but is frozen.

Does this need a Facebook account?

No. The scraper reads only what Facebook shows a visitor who is not signed in, so there is no account to connect and nothing of yours at risk.

Will it get every event from a page?

It returns the upcoming events a page lists publicly, and past events too if you point it at the past events URL. Events that Facebook hides from signed-out visitors, such as private ones, are not visible to anybody without an invitation.

Try the Facebook Events Scraper See the ready-made example