Webhooks vs polling for Google Calendar and Outlook changes
Updated
TL;DR
Use both. Polling every calendar spends quota on calls that find nothing, and Google calls it an anti-pattern. Push alone misses changes: Google says "Notifications are not 100% reliable" and Graph says "Dropped notifications can't be recovered." The design that works: a push notification triggers an incremental sync (Google's syncToken, Graph's delta query), and a scheduled sync catches whatever push missed.
The short version
| Polling | Push notifications | |
|---|---|---|
| How you learn of a change | You ask, on a schedule | The provider calls your endpoint |
| Delay | Up to your polling interval | Usually seconds; Graph lists event latency as "Unknown" |
| Quota | Every call counts, even when nothing changed | Calls only when something changed, plus renewals |
| Reliability | Never misses a change, eventually | Can drop notifications |
| Setup | A loop and a sync token | A public HTTPS endpoint, validation, renewals |
Neither is enough alone. Both providers' docs point to the same design: push to know when, incremental sync to know what, and a scheduled sync as the safety net.
What polling costs
Google's quota guide is direct: "An anti-pattern here is to repeatedly poll every calendar of interest." Its worked example is 5,000 users polled once a minute, which needs at least 5,000 requests a minute of quota just to find out nothing changed. The limits are 10,000 requests per minute per project and 600 per minute per user, and push notifications "allow for dramatically more efficient use of your quota."
Outlook's limit is per app per mailbox: 10,000 requests in 10 minutes and four at a time. Polling one mailbox every minute uses a small share of that, but every call still costs you a request and a round trip, multiplied by every calendar you watch.
What push costs
Google sends a header-only notification when a watched calendar changes. The channel expires, "there's no automatic way to renew a notification channel", and you replace it before it runs out, with an overlap. Google's push guide also says: "Notifications are not 100% reliable. Expect a small percentage of messages to get dropped under normal working conditions."
Microsoft Graph subscriptions on Outlook events last at most 10,080 minutes, and a mailbox allows "a maximum of 1,000 active subscriptions per mailbox for all applications." Your endpoint must answer the validation request within 10 seconds and each notification within 3. Graph retries "for up to 4 hours", but an endpoint that answers slowly is marked slow and its notifications delayed, and "Dropped notifications can't be recovered." Add a lifecycle URL: Graph sends missed when notifications weren't delivered, and Microsoft's answer to that is to "Perform a full data resync" with the delta query.
Latency is a fair question, and Microsoft doesn't commit to a number: its change notifications overview lists both average and maximum latency for events as "Unknown".
The hybrid both providers recommend
- Subscribe to every calendar you sync, and renew before expiry.
- On every notification, run an incremental sync for that calendar: Google's
syncToken, Graph's delta link. The notification says "look again", and the sync says what changed. - Run the same incremental sync on a schedule, every few hours, so a dropped notification costs you hours of delay, not a missing change.
- On a lost or expired token, re-read your window and compare it with what you have.
Microsoft describes the same pattern: change notifications plus delta query "nearly eliminate ... the need to frequently poll". Calendar webhooks that keep working covers each provider's setup.
How Pyas does it
Pyas runs the hybrid for every connected Google and Outlook calendar and sends you webhooks, so your app never polls a provider:
- Push from both providers, with Google channels and Graph subscriptions renewed before they run out and recreated if a provider drops one.
- A schedule behind it. Subscriptions are checked every hour, and an account that hasn't synced in six hours is synced anyway, so a lost notification is found within hours.
- One webhook per change to your endpoint, signed, naming the event or calendar that changed by its IDs. Your handler reads that one event.
- Delivery you can rely on. Failed deliveries are retried 8 times over about 41 hours and can be replayed for 30 days.
See Pyas webhooks, or test a handler against simulated changes in the sandbox.