Module 3: Google Calendar
Listing and Searching Events
Capsule description
Creating events is only half of it. The other half is reading the calendar: knowing what's there before acting.
Why does it matter? Because you almost never want to create events "blind". Before scheduling a call you want to know if that slot is free. Before sending a reminder you want to list tomorrow's appointments. Before canceling an event you have to find it first. Reading the calendar is what connects Calendar to the rest of the workflow.
This capsule covers the reading operations: Get Many (list events in a date range) and Get (fetch a specific event). And it applies a pattern you already know from Module 1 — "search before you act" — to the calendar: searching whether an event already exists before creating one, so you don't fill the calendar with duplicates.
What you'll learn
By the end of this capsule you'll be able to:
- ✅ List events from a calendar in a date range
- ✅ Filter events by text and other criteria
- ✅ Fetch a specific event by its ID
- ✅ Understand an event's output: which fields it gives you
- ✅ Apply "search before you create" to avoid duplicating events
- ✅ Limit results so you don't bring in a whole calendar
The two reading operations
| Operation | What it does | When to use it |
|---|---|---|
| Get Many | Lists a calendar's events in a range | "Give me this week's appointments" |
| Get | Fetches one event by its ID | When you already have a specific event's ID |
The one you'll use most is Get Many.
Listing events with Get Many
Google Calendar node configuration:
- Resource:
Event - Operation:
Get Many - Calendar: the calendar to query
- After / Before: the date range (from — to)
- Limit: the maximum number of events to bring
The date range
Unlike reading a sheet (where you read "all the rows"), a calendar has no end — there are always more events in the future. That's why Get Many almost always needs a range:
| I want... | After | Before |
|---|---|---|
| Today's events | start of today | end of today |
| Tomorrow's events | start of tomorrow | end of tomorrow |
| This week | today | 7 days from now |
With expressions:
After: {{ $now.startOf('day').toISO() }}
Before: {{ $now.endOf('day').toISO() }}
That lists today's events. For tomorrow:
After: {{ $now.plus({ days: 1 }).startOf('day').toISO() }}
Before: {{ $now.plus({ days: 1 }).endOf('day').toISO() }}
startOf('day')takes the date to the start of the day (00:00);endOf('day')to the end (23:59). Combined, they define "all of day X" — the most common range.
Result
Get Many returns one item per event in the range. If you have 4 appointments tomorrow, you get 4 items.
Filtering what you list
Beyond the date range, Get Many accepts filters to narrow it further:
- Query / Search: brings only events whose text (title, description) matches a term
- Single Events: if you turn it on, recurring events are "expanded" into their individual repetitions instead of coming as a single entry (useful when you work with repeated events — more in capsule 06)
Example: list this week's "calls":
After: {{ $now.startOf('day').toISO() }}
Before: {{ $now.plus({ days: 7 }).toISO() }}
Query: call
Just like with Sheets and Gmail: always narrow it down. A broad range with no query and no limit can bring hundreds of events. Specific is better.
What each event gives you
When you read an event, it comes as an item with fields. The main ones:
| Field | What it contains |
|---|---|
id | The event's unique identifier |
summary | The title |
description | The description |
start | Start date/time |
end | End date/time |
attendees | The guests |
location | The location |
status | Status: confirmed, cancelled, tentative |
htmlLink | A direct link to the event in Google Calendar |
Practical tip: just like in the previous modules, before building the rest of the workflow, run the Get Many and look at the real output. The date fields (
start,end) in particular have an internal structure — look at them to know exactly how to access them.
The htmlLink is especially useful: if your workflow notifies someone about an event, including that link lets them open it directly.
Fetching a specific event with Get
When you already have an event's ID (because you created it earlier and saved the ID, or because you got it from a Get Many), you can fetch it directly:
- Operation:
Get - Calendar: the calendar
- Event ID:
{{ $json.id }}
It returns that specific event, with all its fields up to date. Useful for "seeing the current state" of an event before modifying or canceling it (capsule 05).
The pattern: "search before you create"
Here we apply Module 1's star pattern to the calendar.
The problem
Your workflow creates an event every time a lead comes in. But:
- The same lead fills out the form twice → two identical events
- You test the workflow 5 times → 5 events on the calendar
- The workflow retries → duplicate event
The calendar fills up with repeated appointments. Confusing for everyone.
The solution
Before creating, search whether it already exists:
Trigger
│
▼
Google Calendar (Get Many) ── range: the proposed appointment day/time
query: the customer's name
│
▼
IF: did the lookup return 0 events?
│
┌──────────┴──────────┐
▼ YES (doesn't exist) ▼ NO (there's already an event)
Google Calendar don't create (or update — capsule 05)
(Create)
The IF condition, as in Module 1:
{{ $('Google Calendar').all().length === 0 }}
With this, the workflow is idempotent: the same lead, the same tests, don't generate duplicates on the calendar.
Nuance: "search before you create" in Calendar is a bit subtler than in Sheets. A sheet has a clear key column (email, ID). An event doesn't — two different people can have an appointment "at 10:00". For the lookup to be reliable, search by a specific combination: the exact date range + something identifiable about the customer in the title or description. The mini-project (capsule 08) applies it carefully.
Common traps
Trap 1: Get Many without a date range
What happens: You list "the events" with no After/Before and bring hundreds, or the node doesn't know what to bring.
How to avoid it: A calendar has no end — always define a range. After and Before are your main narrowing.
Trap 2: Badly built date range
What happens: You want "today's events" but you set After = today and Before = today at the same time → zero-duration range, 0 results.
How to avoid it: Use startOf('day') for the After and endOf('day') for the Before. The range should cover the period, not be a point.
Trap 3: Assuming a single lookup result
What happens: You search for a customer's events and assume there's exactly one, but there are two (one old, one new). The workflow processes the wrong one.
How to avoid it: Handle the "several results" case — or make the lookup so specific (range + identifier) that only one can match.
Trap 4: Searching by title only and hitting namesakes
What happens: You search by query "Call with Ana" and bring another Ana's appointment.
How to avoid it: For reliable lookups, combine criteria: a narrow date range + a unique identifier (the customer's email in the description, for example).
Trap 5: Not looking at the structure of start and end
What happens: You try to use {{ $json.start }} directly and get an object, not a readable date.
How to avoid it: Run the Get Many, look at the output, and access the correct subfield of start (it has an internal structure). Don't guess.
Exercise: read your calendar
Goal: practice listing with ranges and the "search before you create" pattern.
Setup
Make sure you have some test events on your calendar (the ones from the capsule 03 exercise work).
Your task
- Today's events: Manual Trigger → Google Calendar (Get Many) with After/Before for today. How many does it bring?
- The week's events: change the range to the next 7 days
- Search with query: add a Query to bring only the events that contain a certain word in the title
- "Search before you create": build Get Many (search for a specific event) → IF (length === 0) → Create (only if it doesn't exist). Run it twice and verify it isn't duplicated
See hints
- After:
{{ $now.startOf('day').toISO() }}, Before:{{ $now.endOf('day').toISO() }} - Before:
{{ $now.plus({ days: 7 }).endOf('day').toISO() }} - Query field with the word to search
- The IF condition:
{{ $('Google Calendar').all().length === 0 }}— Create goes in the true branch
Summary and next step
- Get Many lists events in a date range (After/Before) — a calendar has no end, always narrow down
- Use
startOf('day')/endOf('day')to build "all of day X" ranges - Get fetches a specific event by its ID
- Each event gives you:
id,summary,start,end,attendees,status,htmlLink— look at the real output, especially the structure ofstart/end - The "search before you create" pattern avoids duplicates on the calendar — but the lookup must be specific (range + identifier), not just by title
- 5 traps: no range, zero-duration range, assuming one result, searching by title only, not looking at the date structure
Before moving on you should be able to:
- List a day's and a week's events
- Build a correct date range with expressions
- Apply "search before you create" with a specific lookup
What's next (capsule 05):
You now know how to create and read. What's left are the two operations that close an event's lifecycle: update (change an appointment's time, add a guest) and delete (cancel). Capsule 05 covers them — and here the event's ID takes center stage.
Additional resources
- n8n Google Calendar node Docs - Get and Get Many operations.
- n8n: date methods -
startOf,endOf,plusto build ranges. - n8n IF node Docs - For the "search before you create" pattern.
Created: May 14, 2026 Version: 1.0