Skip to content
adapters.io

Webhooks vs polling: choosing how your systems talk

6 min read Data engineering The Adapters team

Webhooks push an event to your endpoint the moment it happens; polling asks the source API for changes on a schedule. Webhooks give you seconds of latency but can miss events. Polling never misses what it can see, but is only as fresh as its interval.

Key takeaways

  • Webhooks are a delivery attempt, not a guarantee. Design for duplicates, gaps, and out-of-order arrival.
  • Polling trades latency for completeness, and spends your rate limit doing it.
  • The production answer is both: webhooks for speed, a reconciliation poll for truth.
  • Deletes are the trap: most webhooks announce them, most polling cannot see them.

How webhooks actually fail

A webhook is the source system making an HTTP POST to your endpoint when something changes. When it works, latency is a second or two. The failure modes are what the brochure leaves out:

  • Delivery is best-effort. If your endpoint is down, deploying, or slow, the event is retried on the provider's schedule and then dropped. Stripe retries for up to 3 days; plenty of SaaS tools retry 3 times over an hour and give up quietly.
  • Duplicates are guaranteed. Retries mean at-least-once delivery. Without an idempotency check on the event ID, a payment webhook redelivered twice becomes two invoices.
  • Order is not. invoice.paid can arrive before invoice.created. Consumers that assume sequence corrupt state.
  • Payloads go stale. The event describes the object as it was when the event fired. By the time you process a retried event from yesterday, the record may have changed twice.
  • Unverified endpoints are an open door. Anyone who finds the URL can POST fake events. Signature verification (Stripe's Stripe-Signature header, HubSpot's request signing) is not optional.

What polling really costs

Polling is a loop: every N minutes, ask the API what changed since the last cursor. It is boring and dependable, and it bills you in three currencies:

  • Rate limit. Syncing a 100,000-record object with a page size of 100 is 1,000 requests per sweep. Poll every 5 minutes and that is 288,000 requests a day against quotas like Salesforce's daily API cap. Incremental cursors (updated_at > last_run) fix most of this, when the API offers one.
  • Cursor edge cases. updated_at filters miss records when timestamps have second granularity and two updates share a second, when the server clock skews, or when a bulk import backfills rows with old timestamps. Always overlap the window by a minute and dedupe.
  • Invisible deletes. A deleted record simply stops appearing. Unless the API exposes a tombstone or an events endpoint, polling cannot distinguish "deleted" from "unchanged". Webhooks usually announce *.deleted; this asymmetry alone forces the hybrid design.

Side by side

Dimension Webhooks Polling
Latency Seconds The polling interval
Delivery guarantee At-least-once, until retries expire Complete, for what the query can see
Duplicates Expected; dedupe on event ID Only at window overlaps
Ordering Not guaranteed Whatever sort you request
Rate-limit pressure Near zero The main constraint
Sees deletes Usually, via deleted events Usually not
Backfill / historical No, events only flow forward Yes, page through history
Setup Public endpoint, signatures, queue A scheduler and a cursor

Implementation notes that save an incident

Three habits separate webhook consumers that survive Black Friday from the ones that page somebody:

  • Acknowledge fast, process later. Return 200 within a couple of seconds and push the event onto a queue. Providers time out slow endpoints and count the timeout as a failed delivery; Shopify, for example, deregisters webhooks that keep failing.
  • Treat the payload as a hint, not the truth. On receipt, fetch the object fresh from the API before writing downstream. This one habit neutralizes stale retries and out-of-order delivery at the cost of a single GET.
  • Keep processed event IDs for at least 30 days. Dedupe against that set. Providers can and do redeliver events from days ago after an outage on their side.

For polling, the equivalent habit is one line: persist the cursor transactionally with the records it fetched. A cursor saved before the batch commits is how a crash turns into a permanent gap.

The production answer is both

Mature integrations do not choose. They run webhooks as the fast path and a reconciliation poll as the truth path: events land in seconds, and an hourly or nightly sweep pages through recently updated records to catch anything the webhook layer dropped, plus an initial backfill when the integration first connects. The webhook keeps the CRM fresh; the sweep is why the numbers still agree at month end.

This is how production API adapters are built internally: webhook triggers where the source supports them, scheduled polling where it does not, and idempotent writes so the two paths never double-apply the same change. Idempotency, in turn, depends on a stable match key, rule one of data mapping best practices.

A quick decision framework

  • User is waiting on the result (payment confirmations, signup provisioning): webhooks, with a queue in front and signature checks.
  • Completeness beats freshness (finance, reporting, inventory counts): polling with an overlapped incremental cursor.
  • Source has no webhooks (most databases, older ERPs, spreadsheets): polling is the only option; pick the shortest interval your rate limit affords, 1 to 5 minutes is typical.
  • Two systems must agree over time: both, always. Fast path plus reconciliation.

If you want the mechanics one level up, from trigger to mapped write, see how does data integration work. On a data integration platform the hybrid pattern comes assembled: you pick the pair, and the trigger strategy, retries, and reconciliation are already wired.

Fast path and truth path, prewired

Webhook triggers where sources support them, scheduled sweeps where they do not, idempotent writes everywhere. From $49 a month.

Try the live demo