Skip to content
TryWebhook
New URL

Guide · Last reviewed

Webhooks vs polling

Webhooks are usually presented as the modern replacement for polling. They are not a replacement — they trade a predictable cost for a lower one and take on a harder failure model in exchange.

The actual trade

Polling asks “has anything changed?” on a schedule. Webhooks are told “this changed” when it happens. Both get you the same information; they differ in what they cost and in how they fail.

Polling Webhooks
Latency Half your interval, on average Under a second
Requests for a quiet resource Every interval, forever Zero
Public URL needed No Yes
Missed an event? Impossible — you re-read state Possible, permanently
Ordering You control it None guaranteed
Local development Trivial Needs a tunnel or a capture URL
Debugging Re-run the request Inspect a delivery you cannot re-request
Provider rate limits Your main constraint Not a factor
Failure mode You retry, on your terms The provider retries, then gives up

Read the last two rows together, because they are the real decision. Polling makes you responsible for the schedule, which is annoying and completely under your control. Webhooks make the provider responsible for delivery, which is efficient and completely outside it.

The arithmetic that pushes people to webhooks

One resource polled every minute is 1,440 requests a day. A hundred customers’ resources polled every minute is 144,000, and you will hit a rate limit long before that.

Meanwhile, on a normal day, essentially all of those requests return “nothing changed”. You are paying full price for the information that nothing happened, and still accepting up to sixty seconds of latency.

Webhooks invert both numbers: zero requests when nothing happens, sub-second latency when something does. For anything event-shaped and infrequent — a payment completing, a build finishing, a subscription cancelling — that is not a marginal improvement.

Where polling is genuinely the better choice

It is worth being specific, because “use webhooks” has become the default advice regardless of context.

You cannot receive inbound requests. No public URL, a corporate firewall, a mobile app, a CLI tool, a client-side integration. Polling needs only outbound access.

You need a completeness guarantee. Reconciliation, financial reporting, anything where “we might have missed one” is not an acceptable answer. Polling re-reads state, so a missed poll is corrected by the next one. A missed webhook is gone.

Order matters and the events are dense. Polling returns a consistent snapshot in a defined order. Webhooks arrive in whatever order the network produced, and rebuilding a sequence from unordered notifications is real work.

The provider’s webhooks are unreliable or unsigned. Some are fire-and-forget with no retry and no signature. Supabase Database Webhooks, for example, are pg_net calls that are not retried at all. Polling their API is more dependable than trusting those deliveries.

You are still building. Polling works on your laptop with no tunnel, no ngrok, and no deploy. Get the logic right first; make it fast later.

Where webhooks are the only sensible option

Latency is visible to a user. Someone finished checkout and is watching a spinner. A sixty-second poll interval is a sixty-second spinner.

The resource set is large. You cannot poll ten thousand customers’ subscriptions individually, and most APIs offer no “everything that changed since” endpoint that scales.

Events have no queryable state. “A message was delivered”, “a call ended”, “an email bounced” — these happened and are over. There is often no object you can poll to discover them after the fact.

The provider only offers webhooks. Increasingly common for event streams. Delivery events from SendGrid and Mailgun exist nowhere else.

What mature integrations actually do

Both, in specific roles.

The webhook is the fast path. It arrives, you verify it, and you queue it. Latency stays under a second and the request volume is zero when nothing is happening.

The polling job is the safety net. A periodic sweep — hourly, nightly, whatever matches your tolerance — lists everything changed since the last run and compares it against your database:

// runs on a schedule, not in the request path
const since = await getLastReconciledAt();
for await (const charge of stripe.charges.list({ created: { gte: since } })) {
  await upsertCharge(charge);   // idempotent, so overlap with webhooks is harmless
}
await setLastReconciledAt(now);

This catches four things the webhook cannot:

  • deliveries dropped after the provider’s retry window closed
  • events that fired while you were deploying, from a provider that does not retry
  • events from before the endpoint existed
  • anything your handler processed incorrectly and then acknowledged

It only works if your writes are idempotent, which they should already be, because duplicates are unavoidable on the webhook path too. The same upsert serves both.

The reconciliation job is also the answer to “how do we know the integration is healthy?” If a sweep regularly finds objects the webhooks should have delivered, something is wrong — and you found out from your own data rather than from a customer.

What about WebSockets and SSE?

Different problem, though they get compared.

WebSockets and server-sent events keep a connection open between a client and a server, for as long as the client is there. They are how a dashboard updates live or a chat message appears instantly. When the tab closes, the connection closes, and events that occur afterwards are not delivered.

Webhooks are server-to-server and must work when nobody is watching. A payment at 3 a.m. still has to be processed.

The two compose rather than compete: the provider’s webhook hits your server, your server writes the change and pushes it down an open WebSocket to whichever browsers are currently looking. Each layer does the thing it is good at.

A decision procedure

  1. Can you receive inbound HTTPS in production? If not, poll. Nothing else to decide.
  2. Does the event have queryable state? If not — delivery receipts, call events — webhooks are the only source.
  3. Is anyone waiting on it? If yes, webhooks; a poll interval you would accept is longer than a person will.
  4. Would missing one be expensive? If yes, add reconciliation regardless of which you chose. This is not optional for anything involving money.
  5. How many resources? More than a few hundred and polling stops scaling.
  6. Is this the first version? Start with polling if the answers above allow it. Correct and slow beats fast and subtly broken, and the reconciliation job you write now is the one you will keep.

If you land on webhooks, the next two things to get right are verifying the signature and handling duplicates. Those two account for most of the difficulty, and neither has anything to do with the choice you just made.

Answers

Common questions

Are webhooks always better than polling?+

No. Webhooks win on latency and on wasted requests, and lose on reliability, ordering, and how easy they are to debug. If an event is rare and needs to be reacted to within seconds, use a webhook. If you need a guarantee that you have seen everything, poll.

Do I need a public URL for webhooks?+

Yes — the provider has to reach you, so `localhost` will not work. Polling needs only outbound access, which is why it is often the only option behind a corporate firewall or inside a client-side application.

Can I use webhooks and polling together?+

That is what most mature integrations do. The webhook drives the fast path and a periodic reconciliation job catches anything that was dropped, arrived out of order, or happened while you were deploying. The polling job is the safety net, not the main path.

How often can I poll?+

As often as the provider's rate limit allows, which is usually far less often than you want. At one request a minute you burn 1,440 requests a day per resource and still have up to 60 seconds of latency — the arithmetic is what pushes busy integrations to webhooks.

What about WebSockets or server-sent events?+

Different tool, different problem. Those keep a connection open for a client that is currently looking at something — a live dashboard, a chat window. Webhooks are for server-to-server notifications that must work when nobody is watching.

Which should I build first?+

Polling, usually. It is easier to get right, easier to debug, and needs no public URL, so you can have a correct integration quickly. Add webhooks when the latency or the request volume becomes a real problem, and keep the polling job as reconciliation.

Keep reading

Related guides

Try it on a real request

Reading about it only goes so far.

Create a URL, point a provider at it, and inspect the delivery — the real headers, the raw bytes, and a signature you can check in the browser.

No signup · No email · Ready in about a second