Google Calendar Client
GoogleCalendarClient is a bundled HakiAPI client for the Google Calendar API v3, built on top of BaseAPIClient. Like GmailClient, it uses resource-based routing — endpoints are grouped under .calendars and .events attributes rather than as flat methods on the client.
Initialization
from hakiapi import GoogleCalendarClient # token is required — GoogleCalendarClient has no unauthenticated mode cal = GoogleCalendarClient(token=access_token)
base_urlis fixed tohttps://www.googleapis.com/calendar/v3/.tokenis required and is always wrapped inBearerTokenAuth.- The token must be a valid OAuth 2.0 access token with the appropriate Calendar scope (e.g.
calendar.readonlyfor read-only access, orcalendarfor full read/write) — see Interactive OAuth 2.0 for getting one viaGoogleOAuthFlow.
Supports the context manager protocol like every BaseAPIClient subclass:
with GoogleCalendarClient(token=access_token) as cal: cal.events.today()
Resources
| Resource | Attribute | Wraps |
|---|---|---|
CalendarsResource | cal.calendars | users/me/calendarList |
CalendarEventsResource | cal.events | calendars/{calendar_id}/events |
calendars — CalendarsResource
cal.calendars.list(max_pages=1)
list(max_pages=None, **kwargs) lazily yields every entry on the authenticated user's calendar list (users/me/calendarList) via the shared paginate() helper. Pass max_pages to cap how many pages are fetched; omit it to walk every page.
events — CalendarEventsResource
cal.events.get(event_id) cal.events.list(calendar_id="primary") cal.events.today() cal.events.upcoming(max_results=5) cal.events.create({"summary": "...", "start": {...}, "end": {...}}) cal.events.delete(event_id)
Every method takes calendar_id as a keyword-or-positional argument, defaulting to "primary" (the user's main calendar).
get(event_id, calendar_id="primary", **kwargs)fetches the full details of one event by ID.list(calendar_id="primary", max_pages=None, **kwargs)lazily yields all events on a calendar, delegating straight topaginate()againstcalendars/{calendar_id}/events.today(calendar_id="primary", **kwargs)fetches every event between midnight today and midnight tomorrow, UTC — not the local timezone. It computestimeMin/timeMaxfromdatetime.now(timezone.utc), setssingleEvents=True(expanding recurring events into individual instances) andorderBy="startTime", then callslist().upcoming(calendar_id="primary", max_results=10, **kwargs)fetches the nextmax_resultsevents starting from right now. SetstimeMinto the current UTC time,maxResults,singleEvents=True, andorderBy="startTime". Unlikelist()andtoday(), this is hardcoded tomax_pages=1— it's meant to return a single page of the soonest events, not paginate through everything; anymax_pagesyou pass is discarded.create(payload, calendar_id="primary", **kwargs)creates a new event.payloadmust includestartandenddatetime objects in the format the Calendar API expects (e.g.{"dateTime": "2025-01-01T10:00:00Z"}), plus whatever other event fields you want (summary,attendees, etc.).delete(event_id, calendar_id="primary", **kwargs)deletes an event. A successful delete typically returns Google's empty204response.
Note:
today()andupcoming()both auto-filltimeMin/timeMax, forcesingleEvents=True, and sort bystartTime— you only need to supply the calendar ID (and, forupcoming(), optionallymax_results).
Errors
GoogleCalendarClient inherits BaseAPIClient's exception handling, so the same typed hierarchy applies:
from hakiapi.core.exceptions import AuthenticationError, RateLimitError, HakiAPIError try: cal.events.upcoming(max_results=5) except AuthenticationError: print("Access token missing, expired, or lacks Calendar scope.") except RateLimitError as e: print(f"Rate limited — retry after {e.retry_after}s") except HakiAPIError as e: print(f"Calendar API error: {e}")
An expired OAuth access token surfaces as AuthenticationError (401/403) — pair GoogleCalendarClient with GoogleOAuthFlow and refresh_access_token() (see the main HakiAPI docs on OAuth 2.0) to keep a long-running process authenticated without manual re-consent.