Blog · Industry

Webhooks or polling: how your systems find out

October 28, 2026 · The Murmurator team

Two ways for one system to learn that something happened in another: ask repeatedly, or be told. Most teams don't choose — they use whatever the vendor documented first. The choice has consequences that surface months later.

Polling

You ask on a schedule: anything new since the last time I asked?

What's good about it is entirely about control. You decide when the work happens, so load is predictable and fits your capacity. Nothing is exposed to the internet. If your consumer is down for an hour, the next poll picks up everything missed — no special recovery logic, because catching up is the normal path. Debugging is straightforward: run the request yourself and see what comes back.

What's bad is efficiency and latency. Polling every minute for an event that occurs twice a day means over a thousand wasted requests per event. Push that interval down to get lower latency and the waste multiplies, while the worst-case latency still equals the interval. Rate limits become a real constraint, and they're consumed by requests that mostly return nothing.

Polling also needs a reliable cursor. updated_since timestamps are the common choice and the common bug: clock skew, records updated during your query window, and ties at the same timestamp all cause silently missed records. Use an opaque cursor or sequence number where the API offers one, and overlap your windows slightly when it doesn't — duplicates you can dedupe, gaps you can't detect.

Webhooks

The provider calls you when something happens.

Latency is near-zero and there's no wasted traffic. For high-volume, low-frequency events it's obviously correct — a thousand polls replaced by one request.

The costs are all operational. You now run a public HTTP endpoint with an availability requirement, and the consequences of being down are not symmetrical with polling. Providers retry, but with their policy, not yours: some retry for hours with backoff, some give up after three attempts in five minutes, some don't retry at all. If your endpoint is down longer than their patience, those events are gone. There's no catch-up unless the provider offers a replay API, and many don't.

Delivery is also at-least-once and unordered. The same event arrives twice; a created and an updated land out of order. Handlers must be idempotent and must not assume sequence.

And it's an attack surface. An endpoint that accepts unauthenticated POSTs and acts on them is an open API for anyone who guesses the URL. Signature verification isn't optional, and it has to be done correctly — constant-time comparison, timestamp checking to prevent replay, and rejection rather than logging when it fails.

The pattern that works

For anything where missing an event is expensive, most mature systems end up running both: webhooks for latency, a periodic reconciliation poll for completeness.

The webhook handles the normal case in seconds. A less frequent sweep — hourly, nightly — asks "what changed since my last successful sync?" and fills gaps from failed deliveries, downtime and the provider's own dropped sends.

This costs more to build than either alone, and it's the only configuration that is both fast and correct. The reconciliation pass is also the thing that tells you your webhook pipeline has been broken for two days, which is information you otherwise get from a customer.

Receiving webhooks well

A few things that separate handlers that survive from handlers that don't:

Acknowledge immediately, process asynchronously. Verify the signature, persist the raw payload, return 200. Do the work in a background job. Handlers that process inline eventually exceed the provider's timeout, get marked failed, and receive a duplicate — while the original work completes. Now you have the duplicate and a false failure.

Persist before processing. Store the raw body before any parsing. When a handler has a bug, replaying stored payloads is the difference between a fix and a data loss incident.

Verify signatures properly. Compare in constant time. Reject old timestamps. Don't skip verification in staging — that's how it ends up skipped in production.

Return the right status codes. 2xx means "I have durably accepted this." Returning 200 from a handler that then loses the payload tells the provider to stop retrying. Return 5xx when you genuinely couldn't accept it, so the retry happens.

Expect duplicates and reordering. Dedupe on the provider's event ID. Never assume sequence.

Monitor the gap. Alert on absence — a webhook source that has been silent for longer than its normal quiet period is more likely broken than genuinely idle. Silence is the failure mode that looks like success.

Choosing

Use polling when: events are frequent enough that most polls return work, you can't expose an endpoint, the provider's retry policy is weak, or you need strict control over processing rate.

Use webhooks when: events are infrequent relative to poll frequency, latency matters, or you're approaching rate limits.

Use both when missing an event has real consequences — which, in practice, is most of the cases worth automating.

Turn one person's AI process into the company's.

Murmurator is invite-only. Already invited? Sign in and build something real.