I ran into a problem that looked trivial until I tried to solve it: delete a lot of Google Calendar events without also deleting the ones I still needed.
One-by-one deletion is fine for a handful of leftovers. It falls apart when you have a packed morning, an old project calendar, or a week of junk you want gone. Google Calendar is great at adding meetings. It is awkward when you want something closer to "clear this afternoon, except these three."
I looked for a tool that matched that workflow. I did not find one I liked, so I built GCal Cleaner.
This post is the story behind it, plus the technical choices that ended up mattering more than the delete button.
The product constraint I cared about
Bulk delete is easy to ship as a dangerous shortcut. The harder problem is confidence:
- Narrow the blast radius (one calendar, one day, one timeslot).
- Preview what will be affected.
- Let people uncheck exceptions.
- Make recurring behavior explicit before anything is removed.
That became the whole product:
Wipe a timeslot, keep what you uncheck.
Nothing deletes until you confirm. Guests are not emailed (sendUpdates=none). Recurring events ask whether you mean this day, this and following, or the entire series.
Why a browser app, not a backend
I am a frontend developer, and I wanted something people could open without installing anything. More importantly, I did not want to run a server that stores calendar data.
GCal Cleaner is a static app:
- Angular 22
- Static marketing site in front
- Google Identity Services token client for OAuth
-
Calendar API v3 called directly with
fetch - Hosted on Netlify
There is no GCal Cleaner API in the middle. The browser talks to Google. Access tokens live in localStorage for about an hour. There is no refresh token; reconnect is an explicit user action. Sign-out revokes the token when possible.
That architecture is not just a cost decision. For a destructive tool, I want the trust story to be simple: if my server is down, your events are not sitting in my database either.
Scopes are intentionally limited to what the app needs: event management, calendar list (read), calendar delete for secondary calendars, plus basic profile info for the signed-in UI.
The workflow is the feature
The UI is deliberately scoped:
- Sign in with Google.
- Pick a writable calendar.
- Pick a day and a timeslot (full day, morning, afternoon, evening, or custom).
- Load events that overlap that slot.
- Uncheck anything you want to keep.
- Choose recurring behavior if needed.
- Confirm, then delete with progress, pause, and stop.
A few details that make this feel safer in practice:
- In-slot events start checked. Selection is tracked as an "unchecked" set, so the default action is "clean the slot," not "manually select everything."
- Events outside the slot stay visible but cannot be selected. That keeps context without widening the blast radius.
- All-day events only enter the selection when the slot is the full day.
- The confirm modal shows titles before the run starts.
This is the same pattern you want in any bulk UI: select, preview, exclude, then mutate.
The interesting part: recurring events
Listing a day with singleEvents=true is the easy half. You get expanded occurrences, which is what humans expect in a day view.
Deletion is harder, because "delete this recurring event" can mean three different API operations:
| Mode | What the app does |
|---|---|
| This day only |
DELETE the occurrence id |
| Entire series | Deduplicate by recurringEventId, then DELETE the master once |
| This and following | Fetch the master, then PATCH its RRULE with an UNTIL just before the selected occurrence |
"This and following" was the sharp edge. Google does not give you a single "delete from here forward" endpoint that matches Calendar's UI perfectly, so the app:
- Finds the earliest selected occurrence in each series.
- Loads the master event.
- Rewrites recurrence: strip existing
UNTIL/COUNT, set a newUNTILone second earlier (or one day earlier for all-day events). - Falls back to deleting the instance (or the whole master) when the master is missing, has no
RRULE, or the computedUNTILwould land before the series start.
Default mode is this day only. That felt like the least surprising default for a cleanup tool.
Rate limits, pauses, and hour-long tokens
Deleting hundreds of events is where a toy script dies and a real tool shows up.
The delete runner uses a small burst queue:
- concurrency of 4 deletes (2 for master reads)
- short stagger inside a burst
- gap between bursts
- retries with exponential backoff, jitter, and
Retry-After - 404/410 treated as success ("already gone")
- mid-run 401 pauses the job and asks you to reconnect
Pause and stop are first-class. Pause aborts in-flight fetches and requeues unfinished items; stop cancels the run. That mattered once I hit long cleanups against Google's quotas and one-hour access tokens.
There is also a secondary-calendar wipe path for owners: try deleting the calendar, and if Google says it is too large, list event ids, burst-delete them, then try the calendar delete again. That was not in the original sketch. Real calendars forced it.
What this taught me about bulk UIs
The delete call is boring. The product work is everything around it:
- constrain the domain before offering power
- show the affected set clearly
- make exceptions cheap
- name irreversible modes in plain language
- expect auth expiry and rate limits during long operations
Those ideas transfer to admin panels, file cleaners, CMS bulk actions, and anywhere users modify many records at once. Speed helps. Predictability is what makes people willing to click confirm.
What I still want to learn
I am still iterating on GCal Cleaner, and feedback from people with messy calendars is more useful than another feature idea invented in isolation.
- What makes you hesitate before a bulk calendar delete?
- Do you think in days, timeslots, keywords, or some mix?
- Which recurring behavior needs the clearest warning copy?
- What would make you trust a browser tool with a busy work calendar?
If you have ever wanted a "delete this afternoon" control that Google does not ship, try it at https://gcal-cleaner.com/. It is free right now.
I built it for my own cleanup pain. I would love to hear what works, what feels scary, and what you would change in the workflow.


Top comments (0)