Skip to content
C Codeloom
REST APIs

REST Webhooks vs Polling

Choosing between webhooks and polling for event delivery in REST APIs: trade-offs in latency, reliability, security, and operations, with a working example of both.

·4 min read · By Codeloom
Intermediate 9 min read

What you'll learn

  • When polling is the right answer
  • How webhooks deliver and retry
  • Security: signing and verifying payloads
  • Idempotent consumers and dedupe
  • Operational gotchas on both sides

Prerequisites

  • Basic HTTP and REST knowledge

If your API has events worth telling consumers about, you eventually have to choose: do they ask, or do you tell? Polling and webhooks both work; they fail in different ways. Picking the right one early saves you from a painful migration later.

What and Why

Polling is the client periodically calling your API to ask what changed. Webhooks are your server calling the client’s URL when something changes. Polling is pull, webhooks are push.

Polling is dead simple: it reuses your normal REST surface, has no new auth model, and the client is always in control. Its weakness is latency and waste; most polls return nothing. Webhooks deliver near real-time but force you to operate an outbound HTTP system with retries, signing, queues, and dead letters. The provider takes on more work so the consumer can stay reactive.

Mental Model

Picture two diners. A poller walks to the kitchen every five minutes asking if the food is ready. A webhook consumer sits at the table and the waiter brings the plate the moment it is up. The first model is simple but tiring; the second model is faster but requires a working waiter, a known table, and a plan for when the diner is in the bathroom when the food arrives.

Hands-on Example

Polling looks like a paginated changes feed. The client stores a cursor and asks for everything since it.

GET /v1/changes?since=2026-06-28T10:00:00Z&limit=100
{
  "events": [
    { "id": "ev_01", "type": "order.paid",   "at": "2026-06-28T10:00:05Z" },
    { "id": "ev_02", "type": "order.shipped","at": "2026-06-28T10:01:11Z" }
  ],
  "next_cursor": "2026-06-28T10:01:11Z"
}

Webhooks reverse it. The consumer registers a URL and gets POSTs.

POST https://consumer.example.com/hooks/orders
Webhook-Id: ev_01
Webhook-Timestamp: 1719568805
Webhook-Signature: t=1719568805,v1=8a4f...

{ "type": "order.paid", "id": "ev_01", "data": { "order_id": "o_42" } }

The consumer verifies the signature, dedupes by Webhook-Id, and responds 2xx quickly.

Polling:
client --GET /changes?since=cursor--> server
client <--events[]--                  server
(repeat every N seconds)

Webhooks:
event happens
  -> [ outbox / queue ] -> POST consumer URL
                              |
                              2xx -> done
                              non-2xx / timeout -> retry with backoff
                              exhausted -> dead letter + alert
Two delivery styles

A real webhook system is not just “POST when something happens.” It is a queue, a worker, exponential backoff, a dead-letter table, and a replay endpoint so consumers can recover after an outage.

Common Pitfalls

  • Polling too aggressively. A client polling every second across thousands of tenants will dwarf your real traffic. Cache aggressively and return 304 Not Modified when nothing changed.
  • Polling too slowly. Hourly polls make events feel broken. Match the cadence to the business need, not the developer’s comfort.
  • Webhooks without signing. An unsigned endpoint is a free hand to anyone who guesses the URL. Always HMAC the body with a shared secret and a timestamp.
  • Treating the consumer as reliable. It will be down, slow, or rate-limited. Retry with jitter, cap attempts, and dead-letter for human review.
  • No replay path. When a consumer misses a day of events, “we already sent them” is not an answer. Offer a /changes?since= feed even if you push.

Practical Tips

Send minimal payloads with a stable event_id and a link to fetch the full object. That way you do not leak data via stale snapshots, and consumers can verify state. Require a 2xx within a few seconds; anything slower will time out under load. Document your retry schedule so consumers know how long they have to recover. For internal services, a queue like SQS or Kafka beats both webhooks and polling, but only because you control both ends.

If you can only build one, build the polling feed first. It is the source of truth and the recovery mechanism that webhooks rely on anyway.

Wrap-up

Polling trades latency for simplicity; webhooks trade simplicity for latency. The strongest APIs ship both: a push for real-time happiness and a pull for honest recovery. Pick your default based on who feels the most pain when an event is missed, then build the other as a safety net.