Facebook events to Google Calendar in 2026: what actually works
Every guide you find for this is broken, and the reason is simple. Facebook removed the calendar export years ago. Here is the method that still works, for a single event or for a whole page's programme on a schedule.
Short version. There is no export button any more. Get the event data out as structured records, then either import a CSV into Google Calendar once, or write an .ics file to a fixed URL and subscribe to it so the calendar keeps itself up to date. Both paths are below, including the conversion script.
Why the old instructions fail
If you have been sent here by a forum thread, you have probably already tried these:
- The "Export event" link. Facebook used to offer it on the event page, next to the RSVP buttons. It is gone.
- The personal iCal feed. Facebook served a
webcal://address that carried your upcoming events and friends' birthdays. Those endpoints were retired around 2020. Subscriptions made before that still exist in people's calendars but never receive anything new, which is why they look like they work until you notice nothing has appeared for years. - IFTTT and Zapier recipes. They were built on the Graph API permissions that Facebook withdrew from third-party apps. The connectors that remain cover ads and page posting, not public events.
- The Graph API. Public event search was closed to third-party apps. See what the events API still returns for the detail.
So anything that promises a one-click sync is either very old or describing a product that quietly stopped working.
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:
| Field | What it holds |
|---|---|
name | Event title |
utcStartDate, utcEndDate | Start and end in UTC, ISO 8601. Use these rather than the display strings. |
address, location.name | Venue name and one-line address |
description | Event description |
url | Link back to the event on Facebook |
childEvents | Every 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 column | Value to use |
|---|---|
| Subject | name |
| Start Date | date part of utcStartDate, as MM/DD/YYYY |
| Start Time | time part of utcStartDate, as hh:mm AM/PM |
| End Date, End Time | same from utcEndDate |
| Location | address, or location.name when the address is empty |
| Description | description, 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.
Step 2b: keep a calendar in sync with an .ics feed
This is the version worth setting up if you are a venue, a promoter, or you run a 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, usually several times a day.
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, 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"):
yield child["utcStartDate"], child.get("utcEndDate"), child.get("id") or event.get("id")
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
- Time zones. Everything above is UTC, which is why the timestamps end in
Z. Google converts to the viewer's zone automatically. If you write local times without a zone, events land at the wrong hour for half your audience. - Events with no end time. Facebook often has none. Omit
DTENDrather than inventing one; calendars then show a default duration. - Stable identifiers. Each entry needs a
UIDthat does not change between runs, or every refresh creates duplicates. The event ID does that job. - Cancelled events. A cancelled event still appears in the data with
isCanceledset, so filter those out or mark them in the title.
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
| Situation | Method |
|---|---|
| You want a handful of events in your own calendar today | CSV import, step 2a |
| You run a venue, club or festival page and want the programme to stay current | Scheduled run plus .ics subscription, step 2b |
| You live in Apple Calendar or on an iPhone | Same .ics file, step 2c |
| You publish a what's-on site or newsletter | Scheduled 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. You can still get public event data out and convert it, which is what this page describes.
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.