Webhook vs. Polling: Pick the Right Data Sync Pattern
For: A backend engineer or technical product lead at a B2B SaaS company who just inherited an integration with a third-party payment, ERP, or logistics API and needs to decide whether to receive event pushes via webhook or poll on a schedule — before they wire up the wrong pattern and spend months firefighting missed updates or runaway API quota bills
Use webhooks when the upstream vendor gives you signed payloads, idempotency keys, ordered delivery (or a monotonic event ID), and documented retry semantics — and when your consumer is idempotent. Use polling in every other case, or when the data is your system of record and a missed event means money or medication goes to the wrong place. The webhook vs polling debate is not about latency. It is about whether you trust the sender enough to let it drive your state machine.
Everything below is the long version of that answer, aimed at the engineer who just inherited a Stripe, NetSuite, Shippo, or Razorpay integration and has to decide before Monday's sprint planning.
The framing everyone gets wrong
Most comparisons of webhook vs polling read like this: webhooks are real-time and efficient, polling is simple and wastes quota, pick based on how fresh your data needs to be. That framing is fine if you are building a hobby project. It is dangerous in production because it hides the two dimensions that actually cause incidents:
- Delivery guarantee of the sender. Does the upstream vendor retry failures? For how long? In what order? Do they include a monotonically increasing event ID or timestamp you can use to detect out-of-order arrivals? Do they sign payloads?
- Idempotency of the receiver. If the same event arrives twice — or arrives after a later event has already been processed — does your database end up in the correct state, or corrupted?
Latency is the third-most-important axis, not the first. A webhook that arrives 200ms after an event but delivers the "refund issued" message after the "refund reversed" message is worse than a poll that runs every 60 seconds and reads the current authoritative state from the vendor's API.
Head-to-head: webhooks vs. polling vs. hybrid
Three real patterns, compared on the dimensions that actually matter when you are on-call.
| Dimension | Webhooks | Scheduled polling | Hybrid (webhook trigger + poll reconciliation) |
|---|---|---|---|
| Latency to freshness | Seconds | Poll interval (30s–1h typical) | Seconds for hot events, minutes for reconciliation |
| Ordering guarantee | Vendor-dependent. Stripe and Shopify do not guarantee order. GitHub does not either. | You read current state — ordering is irrelevant | Reconciliation heals ordering bugs |
| Duplicate delivery | Common. Vendors retry on 5xx and network timeouts. | None from the transport itself | Same as webhooks, healed by poll |
| Missed events | Silent failure risk if you 500 and vendor gives up (Stripe retries for 3 days, Shopify for 48h, others less) | Impossible to miss — you re-read the source of truth | Reconciliation catches misses |
| API quota cost | Near zero on your side | High. 1 poll/min per resource = 43,200 calls/month per tenant | Moderate — only reconcile periodically |
| Infra complexity | Public HTTPS endpoint, signature verification, dedupe store, DLQ | Cron + cursor state per tenant | Both, plus the reconciliation logic |
| Local dev friction | High (ngrok, webhook.site, replay tooling) | Trivial | High |
| Suitable as system of record? | Only if vendor documents at-least-once delivery + you enforce idempotency | Yes — you read current state directly | Yes |
When webhooks are the right call
Webhooks win when all four of the following are true:
- The vendor documents retry behavior. Stripe, Shopify, Twilio, GitHub, Slack, and Zoom all publish this. NetSuite's SuiteAnalytics, most ERPs, and a surprising number of logistics APIs do not.
- Payloads include a stable event ID. You need this to dedupe. Stripe's
event.id, GitHub'sX-GitHub-Delivery, Shopify'sX-Shopify-Webhook-Id. If the vendor does not send one, do not use their webhooks as anything but a wake-up signal. - Payloads are signed. HMAC signature with a shared secret. Without this you are one DNS misconfiguration away from accepting forged payment events from the open internet.
- Your consumer is idempotent by construction. Not "we handle duplicates because we check first." That is a race condition dressed up as safety. Idempotent means the write itself is safe to repeat — upserts keyed on the vendor's event ID, or state transitions gated on a version number.
What webhooks are bad at, honestly: local development, replay after an outage, and any scenario where you need historical backfill. Webhooks are a forward-looking transport. If your consumer was down for six hours and the vendor gave up retrying at four, those events are gone from the webhook channel. You will need a polling or API-based recovery path anyway — so build it from day one.
When polling is the right call
Polling wins in cases most senior engineers underrate:
- The upstream data is the system of record. Bank balances. Inventory counts. Shipment status. If the truth lives in the vendor's database and being wrong costs money, poll. You are re-reading the authoritative value each time; ordering and duplication become non-issues.
- The vendor's webhook implementation is unreliable or undocumented. Many ERPs, older payment gateways, and regional logistics providers publish webhook features that quietly drop events under load. If the docs do not name a retry policy, assume there isn't one.
- You cannot expose a public endpoint. Air-gapped enterprise deployments, on-prem installations, and heavily firewalled hospital networks often cannot accept inbound webhooks. Polling is your only option.
- Traffic is low. If you have 50 tenants and each has a handful of updates per day, the "wasted" poll calls are irrelevant compared to the engineering time saved.
What polling is bad at: latency-sensitive user experiences (a chat "typing" indicator polled every 30 seconds is embarrassing), high-cardinality data where you would need to poll thousands of records to detect the one that changed, and vendors with strict rate limits. If the vendor allows 100 requests/minute and you have 200 tenants, per-minute polling is off the table without sharding.
The hybrid pattern most production systems actually need
For anything financial, medical, or logistical — the domains where CodeNicely spends most of its time — the correct architecture is almost always hybrid:
- Webhook as trigger, not truth. When a webhook arrives, treat it as "something might have changed for entity X." Verify the signature, dedupe by event ID, then call the vendor's API to fetch the current authoritative state of that entity. This eliminates ordering bugs — the API always returns the latest state, regardless of the order webhooks arrive.
- Scheduled reconciliation poll. Every 15 minutes or every hour, poll a "list changed since cursor T" endpoint. Compare against your local state. Fix drift. This catches missed webhooks, vendor outages, and bugs in your handler.
- Dead-letter queue for failed webhook processing. Anything that fails after N retries goes to a DLQ with the raw payload, the signature, and the timestamp. A human triages. This is not optional — it is how you find out your handler broke on a payload shape the vendor started sending last Tuesday.
The tradeoff: you are now maintaining three code paths instead of one. This is the honest cost of doing real-time data sync architecture correctly. Teams that skip the reconciliation poll are the ones filing tickets six months later titled "why does our order count not match Shopify's dashboard."
Concrete rules of thumb
- Stripe, Shopify, GitHub, Twilio, Slack: webhooks are safe as a trigger. Always fetch current state via API before writing. Always store the event ID for dedupe with a TTL of at least 7 days.
- NetSuite, SAP, older Oracle systems: poll. Their webhook stories are either absent or fragile. Use their saved-search or change-data-capture endpoints with a cursor.
- Payment reconciliation, invoice sync, ledger updates: hybrid, always. The cost of a missed event is a customer-support ticket at best, a compliance issue at worst.
- Logistics status updates (shipping carriers): hybrid. Carriers vary wildly in webhook quality. Poll every 30–60 minutes as backstop.
- Chat, notifications, presence: webhooks or long-polling. Latency dominates; occasional loss is acceptable.
The failure mode nobody warns you about
Webhook consumers that look idempotent but aren't. Example: you receive invoice.paid, check "is invoice already marked paid?", see no, then update. Two webhook deliveries race. Both check, both see no, both update. Now you have two payment records, or you fired two fulfillment jobs.
The fix is either a unique constraint on the vendor's event ID in your dedupe table (inserted before processing), or an optimistic-concurrency version column on the entity being updated. "Check then write" without a database-level constraint is not idempotency. It is a bug waiting for traffic.
How CodeNicely can help
Choosing the right sync pattern is a decision most teams only get to make once — after that it is very expensive to change. Our engagement with GimBooks, a YC-backed accounting SaaS, is directly relevant here: accounting software lives or dies on ledger correctness, and the integrations with payment gateways, GST systems, and bank feeds each had different reliability profiles. We built a hybrid ingestion layer with webhook-as-trigger plus scheduled reconciliation, event-ID dedupe, and a DLQ that a support engineer could triage without engineering involvement.
If you are inheriting a third-party integration and need a second set of eyes on the architecture before you commit — or if your current webhook pipeline is dropping events and you cannot figure out why — we do focused technical audits of integration layers. See our offerings or the digital transformation page for how we typically engage with SMB and enterprise teams on this class of problem.
Frequently Asked Questions
Can I just use webhooks and skip the reconciliation poll?
You can, but only if the vendor documents at-least-once delivery with a long retry window (Stripe's 3 days, for example), you enforce idempotency at the database level, and a missed event is not a compliance or financial issue. For anything touching money, inventory, or health data, add reconciliation. The cost of running an hourly poll is trivial compared to explaining a drift to a customer.
What's a safe polling interval that won't blow through API quotas?
Start with the vendor's documented rate limit divided by your tenant count, then halve it for safety. Use conditional requests (ETag, If-Modified-Since) or cursor-based "changes since" endpoints whenever the vendor supports them — they cost the same as a poll but return nothing when there are no changes. For high-tenant-count SaaS, shard polling across a rolling window rather than firing all tenants on the same cron tick.
How do I test webhook handlers locally without exposing my laptop to the internet?
Use the vendor's CLI if they have one — Stripe CLI, Shopify CLI, and GitHub's gh webhook forward are the gold standard because they replay real event shapes. Fall back to ngrok or Cloudflare Tunnel for vendors without CLI support. Never test only with hand-crafted payloads; real vendor payloads have fields the docs don't mention.
Should signature verification happen before or after enqueuing the webhook?
Before. Always. Verify the HMAC signature at the edge — before the payload touches your queue, your logs, or your database. An unverified payload is untrusted input from the open internet. Return 401 immediately on signature failure and do not retry on your end.
What's the right dedupe TTL for stored event IDs?
At least as long as the vendor's maximum retry window, plus a safety buffer. Stripe retries for 3 days, so 7 days is a reasonable floor. For vendors that don't document a retry window, use 30 days. Store the event ID with a unique constraint and let the database's insert failure be your dedupe signal — cheaper and safer than a read-then-write check.
How much does it cost to build a production-grade hybrid webhook pipeline?
That depends heavily on the vendor mix, tenant count, and your existing infrastructure. Contact CodeNicely for a personalized assessment — we can usually scope this after a one-hour review of your current integration inventory.
Building something in SaaS?
CodeNicely partners with founders and tech teams to ship AI-native products that move metrics. Tell us about the problem you're solving.
Talk to our team_1751731246795-BygAaJJK.png)