WordPress to Google Sheets: Fix Silent Failures & Duplicates
September 28, 2026
Why direct WordPress-to-Sheets looks simple but isn't
Here's the version nobody wants to hear: connecting WordPress to Google Sheets is easy. Keeping that connection honest is the hard part.
Anyone can fire a webhook at Google Apps Script and watch a row appear. The trouble starts later, when a request times out, or Google throttles you, or the same order gets logged twice. According to Google's Sheets API limits page, the default quota is 500 requests per 100 seconds per user, and going over it triggers a 429 error. Most tutorials never mention that number, let alone what happens when you hit it. What happens is your data just doesn't show up, and nothing tells you.
The second problem is duplicates. Webhooks retry on failure. That's the whole point of them. But a retry with no way to recognize "I already handled this one" just appends the same order again. RFC 6585 is blunt about it: webhook handlers need idempotency or they'll produce duplicate data on retry, and that's standard practice for Stripe, GitHub, and Twilio integrations. There's no reason WooCommerce orders should be held to a lower bar.
Third, and this one gets skipped constantly: signature verification. If your handler accepts any POST request that looks like a webhook, it will accept forged ones too. OWASP's guidance on webhook spoofing is clear that verification isn't optional. It's the difference between a webhook endpoint and an open door.
The webhook flow: what actually happens when an order hits Sheets
WooCommerce fires webhooks on order lifecycle events like order.created and order.updated, and the requests are signed to prevent spoofing, per WooCommerce's own webhook documentation. That signing matters, because it's the only thing standing between your sheet and anyone who finds your endpoint URL.
On the receiving end, Google Apps Script is the usual free option. It gives consumer Google accounts 20,000 requests a day, according to Google's Apps Script quota documentation, which is plenty for most small stores and form-heavy sites. Workspace accounts get more.
The flow, in plain terms: WooCommerce sends a signed payload to your Apps Script URL, your script checks the signature, appends a row if everything checks out, and returns a 200 so WooCommerce knows not to retry. Miss any one of those steps and you're exposed to one of the two failure modes below.
Failure mode 1: duplicate rows from retry storms
If your handler times out, or throws a 500, WooCommerce retries automatically. That's good behavior on WooCommerce's part, but bad news for your sheet if you have no way to tell "this is the third attempt at the same order" from "this is a new order."
The fix is an idempotency key. WooCommerce sends an event ID with each webhook. Store it. Before you append a new row, check whether you've already logged that ID. If you have, return 200 and skip the append.
Failure mode 2: silent data loss from rate limits
The other failure mode is the opposite problem: not too much data, but data that vanishes. When you cross Google's quota, the API returns a 429. If your script doesn't catch that and retry, the request just fails, and depending on how WooCommerce is configured, that order may never make it into your sheet at all.
Most DIY setups I've seen don't handle this because it doesn't show up in testing. Then there's a sale, or a marketing email goes out, orders spike, and that's exactly when your logging quietly stops working. Worst possible time for it to happen.
The fix is exponential backoff: catch the 429, wait, retry, wait longer, retry again. If your volume is high enough that this happens often, batch your appends instead of writing one row per request, because it cuts the number of API calls you're making in the first place. Google's guide to Sheets API values notes that append operations are reliable but slow down on large sheets, so batching isn't just a quota fix, it's a performance one too.
I've written more on this pattern generally in [API Rate Limiting: Why Integrations Fail Silent and How to Fix It](/blog/api-rate-limiting-