Google Calendar
This page contains the setup guide and reference information for the Google Calendar source connector.
Prerequisites
- A Google account with access to the calendars you want to sync.
- Airbyte Cloud: sign in with Google during setup using Airbyte's OAuth app, or use your own OAuth app as described below.
- Airbyte Open Source: a Google Cloud project with the Google Calendar API enabled and OAuth credentials (Client ID, Client Secret, and a Refresh Token). See the setup steps below.
- (Optional) A specific Calendar ID if you only want to sync
events,acl, andfreebusyfrom one calendar. - (Optional) A Start Date to limit event modification times on Full Refresh, initial Incremental syncs, and full re-reads.
Setup guide
Set up Google Calendar credentials, then configure the source in Airbyte.
Set up Google Calendar
On Airbyte Cloud, choose Authenticate via Google (OAuth) in the Airbyte setup to sign in using Airbyte's OAuth app; you do not need to create your own project. That flow requests these six scopes: calendar.calendarlist.readonly, calendar.calendars.readonly, calendar.events.readonly, calendar.settings.readonly, calendar.freebusy, and calendar.acls.readonly (each prefixed with https://www.googleapis.com/auth/). Google lists each permission separately on its consent screen: allow all of them. A stream whose permission was left unchecked fails with a missing-scope error until you re-authenticate and allow it.
For Airbyte Open Source, or to use Authenticate with custom app (client ID / secret) on Cloud, obtain a Client ID, Client secret, and Refresh token from your own Google Cloud project:
- Enable the Google Calendar API for your project in the API Library.
- Configure the OAuth consent screen; Google does not create an OAuth client until the project has one. For a Google Workspace account in a Google Cloud organization, choose Internal. Otherwise choose External and set the publishing status to In production: Google expires refresh tokens issued to an External app in Testing after 7 days, and syncs then fail with an authentication error. Because the Calendar scopes are sensitive, an unverified app in production shows a "Google hasn't verified this app" warning when you authorize it; you can continue past it for your own account. Add the
calendar.readonlyandcalendar.acls.readonlyscopes. - Create an OAuth client (type Web application) on the Credentials page. Add
https://developers.google.com/oauthplaygroundas an authorized redirect URI. - Obtain a refresh token with the OAuth 2.0 Playground:
- Click the gear icon, check Use your own OAuth credentials, and enter your Client ID and Client Secret.
- Authorize the scopes
https://www.googleapis.com/auth/calendar.readonlyandhttps://www.googleapis.com/auth/calendar.acls.readonly, then exchange the authorization code for tokens. Thecalendar.acls.readonlyscope is required for theaclstream;calendar.readonlyalone is sufficient for all other streams. Instead ofcalendar.readonly, you can authorize the five granular scopes Airbyte Cloud's OAuth flow requests (calendar.calendarlist.readonly,calendar.calendars.readonly,calendar.events.readonly,calendar.settings.readonly, andcalendar.freebusy).
Set up the Google Calendar connector in Airbyte
For Airbyte Cloud:
- Log into your Airbyte Cloud account.
- Click Sources and then click + New source.
- On the Set up the source page, select Google Calendar from the Source type dropdown.
- Enter a name for the connector.
- Under Authentication, choose Authenticate via Google (OAuth) and authorize your Google account. Alternatively, choose Authenticate with custom app (client ID / secret) and enter your own app's Client ID, Client secret, and Refresh token from Google Calendar setup.
- (Optional) For Calendar Id, enter a specific calendar ID to sync
events,acl, andfreebusyfrom only that calendar;calendarlistandcalendarsignore this setting. The calendar does not need to appear in the account's calendar list; it is read directly. The valueprimaryis accepted for the account's primary calendar. Leave empty to sync every calendar in the account's calendar list, hidden calendars included. - (Optional) For Start Date, enter the earliest event
updatedtimestamp in the formatYYYY-MM-DDTHH:mm:ssZorYYYY-MM-DDTHH:mm:ss.SSSZ. When unset, the cursor starts at2000-01-01T00:00:00Z. On an initial sync or full re-read, the connector sends the filter server-side only for a date within the last 21 days; otherwise it reads the calendar and drops older events itself (see Events sync behavior). - (Optional) For Number of concurrent workers, set the number of concurrent request workers (1–10, default 3). Values below 2 run with 2 workers.
- Click Set up source and wait for the tests to complete.
For Airbyte Open Source:
- Navigate to the Airbyte Open Source dashboard.
- Click Sources and then click + New source.
- On the Set up the source page, select Google Calendar from the Source type dropdown.
- Enter a name for the connector.
- Under Authentication, choose Authenticate with custom app (client ID / secret) and enter the Client ID, Client secret, and Refresh token from Google Calendar setup — a refresh token issued by your own Google Cloud OAuth app with the
calendar.readonlyandcalendar.acls.readonlyscopes, plus that app's client ID and client secret. Withoutcalendar.acls.readonly, theaclstream fails with a configuration error — generate a new refresh token that includes the scope, or deselect the stream. - (Optional) For Calendar Id, enter a specific calendar ID to sync
events,acl, andfreebusyfrom only that calendar, orprimaryfor the account's primary calendar;calendarlistandcalendarsignore this setting. The calendar does not need to appear in the account's calendar list; it is read directly. Leave empty to sync every calendar in the account's calendar list, hidden calendars included. - (Optional) For Start Date, enter the earliest event
updatedtimestamp (YYYY-MM-DDTHH:mm:ssZorYYYY-MM-DDTHH:mm:ss.SSSZ). When unset, the cursor starts at2000-01-01T00:00:00Z. On an initial sync or full re-read, the connector sends the filter server-side only for a date within the last 21 days; otherwise it reads the calendar and drops older events itself (see Events sync behavior). - (Optional) For Number of concurrent workers, set the number of concurrent request workers (1–10, default 3). Values below 2 run with 2 workers.
- Click Set up source and wait for the tests to complete.
Configuration files and saved connections
If you configure the connector with a configuration file, the Airbyte API, or Terraform, credentials is required and contains these fields:
auth_type:oauth2.0for Authenticate via Google (OAuth) ormanualfor Authenticate with custom app (client ID / secret).client_id,client_secret, andrefresh_token. The OAuth flow fills these in for theoauth2.0option.
The optional top-level fields are calendarid, start_date, and num_workers (default 3, range 1–10). Values below 2 run with 2 workers.
Before version 1.1.0, the credentials were the top-level fields client_id, client_secret, and client_refresh_token_2. On its first run on version 1.1.0 or later, the connector rewrites a saved configuration that uses these fields to credentials with auth_type: manual, so existing connections keep working with the same OAuth app. Keep the following in mind:
- The Airbyte API and Terraform validate new and updated configurations against the 1.1.0 spec before the connector runs, so they must use the
credentialsfields. This includes the nextapplyof an existing Terraform resource. - Downgrading a connection below 1.1.0 after that first run requires restoring the top-level fields by hand.
Supported sync modes
The Google Calendar source connector supports the following sync modes:
- Full Refresh - Overwrite
- Full Refresh - Append
- Incremental - Append (
eventsonly) - Incremental - Append + Deduped (
eventsonly)
Supported Streams
The events, acl, and freebusy streams sync once per calendar. When Calendar Id is configured, only that calendar is synced — it is read directly, so it does not need to appear in the account's calendar list; otherwise every calendar in the account's calendar list is synced.
| Stream Name | Primary Key | Pagination | Supports Full Sync | Supports Incremental |
|---|---|---|---|---|
| colors | kind | No pagination | ✅ | ❌ |
| settings | id | DefaultPaginator | ✅ | ❌ |
| calendarlist | id | DefaultPaginator | ✅ | ❌ |
| calendars | id | No pagination | ✅ | ❌ |
| events | calendar_id, id | DefaultPaginator | ✅ | ✅ |
| acl | calendar_id, id | DefaultPaginator | ✅ | ❌ |
| freebusy | calendar_id, start, end | No pagination | ✅ | ❌ |
With Calendar Id empty, events reads only calendars the account can read events from, acl only calendars it owns, and freebusy every listed calendar.
Deleted events are replicated: cancelled events arrive with status: "cancelled" (showDeleted=true) and are stamped with an updated value so they replace the live row under Append + Deduped (see Events sync behavior).
Deleted calendar-list entries appear in calendarlist with deleted: true, and deleted sharing rules appear in acl with role: "none". The calendars, events, acl, and freebusy streams skip calendars removed from the calendar list, so the connector doesn't request calendars you no longer have access to. The calendars stream has no deletion record of its own: a removed calendar drops out of the next snapshot, and its deletion is visible in calendarlist.
Google user data
Airbyte's use and transfer to any other app of information received from Google APIs will adhere to the Google API Services User Data Policy, including the Limited Use requirements.
The use of raw or derived user data received from Google Workspace APIs, including the Google Calendar API, will adhere to the Google User Data Policy, including the Limited Use requirements. Airbyte doesn't use this data to develop, improve or train generalized or non-personalized AI or machine learning models.
The connector only reads Calendar data, and only to sync it to the destination you configure in your connection.
IP allow list
If you use Airbyte Cloud and your organization restricts access to specific IPs, add the Airbyte Cloud IP addresses to your allow list.
Limitations & Troubleshooting
Expand to see details about Google Calendar connector limitations and troubleshooting.
Connector limitations
- API quota: the connector uses a moving-window budget of 500 requests/minute, including POST requests, and retries
rateLimitExceeded/userRateLimitExceededresponses and HTTP 429 with exponential backoff.quotaExceeded/dailyLimitExceedederrors other than HTTP 429 fail the sync as transient errors until the quota resets; they do not require a configuration change. Project quotas can vary; see Google's quota documentation. - Stale cursor: Google rejects an
updatedMinthat is too far in the past (measured at 29 days; the limit is undocumented). When the saved cursor is older than 21 days, the connector clears it and re-reads every event (bounded by Start Date when set) instead of failing. Should Google still return410 updatedMinTooLongAgo, it is treated as a transient error: the connector re-reads automatically once the saved cursor is more than 21 days old, and you can clear theeventsstream to re-read sooner; a410for a deleted calendar orfullSyncRequiredis a configuration error. calendarsreturns calendar resources, not list entries: it returns each listed calendar the account can read (minAccessRole=reader), hidden calendars included, with fields such askind,etag,id,summary,description,location,timeZone,conferenceProperties,dataOwner,autoAcceptInvitationsandlabelProperties. The per-user list fields (accessRole, colors, reminders,primary,selected) stay incalendarlist. This stream reads the calendar list regardless of Calendar Id.aclscope and ownership: theaclstream lists a calendar's sharing rules and needs thecalendar.acls.readonlyOAuth scope (orcalendar.acls/calendar); without it the stream fails with a configuration error, so re-authenticate and allow every requested permission, or generate a new refresh token for your OAuth client that includes the scope and paste it into Refresh token, or deselect the stream. If your connection adds new streams automatically, deselectacluntil the token has the scope. Google shows sharing rules only to a calendar's owner, so calendars the account does not own are skipped.freebusyis a point-in-time window: it lists busy blocks from 30 days ago to 45 days ahead, recomputed on every sync;start_datedoes not apply to it. A calendar Google cannot compute free/busy for is logged and contributes no rows, so it looks the same as a free calendar in the destination. It is a snapshot, not a historical record.settings.valueis always a string, including for numeric-looking settings.colorsis a singleton: it returns a single record containingcalendarandeventcolor-palette objects.- Push notification channels (
channels) are intentionally not synced: they are write-only subscriptions the connector would have to create, not data it can read.
Troubleshooting
- If syncs start failing with a rejected refresh token (
invalid_grant) about a week after setup, the OAuth app's consent screen is probably an External app in Testing, whose refresh tokens expire after 7 days. Set its publishing status to In production, generate a new refresh token, and paste it into Refresh token. - If a sync fails with a missing OAuth scope error, a permission was left unchecked on Google's consent screen, or the refresh token was issued without one of the scopes in Google Calendar setup. Re-authenticate and allow every requested permission, generate a new refresh token with the required scopes, or deselect the stream.
- Check out common troubleshooting issues for the Google Calendar source connector on our Airbyte Forum.
Events sync behavior
The events stream is incremental on the event's last modification time (updated), using the Google Calendar updatedMin filter.
- Cancelled events are included (
showDeleted=true) withstatus: cancelled, so deletions reach the destination. Google returns them as stubs without a modification time, so the connector sets theirupdateditself (the saved cursor, or one hour before the sync, whichever is later) so that the cancelled row replaces the live one under Append + Deduped. Filterstatus != 'cancelled'downstream if you only want live events. calendar_idholds the calendar's real ID, so it joins tocalendarlist.id/calendars.id. The aliasprimaryis resolved to the account's email address.- One cursor for all calendars. The stream keeps a single
updatedcursor across calendars. A calendar added to the calendar list after the first sync starts from that cursor, so its older events are not synced; clear theeventsstream to backfill it. - Recurring series are returned as a single record (
singleEvents=false); individual occurrences are not expanded. The record'supdatedis the last time the series was edited. start_datelimits every Full Refresh sync, the first Incremental sync, and any full re-read (after a reset, or when the saved cursor is older than 21 days) to events last modified on or after that date. On those reads, the connector sendsupdatedMinonly for a configured date within the last 21 days; otherwise it downloads the calendar and drops older records itself. Because a recurring series counts as one event, a start date also drops series that have not been edited since that date, even if they still have upcoming occurrences. When unset, the cursor starts at2000-01-01T00:00:00Z.- 29-day window. Google rejects
updatedMinolder than 29 days (measured; the limit is undocumented and has been tighter in the past). The connector keeps the newestupdatedit has seen as its cursor; if that cursor is older than 21 days when a sync starts (for example, a calendar with no edits for three weeks, or a connection paused for three weeks) the connector clears the cursor and re-reads the whole calendar (or everything sincestart_date), then continues incrementally. A calendar whose only changes are deletions still advances its cursor (see cancelled events above); one with no changes at all re-reads on every sync until something changes. - Recommended sync mode: Incremental | Append + Deduped with
calendar_idandidas the primary key; the same event id appears on every calendar a meeting is on. With plain Append, each re-read described above adds another copy of every event.
Reference
Config fields reference
Changelog
Expand to review
| Version | Date | Pull Request | Subject |
|---|---|---|---|
| 1.2.0 | 2026-11-12 | 92016 | Include calendar and ACL tombstones, expand schemas, and improve pagination and concurrency reliability. |
| 1.1.0 | 2026-11-11 | 86469 | Add declarative Google OAuth with granular read-only scopes and a custom-app authentication option; automatically migrate saved flat credentials (downgrading below 1.1.0 then needs them restored by hand); report a missing OAuth scope as a configuration error. |
| 1.0.1 | 2026-11-10 | 91570 | Update dependencies |
| 1.0.0 | 2026-11-09 | 86470 | Add acl and freebusy streams, read events/acl/freebusy from every calendar when calendarid is empty, change the events primary key to [calendar_id, id], return calendar resources from calendars, change the colors primary key to kind, and type date and date-time fields (breaking, see the migration guide) |
| 0.1.4 | 2026-11-03 | 90589 | Update dependencies |
| 0.1.3 | 2026-10-27 | 89924 | Update dependencies |
| 0.1.2 | 2026-10-20 | 89285 | Update dependencies |
| 0.1.1 | 2026-10-13 | 88641 | Update dependencies |
| 0.1.0 | 2026-10-07 | 86468 | Add error handling, API budget, concurrency, incremental events (now includes cancelled events via showDeleted=true) with a calendar_id column and an optional start_date, and enable acceptance tests |
| 0.0.55 | 2026-10-06 | 87901 | Update dependencies |
| 0.0.54 | 2026-09-29 | 87194 | Update dependencies |
| 0.0.53 | 2026-09-22 | 86679 | Update dependencies |
| 0.0.52 | 2026-09-15 | 86070 | Update dependencies |
| 0.0.51 | 2026-09-08 | 85541 | Update dependencies |
| 0.0.50 | 2026-08-18 | 84636 | Update dependencies |
| 0.0.49 | 2026-08-11 | 83962 | Update dependencies |
| 0.0.48 | 2026-08-04 | 83499 | Update dependencies |
| 0.0.47 | 2026-07-28 | 82972 | Update dependencies |
| 0.0.46 | 2026-07-21 | 82435 | Update dependencies |
| 0.0.45 | 2026-07-14 | 81849 | Update dependencies |
| 0.0.44 | 2026-06-30 | 81038 | Update dependencies |
| 0.0.43 | 2026-06-23 | 80483 | Update dependencies |
| 0.0.42 | 2026-06-16 | 79880 | Update dependencies |
| 0.0.41 | 2026-06-09 | 79339 | Update dependencies |
| 0.0.40 | 2026-06-02 | 78738 | Update dependencies |
| 0.0.39 | 2026-04-28 | 77268 | Update dependencies |
| 0.0.38 | 2026-04-21 | 76584 | Update dependencies |
| 0.0.37 | 2026-03-31 | 75671 | Update dependencies |
| 0.0.36 | 2026-03-17 | 74985 | Update dependencies |
| 0.0.35 | 2026-02-24 | 73746 | Update dependencies |
| 0.0.34 | 2026-02-17 | 73085 | Update dependencies |
| 0.0.33 | 2026-01-20 | 71931 | Update dependencies |
| 0.0.32 | 2026-01-14 | 71428 | Update dependencies |
| 0.0.31 | 2025-12-18 | 70696 | Update dependencies |
| 0.0.30 | 2025-11-25 | 69889 | Update dependencies |
| 0.0.29 | 2025-11-18 | 69373 | Update dependencies |
| 0.0.28 | 2025-10-29 | 69049 | Update dependencies |
| 0.0.27 | 2025-10-21 | 68310 | Update dependencies |
| 0.0.26 | 2025-10-14 | 68007 | Update dependencies |
| 0.0.25 | 2025-10-07 | 67257 | Update dependencies |
| 0.0.24 | 2025-09-30 | 66309 | Update dependencies |
| 0.0.23 | 2025-09-09 | 66046 | Update dependencies |
| 0.0.22 | 2025-08-23 | 65366 | Update dependencies |
| 0.0.21 | 2025-08-09 | 64614 | Update dependencies |
| 0.0.20 | 2025-08-02 | 64218 | Update dependencies |
| 0.0.19 | 2025-07-26 | 63813 | Update dependencies |
| 0.0.18 | 2025-07-19 | 63491 | Update dependencies |
| 0.0.17 | 2025-07-12 | 63149 | Update dependencies |
| 0.0.16 | 2025-07-05 | 62653 | Update dependencies |
| 0.0.15 | 2025-06-28 | 62156 | Update dependencies |
| 0.0.14 | 2025-06-21 | 61833 | Update dependencies |
| 0.0.13 | 2025-06-14 | 61121 | Update dependencies |
| 0.0.12 | 2025-05-24 | 60587 | Update dependencies |
| 0.0.11 | 2025-05-10 | 59250 | Update dependencies |
| 0.0.10 | 2025-04-26 | 58812 | Update dependencies |
| 0.0.9 | 2025-04-19 | 58175 | Update dependencies |
| 0.0.8 | 2025-04-12 | 57066 | Update dependencies |
| 0.0.7 | 2025-03-29 | 56487 | Update dependencies |
| 0.0.6 | 2025-03-22 | 55939 | Update dependencies |
| 0.0.5 | 2025-03-08 | 55325 | Update dependencies |
| 0.0.4 | 2025-03-01 | 54992 | Update dependencies |
| 0.0.3 | 2025-02-22 | 54395 | Update dependencies |
| 0.0.2 | 2025-02-15 | 47915 | Update dependencies |
| 0.0.1 | 2024-10-06 | Initial release by @bala-ceg via Connector Builder |