Source: https://cli.nylas.com/guides/calendar-api-comparison

# Calendar API Compared: Google, Microsoft, CalDAV

You want to build a calendar integration. Google Calendar API, Microsoft Graph Calendar API, and CalDAV (RFC 4791) all expose event CRUD, free-busy queries, and recurring event support — but they authenticate differently, handle timezones differently, and impose different rate limits. This guide compares all three across the dimensions that matter most, with a unified CLI approach that handles all of them with one command.

Written by [Qasim Muhammad](https://cli.nylas.com/authors/qasim-muhammad) Staff SRE

Updated May 30, 2026

> **TL;DR:** Google Calendar API is the right choice for Google Workspace-only apps — it has push notifications via watch channels, a freebusy endpoint, and a 1 million quota unit daily budget. Microsoft Graph is the choice for Microsoft 365 shops — delta queries, OData filtering, and webhook subscriptions, but Azure AD app registration required. CalDAV is the open standard used by Apple iCloud, Fastmail, and self-hosted servers — no vendor lock-in, but no push and quirky per-server implementations. For multi-provider coverage without maintaining three codepaths, a unified CLI routes through all three with one command.

> **Disclosure:** Nylas CLI is built by Nylas, Inc. This comparison reflects our testing and product understanding as of May 30, 2026.

For CLI commands referenced in this guide, see the [full command reference](https://cli.nylas.com/docs/commands). The key calendar commands are `nylas calendar events list`, `nylas calendar events create`, `nylas calendar availability check`, and `nylas calendar find-time`.

## What are the three main calendar integration APIs?

Three APIs cover the vast majority of calendar integrations: Google Calendar API, Microsoft Graph Calendar API, and CalDAV (defined in [RFC 4791](https://datatracker.ietf.org/doc/html/rfc4791)). Google Calendar ships with every Gmail and Google Workspace account. Microsoft 365 has 400 million paid commercial seats, per Microsoft's Q2 FY2025 earnings. CalDAV powers Apple iCloud Calendar, Fastmail, Proton Calendar, and every self-hosted server running Radicale or Baikal. Together they cover close to 100% of calendar accounts a developer might need to integrate.

The gap between them isn't cosmetic. All three support event CRUD, but they authenticate differently, represent recurring events differently, and expose free-busy data through completely different endpoints. A working Google Calendar integration won't read a single Outlook event, and a CalDAV client that works against iCloud will break against some Nextcloud configurations.

The comparison table below covers the dimensions that matter most when choosing an API for a new integration.

| Feature | Google Calendar API | Microsoft Graph | CalDAV (RFC 4791) | Nylas CLI |
| --- | --- | --- | --- | --- |
| Auth method | OAuth 2.0 (Google Cloud Console) | OAuth 2.0 (Azure AD / Entra ID) | Basic auth or OAuth (provider-specific) | One OAuth flow (all providers) |
| Event CRUD | REST JSON (`events.insert`, `events.patch`) | REST JSON (POST/PATCH to `/me/events`) | WebDAV PUT/DELETE with iCalendar (VCALENDAR) | Unified JSON (provider-agnostic) |
| Recurring events | RRULE in event body; instances expandable via API | RRULE pattern; `recurrenceId` per instance | iCalendar RRULE; RECURRENCE-ID for exceptions | Normalized recurrence across all providers |
| Free-busy / availability | `freebusy.query` endpoint | `getSchedule` action | VFREEBUSY (RFC 4791 §7.10); not all servers implement it | `nylas calendar availability check` |
| Push notifications | Watch channels (push via watch resource) | Webhook subscriptions (expire every 3 days) | None (polling only) | Webhook via Nylas (normalized) |
| Incremental sync | syncToken (next page token pattern) | Delta queries (`@odata.deltaLink`) | ctag / ETag polling | Managed sync (no token tracking needed) |
| Rate limits | 1M quota units/day per project (default); 500 req/100s per user | 10,000 req/10 min per mailbox (varies by tenant) | Provider-specific (no standard) | Managed by Nylas API tier |
| Timezone handling | IANA timezone IDs in event body | ISO 8601 with offset; `Prefer: outlook.timezone` header | TZID parameter in VEVENT; VTIMEZONE component | `--timezone` flag; DST-aware |
| Provider lock-in | Google only | Microsoft only | Any CalDAV server | None (unified) |

## How does the Google Calendar API work?

The [Google Calendar API v3](https://developers.google.com/calendar/api/v3/reference) is a REST API that returns JSON. It covers Google Calendar across every Gmail and Workspace account and exposes resources for calendars, events, ACLs, free-busy, and settings. Authentication requires an OAuth 2.0 app registered in the Google Cloud Console with the `https://www.googleapis.com/auth/calendar` or narrower read-only scope. Apps requesting the full calendar scope go through a verification review that typically takes several weeks for external-facing apps.

Google enforces a default quota of 1 million quota units per day per GCP project, with 500 requests per 100 seconds per user (adjustable via the GCP console). According to the [Google Calendar API quota documentation](https://developers.google.com/calendar/api/guides/quota), listing events costs 1 unit per call and inserting an event costs 1 unit. A free-busy query costs 1 unit regardless of how many calendars you include in the request. Most applications stay well under the daily budget.

The following curl example lists upcoming events from a calendar. You need a valid OAuth 2.0 access token and the calendar ID — use `primary` for the user's main calendar:

```bash
# List 10 upcoming events from the primary calendar
curl -s "https://www.googleapis.com/calendar/v3/calendars/primary/events" \
  -G \
  --data-urlencode "maxResults=10" \
  --data-urlencode "orderBy=startTime" \
  --data-urlencode "singleEvents=true" \
  --data-urlencode "timeMin=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
  -H "Authorization: Bearer $GOOGLE_ACCESS_TOKEN" \
  | jq '.items[] | {summary, start: .start.dateTime}'

# Query free-busy for a time range
curl -s -X POST "https://www.googleapis.com/calendar/v3/freeBusy" \
  -H "Authorization: Bearer $GOOGLE_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "timeMin": "2026-06-01T09:00:00Z",
    "timeMax": "2026-06-01T17:00:00Z",
    "items": [{"id": "primary"}]
  }' | jq '.calendars.primary.busy'
```

Google Calendar's strongest features for developers are the `freebusy.query` endpoint (query multiple calendars in one request), the syncToken-based incremental sync (store the token from the last list response, use it on the next request to get only changes), and push notifications via [push notification channels](https://developers.google.com/calendar/api/guides/push). The tradeoff: you can only reach Google Calendar users. Any user on Outlook, iCloud, or Fastmail requires a separate integration.

## How does the Microsoft Graph Calendar API work?

The [Microsoft Graph Calendar API](https://learn.microsoft.com/en-us/graph/api/resources/calendar) covers Outlook.com and Microsoft 365 (Exchange Online). It's the only supported way to access Outlook calendar data programmatically after Microsoft deprecated Basic Auth for Exchange Online in October 2022. Authentication goes through Azure AD (Entra ID) using MSAL, with either delegated permissions (user signs in) or application permissions (daemon, no user). Application permissions require admin consent from an Azure AD tenant administrator before any mailbox in the organization is accessible.

Graph stands out from the other two on delta queries. Per the [Microsoft Graph delta query documentation](https://learn.microsoft.com/en-us/graph/delta-query-events), you call `/me/calendarView/delta` and receive a `@odata.deltaLink` token in the response. On subsequent calls, passing that token returns only events that changed since your last request. A mailbox that receives 5 calendar invites per day returns 5 events on the delta call, not 500.

The following example lists upcoming calendar events using the Graph API and then queries availability for a user. Both calls use OData query parameters to shape the response server-side:

```bash
# List 10 upcoming events with selected fields
curl -s "https://graph.microsoft.com/v1.0/me/events" \
  -G \
  --data-urlencode '$top=10' \
  --data-urlencode '$select=subject,start,end,organizer' \
  --data-urlencode '$orderby=start/dateTime asc' \
  --data-urlencode '$filter=start/dateTime ge '"'"'2026-05-30T00:00:00Z'"'"'' \
  -H "Authorization: Bearer $GRAPH_ACCESS_TOKEN" \
  | jq '.value[] | {subject, start: .start.dateTime}'

# Get availability (free-busy) for a user
curl -s -X POST "https://graph.microsoft.com/v1.0/me/calendar/getSchedule" \
  -H "Authorization: Bearer $GRAPH_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "schedules": ["user@company.com"],
    "startTime": {"dateTime": "2026-06-01T09:00:00", "timeZone": "UTC"},
    "endTime": {"dateTime": "2026-06-01T17:00:00", "timeZone": "UTC"},
    "availabilityViewInterval": 30
  }' | jq '.value[].availabilityView'
```

Microsoft Graph also supports webhook subscriptions for push notifications. Subscriptions on calendar resources expire after 3 days and must be renewed. The default rate limit is 10,000 requests per 10 minutes per mailbox (varies by tenant and workload) — generous for most use cases, but batch-heavy migrations can hit it. Graph returns `429 Too Many Requests` with a `Retry-After` header when the limit is exceeded.

## How does CalDAV work?

CalDAV, defined in [RFC 4791](https://datatracker.ietf.org/doc/html/rfc4791), is a WebDAV extension for calendar data. It stores events as iCalendar (ICS) objects on a server and uses standard HTTP methods — PUT to create or update, DELETE to remove, REPORT to query. Apple iCloud Calendar, Fastmail, Proton Calendar, and self-hosted servers like Baikal, Radicale, and Nextcloud all implement CalDAV. There's no vendor relationship required: if a server speaks RFC 4791, your client works against it.

CalDAV's authentication story is messy. iCloud uses app-specific passwords (2FA must be enabled). Fastmail uses API tokens. Nextcloud supports both basic auth and OAuth. Google deprecated its CalDAV API for new apps in 2023, and Exchange Online does not expose a supported CalDAV endpoint. In practice, CalDAV is the right choice for iCloud, Fastmail, and self-hosted servers — not Google or Microsoft.

The example below shows a raw CalDAV REPORT request to list events in a date range. This is what calendar clients issue under the hood — it's verbose compared to a REST API call:

```bash
# CalDAV calendar-query REPORT — lists events in a date range
# Replace CALDAV_URL with your server URL, e.g. https://caldav.icloud.com/
curl -s -X REPORT "$CALDAV_URL/calendars/$USERNAME/$CALENDAR_ID/" \
  -H "Authorization: Basic $(echo -n "$USERNAME:$APP_PASSWORD" | base64)" \
  -H "Content-Type: application/xml; charset=utf-8" \
  -H "Depth: 1" \
  -d '<?xml version="1.0" encoding="utf-8" ?>
<C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
  <D:prop>
    <D:getetag/>
    <C:calendar-data/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:time-range start="20260601T000000Z" end="20260630T235959Z"/>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>'
```

That XML is the standard CalDAV query format. In production you'd parse the multipart WebDAV response, extract the ICS payloads from each `calendar-data` element, and parse each VCALENDAR object with a library like [python-recurring-ical-events](https://github.com/niccokunzmann/python-recurring-ical-events) to expand recurring events correctly. CalDAV has no push notification mechanism — you poll the server's `ctag` property to detect changes, then fetch only the events whose ETags changed since your last sync.

## How does authentication complexity compare?

Authentication is where the three APIs diverge most sharply for developer experience. All three ultimately use OAuth 2.0, but the setup paths are different, and the time-to-first-token ranges from minutes (CalDAV with basic auth) to weeks (Google Calendar app verification). That setup cost — app registration, scope review, admin consent — is where most calendar integrations stall, not the event model itself.

**Google Calendar API:** Create a project in the [Google Cloud Console](https://console.cloud.google.com/), configure the OAuth consent screen, enable the Calendar API, and generate OAuth 2.0 credentials. Apps requesting sensitive scopes (anything beyond `calendar.readonly`) are reviewed before Google allows them to access accounts outside your development test users list. That review takes several weeks and requires a privacy policy, a demo video, and a description of your use case.

**Microsoft Graph Calendar API:** Register an app in Azure AD (Entra ID), configure API permissions under Microsoft Graph, and set the redirect URI. For application permissions (daemon access, no user sign-in), a tenant administrator must grant admin consent before the app can read any calendar in their organization. Enterprise customers with IT governance processes can take days or weeks to approve new app registrations.

**CalDAV:** iCloud requires enabling two-factor authentication and generating an app-specific password in Apple ID settings — the process takes minutes. Fastmail generates API tokens in account settings. Self-hosted servers typically use basic auth over TLS. There's no app registration or vendor approval process; any client that can make an HTTP request can connect immediately.

## How do recurring events differ across the three APIs?

Recurring events are defined by an RRULE — a string like `RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR` that specifies when instances occur. All three APIs use the iCalendar RRULE syntax from [RFC 5545](https://datatracker.ietf.org/doc/html/rfc5545), but they differ on how they expand recurring events into individual instances, how they represent exceptions (a modified or deleted single instance), and how they handle timezone transitions when daylight saving time shifts an occurrence.

**Google Calendar API:** By default, `events.list` returns the recurring event's master record with the RRULE. Setting `singleEvents=true` expands it into individual instances within the requested time window — this is the pattern most applications use. Each expanded instance has a `recurringEventId` pointing back to the master. Exceptions (rescheduled or cancelled instances) appear as separate events with a `originalStartTime` field.

**Microsoft Graph:** The `recurrence` property on an event contains a `pattern` object (frequency, interval, daysOfWeek) and a `range` object (start date, end date or number of occurrences). Individual instances are accessible via the `instances` navigation property on the series master. Graph uses its own recurrence object model — RRULE strings don't appear directly in the response.

**CalDAV (iCalendar RFC 5545):** Recurring events are stored as a single VCALENDAR object with a VEVENT containing the RRULE. Exceptions are stored as additional VEVENT components in the same object, identified by a RECURRENCE-ID property that matches the original instance start time. Expanding instances requires a client-side iCalendar parser that handles timezone transitions correctly — this is non-trivial. Libraries like [python-recurring-ical-events](https://github.com/niccokunzmann/python-recurring-ical-events) handle RFC 5545 expansion including EXDATE, RDATE, and VTIMEZONE lookups.

## How do free-busy and availability queries work?

Free-busy queries ask: "is this person available at this time?" All three APIs answer that question, but through different endpoints with different input formats and different levels of reliability. The Google Calendar API's `freebusy.query` endpoint can check up to 50 calendars in a single request. Microsoft Graph's `getSchedule` action returns availability views at configurable time intervals from 5 to 60 minutes. CalDAV's VFREEBUSY component is defined in RFC 4791 §7.10 but not all servers implement it — iCloud's CalDAV endpoint doesn't support VFREEBUSY queries.

The practical availability comparison looks like this:

| API | Endpoint | Max calendars | Output format | Notes |
| --- | --- | --- | --- | --- |
| Google Calendar | `freebusy.query` | 50 | JSON busy/free time ranges | Costs 1 quota unit regardless of calendar count |
| Microsoft Graph | `getSchedule` | 20 | Availability view string (F=Free, B=Busy, T=Tentative) | Configurable 5-60 min intervals; works across org |
| CalDAV | VFREEBUSY REPORT | 1 (per request) | iCalendar VFREEBUSY component | Not implemented on iCloud; Fastmail supports it |
| Nylas CLI | `nylas calendar availability check` | Any | Normalized JSON or plain text | Works against all providers; uses native endpoint per provider |

## How does the Nylas CLI handle all three APIs?

Nylas CLI abstracts API differences by routing through a unified API that speaks Google Calendar, Microsoft Graph, and CalDAV under the hood. A single `nylas calendar events list` command returns the same JSON structure whether the connected account is Google Calendar, Outlook, iCloud, or a Fastmail CalDAV server. There's no provider-specific code in your scripts or pipelines.

Install the CLI with Homebrew, configure authentication with your Nylas API key, and run calendar commands immediately. The CLI supports Google Calendar, Outlook, Exchange, Yahoo, iCloud, and any CalDAV-compatible server from a single binary:

```bash
# Install
brew install nylas/nylas-cli/nylas

# Authenticate with your Nylas API key (headless / CI-friendly)
nylas auth config --api-key YOUR_API_KEY

# Or use OAuth browser flow for interactive setup
nylas auth login
```

After authentication, calendar commands work identically regardless of which provider is connected. The `nylas calendar events list` command accepts a `--days` flag to set the lookahead window and a `--timezone` flag for DST-aware scheduling across regions. Under the hood, the CLI routes through the right native API — `events.list` for Google, `/me/events` for Graph, or a CalDAV REPORT — and normalizes the response:

```bash
# List events for the next 7 days (works on any provider)
nylas calendar events list --days 7

# List events with DST-aware timezone
nylas calendar events list --days 14 --timezone America/New_York

# Create an event (cross-provider)
nylas calendar events create --title "Sync meeting" --start 2026-06-01T09:00:00Z --end 2026-06-01T09:30:00Z

# Check availability across attendees
nylas calendar availability check \
  --start 2026-06-01T09:00:00Z --end 2026-06-01T17:00:00Z \
  --participants alice@example.com,bob@example.com

# List available calendars on the account
nylas calendar list

# Find the next open meeting slot
nylas calendar find-time --participants alice@example.com,bob@example.com --duration 30

# Schedule in natural language
nylas calendar schedule ai "30-minute sync with the eng team next Tuesday morning"

# Detect scheduling conflicts
nylas calendar ai conflicts
```

For AI agents and automation scripts, the unified interface means you don't branch on provider type. A deployment script that checks for calendar conflicts before scheduling a maintenance window works identically on a Google Calendar account, an Outlook account, or an iCloud account. For more on building agents that manage calendar data, see the [calendar from terminal guide](https://cli.nylas.com/guides/manage-calendar-from-terminal).

## Which calendar API should you choose?

The right API depends on which providers your users have, whether you need push notifications, and how much auth infrastructure you want to maintain. Five scenarios cover most real-world integrations. Choosing the wrong API early means maintaining multiple codepaths later — migrating from a single-provider integration to a multi-provider one typically requires reworking authentication, data models, and error handling across each new provider.

| Scenario | Best choice | Why |
| --- | --- | --- |
| Google Workspace-only product with push and free-busy | Google Calendar API | Watch-channel push, freebusy.query for 50 calendars, syncToken incremental sync |
| Microsoft 365 enterprise with delta sync | Microsoft Graph | Delta queries cut sync time dramatically; OData filters; getSchedule for org-wide availability |
| Self-hosted server or iCloud/Fastmail users | CalDAV | No vendor relationship needed; app-password or token auth in minutes; works with any RFC 4791 server |
| Multi-provider SaaS with scheduling or AI agent features | Unified CLI (Nylas) | One auth flow, one response format, one SDK — no protocol-specific branches |
| Internal tool, known server, minimal setup | CalDAV | No cloud console, no OAuth app registration, basic auth over TLS is enough |

**Use Google Calendar API** when your product targets Gmail and Google Workspace users exclusively and you need features that only exist at the Google layer: watch-channel push notifications, the Gmail-flavored freebusy endpoint that returns busy windows for up to 50 calendars in one call, or history-based incremental sync via syncToken.

**Use Microsoft Graph Calendar API** when your users are on Microsoft 365 or Outlook.com and enterprise features matter: delta queries that return only changed events, OData query parameters for server-side filtering, and webhook subscriptions that don't require open TCP connections. Admin consent requirements make Graph slower to deploy in enterprise accounts, but once approved it's reliable.

**Use CalDAV** when your users are on iCloud, Fastmail, Proton, or a self-hosted server, or when you want to avoid vendor lock-in entirely. CalDAV has no push support — plan for polling — and VFREEBUSY isn't universally implemented. For iCloud specifically, you'll work with basic auth and app-specific passwords rather than OAuth 2.0.

**Use a unified API** when you support users across multiple providers and don't want to maintain separate Google Cloud Console apps, Azure AD registrations, and CalDAV implementations. A unified layer normalizes auth, response format, and error handling across all providers behind one interface.

## Next steps

Pick the API that matches your provider mix. If you want multi-provider calendar access from the terminal without building three separate integrations, the CLI handles it today:

```bash
# Install
brew install nylas/nylas-cli/nylas

# Authenticate
nylas auth config --api-key YOUR_API_KEY

# List upcoming events from any connected calendar provider
nylas calendar events list --days 7
```

- [Full command reference](https://cli.nylas.com/docs/commands) — all calendar, email, and contact commands
- [Manage calendar events from the terminal](https://cli.nylas.com/guides/manage-calendar-from-terminal) — DST-aware scheduling, AI-powered meeting finder, and timezone locking
- [Manage Google Calendar from the CLI](https://cli.nylas.com/guides/manage-google-calendar-cli) — Google Calendar-specific commands and OAuth setup
- [Sync calendars across providers](https://cli.nylas.com/guides/sync-calendars-across-providers) — move events between Google Calendar, Outlook, and iCloud
- [IMAP vs Gmail API vs Graph API](https://cli.nylas.com/guides/imap-vs-gmail-api-vs-graph-api) — the same comparison for email protocols
- [Nylas developer docs](https://developer.nylas.com/) — full Calendar API reference
