Polling vs Webhooks for Google Sheets Updates
Google Sheets has no native webhook support. Here are the practical patterns — polling with SheetsAPI, Apps Script triggers, and Google Cloud Pub/Sub — and when to use each.
The problem: Google Sheets has no native webhooks
If you have ever built something on top of a Google Sheet and needed to react to changes, you have run into this wall: Google Sheets does not support HTTP webhooks. There is no "notify this URL when a row is added" setting. You cannot register an endpoint the way you can with Stripe or GitHub.
That gap forces you to choose between several workarounds, each with real tradeoffs. This post covers the three most practical ones and tells you which to reach for depending on what you are building.
Option 1: Polling with SheetsAPI
The simplest approach is to poll — hit the API on an interval and compare what you get to what you saw last time.
With GKit SheetsAPI, you already have a REST endpoint for your sheet. A basic polling loop in any language looks like this:
const POLL_INTERVAL_MS = 30_000; // 30 seconds
async function pollSheet(sheetId: string, apiKey: string) {
let lastRowCount = 0;
setInterval(async () => {
const res = await fetch(
`https://sheetsapi.gkit.mreshank.com/api/v1/sheets/${sheetId}/rows`,
{ headers: { "x-api-key": apiKey } }
);
const { rows } = await res.json();
if (rows.length !== lastRowCount) {
console.log(`Sheet changed: ${lastRowCount} → ${rows.length} rows`);
lastRowCount = rows.length;
// handle the change
}
}, POLL_INTERVAL_MS);
}If your sheet has a column like updated_at that you control, you can filter more precisely — only fetch rows updated since the last poll — which cuts down on unnecessary processing when the sheet is large.
When polling is the right call:
- Your use case tolerates a delay of 30 seconds to a few minutes (displaying a leaderboard, refreshing a pricing page, syncing a dashboard).
- You are reading data, not reacting to individual write events.
- You want the simplest possible setup with no Google-side configuration.
The downside: Polling is inherently eventual. If you need to react within seconds of a cell change, polling is not the right tool.
Option 2: Apps Script onChange trigger + UrlFetchApp
Google Apps Script runs JavaScript inside Google's infrastructure and has direct access to sheet events. You can attach a trigger to the onChange or onEdit event that fires a request to your server the moment something changes.
Here is a complete Apps Script example. Open your spreadsheet, go to Extensions → Apps Script, paste this in, and then set up a trigger from Triggers → Add Trigger:
// apps-script/notify-on-change.gs
const WEBHOOK_URL = "https://your-app.example.com/api/sheet-changed";
const SECRET = "your-shared-secret"; // validate this on your server
function onSheetChange(e) {
const payload = {
changeType: e.changeType, // "EDIT", "INSERT_ROW", "REMOVE_ROW", etc.
sheetName: e.source.getActiveSheet().getName(),
timestamp: new Date().toISOString(),
secret: SECRET,
};
UrlFetchApp.fetch(WEBHOOK_URL, {
method: "post",
contentType: "application/json",
payload: JSON.stringify(payload),
// Don't throw on non-2xx so the trigger doesn't silently fail
muteHttpExceptions: true,
});
}To wire it up, go to the Apps Script editor's Triggers panel (the clock icon) and create an installable trigger:
- Function to run:
onSheetChange - Event source: From spreadsheet
- Event type: On change
The onChange trigger fires on structural edits (rows inserted or deleted, columns added). For cell-level edits, use onEdit — but note that onEdit is a simple trigger and cannot call UrlFetchApp; you must use an installable trigger version instead.
Time-driven triggers as an alternative to polling
Apps Script also supports time-driven triggers — you can schedule a function to run at regular intervals. The minimum interval is 1 minute. This is essentially server-side polling: Google runs your script on a schedule and you push the result rather than pull it. It is more reliable than client-side polling because the execution happens inside Google's infrastructure close to the sheet data.
When Apps Script triggers are the right call:
- You need near-real-time notification of write events (row added, cell edited, form submitted).
- You are already comfortable with a small amount of Apps Script boilerplate.
- You want Google to handle the execution infrastructure.
The downside: Apps Script runs under a Google account and has quota limits (executions per day, URL fetch calls per day). For very high-frequency sheets, you can hit these. The trigger also runs with the permissions of the account that set it up — if that account loses access to the sheet, the trigger silently stops firing.
Option 3: Google Drive API push notifications
Google Drive's API supports push notifications — a proper webhook mechanism where Drive calls your server when a file changes. Since every Google Sheet is a Drive file, this technically works for sheets too.
The setup involves:
- Registering a notification channel via
drive.files.watchwith your endpoint URL and a channel ID you generate. - Receiving
POSTrequests at your endpoint with aX-Goog-Resource-Stateheader (update,change, etc.). - Renewing the channel before it expires (channels expire after at most one week and must be re-registered).
- Your server handling duplicate deliveries gracefully — Drive does not guarantee exactly-once.
The Drive API does not tell you what changed, only that the file changed. If you need the actual new data, you still have to call the Sheets API (or SheetsAPI) after receiving the notification. The Drive notification is purely a "something happened" signal.
When Drive push notifications make sense:
- You need low-latency notification and cannot use Apps Script (e.g., the sheet is owned by a service account, not a user).
- You are already working within Google Cloud infrastructure and can handle the channel renewal lifecycle.
- You are building a production system and want to avoid per-user Apps Script quotas.
The downside: Meaningfully more complex to set up and maintain. You need a publicly accessible HTTPS endpoint, channel renewal logic, and duplicate-handling. It is the most powerful option but has the highest operational overhead.
Comparison at a glance
| Polling (SheetsAPI) | Apps Script trigger | Drive push notifications | |
|---|---|---|---|
| Setup complexity | Low | Medium | High |
| Latency | 30 s – 5 min (your choice) | Seconds | Seconds |
| Tells you what changed | Yes (you read the data) | Partially (event type + sheet name) | No (file-changed signal only) |
| Runs inside Google infra | No | Yes | No (your server) |
| Quota concerns | SheetsAPI rate limits | Apps Script daily quotas | Drive API quotas |
| Channel renewal needed | No | No | Yes (weekly) |
| Works with service accounts | Yes | No | Yes |
The practical recommendation
Start with polling if you are reading data and the use case can tolerate a short delay. It requires zero Google-side configuration, no Apps Script, and no webhook endpoint — just a scheduled job or a setInterval. For most dashboards, content sites, and data-sync pipelines, this is enough.
Move to Apps Script onChange triggers when you need to react to write events quickly — a new form submission, a row being added by a teammate, a status column being updated. The boilerplate is small and the latency is good.
Only reach for Drive API push notifications if you are building something production-grade that cannot live with Apps Script's per-user quota limits, or if the sheet is managed by a service account rather than a human.
In most side projects and small-business tools, the Apps Script trigger path covers the "real-time" requirement at a fraction of the complexity of a proper webhook pipeline. Get it working, then optimize if you outgrow it.
Ready to connect a sheet and start polling? Set up SheetsAPI in under a minute — no infrastructure required.