slice icon Context Slice

Google Calendar API Reference

Authentication

All requests use OAuth Bearer token: Authorization: Bearer PLACEHOLDER_TOKEN. Pipedream handles token refresh.

Base URL: https://www.googleapis.com/calendar/v3/

Core Methods

calendars.get

Get calendar metadata including timezone.

GET /calendars/primary

Response:

{
  "id": "user@example.com",
  "summary": "user@example.com",
  "timeZone": "America/New_York"
}

calendarList.list

List calendars the user has access to.

GET /users/me/calendarList

Returns items[] with calendar metadata. Primary calendar has primary: true.

events.list

Fetch events from a calendar.

GET /calendars/primary/events?timeMin=2024-01-01T00:00:00Z&timeMax=2024-04-01T00:00:00Z&singleEvents=true&orderBy=startTime

Key parameters:

  • timeMin, timeMax — ISO8601 datetime bounds (required for bounded queries)
  • singleEvents=true — Expand recurring events into instances
  • orderBy=startTime — Sort by start (requires singleEvents=true)
  • maxResults — Max events per page (default 250, max 2500)
  • pageToken — For pagination

Response includes items[] with event data and nextPageToken for pagination.

events.get

Get a single event by ID.

GET /calendars/primary/events/{eventId}

Event Object Structure

{
  "id": "abc123",
  "status": "confirmed",
  "summary": "Team Standup",
  "description": "Daily sync",
  "location": "Conference Room A",
  "start": {
    "dateTime": "2024-03-15T09:00:00-05:00",
    "timeZone": "America/New_York"
  },
  "end": {
    "dateTime": "2024-03-15T09:30:00-05:00",
    "timeZone": "America/New_York"
  },
  "attendees": [
    {
      "email": "user@example.com",
      "displayName": "User Name",
      "responseStatus": "accepted",
      "self": true
    },
    {
      "email": "other@example.com",
      "responseStatus": "needsAction"
    }
  ],
  "organizer": {
    "email": "organizer@example.com",
    "displayName": "Organizer Name",
    "self": false
  },
  "conferenceData": {
    "entryPoints": [
      {
        "entryPointType": "video",
        "uri": "https://meet.google.com/abc-defg-hij"
      }
    ]
  },
  "recurringEventId": "originalEventId",
  "created": "2024-03-01T10:00:00.000Z",
  "updated": "2024-03-14T15:30:00.000Z"
}

All-Day Events

All-day events use date instead of dateTime:

{
  "start": { "date": "2024-03-15" },
  "end": { "date": "2024-03-16" }
}

Note: End date is exclusive (March 15 all-day ends on March 16).

Response Status Values

  • needsAction — Not yet responded
  • declined — Declined
  • tentative — Maybe
  • accepted — Accepted

Event Status Values

  • confirmed — Normal event
  • tentative — Tentative event
  • cancelled — Deleted (only returned with showDeleted=true)

Recurring Events

When singleEvents=false (default), recurring events return once with recurrence array.

When singleEvents=true, each instance is returned separately with recurringEventId pointing to the parent.

Rate Limits

Google Calendar API uses per-user quotas:

  • 1,000,000 queries per day
  • Read operations: ~100 requests/100 seconds per user

In practice, pagination handles most use cases without hitting limits.

Common Errors

Error Meaning
401 Unauthorized Token expired or invalid
403 Forbidden Calendar not accessible
404 Not Found Event or calendar doesn't exist
410 Gone Event was deleted
429 Too Many Requests Rate limited, retry with backoff

Useful Patterns

Get Events for Today (user's timezone)

const tz = userTimezone; // e.g., "America/New_York"
const today = new Date().toLocaleDateString('en-CA', { timeZone: tz }); // YYYY-MM-DD
const tomorrow = new Date(Date.now() + 86400000).toLocaleDateString('en-CA', { timeZone: tz });

const params = new URLSearchParams({
  timeMin: `${today}T00:00:00`,
  timeMax: `${tomorrow}T00:00:00`,
  timeZone: tz,
  singleEvents: 'true',
  orderBy: 'startTime',
});

Identify Meeting Type

const hasAttendees = (event.attendees?.length || 0) > 1;
const isAllDay = !event.start?.dateTime;
const hasVideo = !!event.conferenceData || !!event.hangoutLink;
const duration = /* calculate from start/end */;

const isMeeting = hasAttendees && !isAllDay && duration >= 15;