When your application needs to tell other services that something happened, you have three real options: webhooks (you push to them), polling (they pull from you), and server-sent events (they hold a long-lived connection and you stream to them). WebSockets are a fourth option, but they solve a different problem — bidirectional, real-time interaction — and we'll leave them aside.
The choice is not "which is best" but "which fits this integrator and this event volume." Most production systems end up offering more than one, because different integrators want different things.
Webhooks
Your service makes an HTTP POST to a URL the integrator gave you, with the event payload in the body.
Strengths:
- Real-time delivery (low latency, no polling lag)
- No load on the integrator until events happen
- Simple to integrate from the receiving side — just an HTTP endpoint
Weaknesses:
- Requires the integrator to host a publicly reachable endpoint
- Retry, signing, and replay handling are your responsibility
- The integrator's downtime causes delivery failures you have to manage
- Hard for local development (their endpoint must be reachable from your servers)
Best for: mature integrators with operational maturity, real-time event distribution to a manageable number of subscribers.
Polling
The integrator periodically asks your API for events since a cursor. You return whatever has happened.
GET /events?since=evt_abc123&limit=100
[
{ "id": "evt_def456", "type": "invoice.paid", ... },
{ "id": "evt_ghi789", "type": "user.created", ... }
]
Strengths:
- The integrator controls the pace
- No public endpoint required on the integrator side
- Resilient to outages — if the integrator is down for an hour, it just polls when it comes back
- Easy local development — same flow in dev as in prod
Weaknesses:
- Latency between event and delivery (one poll interval)
- Load proportional to integrator count, even when there are no events
- Cursor management is the integrator's problem and a common source of bugs
- Costs more bandwidth in steady state
Best for: less mature integrators, batch-processing use cases, systems where the integrator wants control.
Server-Sent Events (SSE)
The integrator opens an HTTP connection and you stream events on it as text/event-stream. The connection stays open; you write events as they happen.
GET /events/stream HTTP/1.1
Accept: text/event-stream
id: evt_def456
event: invoice.paid
data: {"id": "inv_abc", "amount": 4500}
id: evt_ghi789
event: user.created
data: {"id": "usr_xyz", "email": "..."}
Strengths:
- Real-time, like webhooks
- No public endpoint on the integrator side
- Built-in reconnection (
Last-Event-IDheader on reconnect to resume from cursor) - Plain HTTP — no extra protocol, works through most proxies and firewalls
Weaknesses:
- Holds a connection per integrator on your servers
- Harder to scale to many subscribers
- Less common — integrators are less familiar with the pattern
- One-way only (use WebSockets for bidirectional)
Best for: real-time delivery to a moderate number of integrators when the integrator cannot host a webhook endpoint, dashboards and admin tools.
Comparison Table
| Aspect | Webhook | Polling | SSE |
|---|---|---|---|
| Latency | Low | Poll-interval | Low |
| Integrator public endpoint required | Yes | No | No |
| Server load proportional to | Event count | Integrator count | Active subscribers |
| Retry semantics | Your problem | Their problem | Reconnect-based |
| Bandwidth in steady state | Low (only on events) | Higher (each poll) | Low (heartbeats) |
| Implementation complexity for you | High (retry, signing) | Low | Moderate |
| Implementation complexity for them | Moderate (HTTP server) | Low (HTTP client) | Low (EventSource) |
| Works through corporate firewalls | Usually | Yes | Usually |
| Order guarantees | Per-resource (with effort) | Strict (cursor) | Strict |
How to Pick
Default to webhooks if your integrator audience is technical and runs services. This is Stripe-style integration. Real-time, low overhead in steady state.
Default to polling if your integrators are less technical or run shorter-lived processes. This is the GitHub-API-style pattern: well-documented, easy to ramp into, accepts some latency.
Add SSE as a third option for real-time dashboards or integrators who cannot host endpoints. Less common but valuable when it fits.
Most mature platforms offer at least two. Stripe offers both webhooks and event polling. GitHub offers both webhooks and the events API. Giving integrators a choice based on their environment increases adoption.
When None of Them Are Right
Sometimes the right answer is a different shape entirely.
Message queue subscriptions. If your integrators are sophisticated and you have a Kafka or similar topic, you can let them subscribe directly. More complex contract, much higher throughput.
Database export. For bulk historical sync, none of the three work well. Provide a periodic snapshot or change-data-capture export. Common for analytics integrations.
API itself. Sometimes "I need to know when this changes" is solved better by "ask the API right before you act." A read-after-write pattern works when freshness matters only at the moment of use.
A Real Decision Framework
Walk through these:
-
Will your integrators run production services that can host an HTTPS endpoint? If yes, webhooks are viable. If no, polling.
-
Is the volume high enough that polling would be wasteful? A few events per day is fine to poll. Thousands per minute is not.
-
Does the integrator need history, or just future events? Polling gives history for free; webhooks do not. If history matters, polling or a separate history API.
-
Is real-time strictly necessary? Most "real-time" requirements are actually "within a minute." Polling at one-minute intervals is often fine.
-
What does the integrator's competitor support? If everyone in your space supports webhooks, integrators expect them.
The Cost of Offering Both
Supporting webhooks and polling means maintaining two integration surfaces. Both have to be documented, tested, and supported. Costs scale with feature additions — a new event type needs to flow through both.
The cost is justified when both surfaces have real users. If 95% of integrators use webhooks and 5% use polling, the polling surface still exists for the 5% — and you cannot deprecate it without breaking them.
Documentation Matters More Than the Mechanism
The integrator experience depends less on which mechanism you offer than on how well you explain it. The best integration docs in the industry have a few things in common:
- Quickstart that gets a "hello world" working in under ten minutes
- Reference docs for every event type with realistic payloads
- Code samples in two or three languages
- A way to test events without performing the business action
- Clear retry, signing, and ordering guarantees
A poorly-documented webhook is harder to integrate than a well-documented polling endpoint. The mechanism is almost a footnote next to docs quality.
Designing the integration surface for an API that other engineering teams will live with for years? We help teams pick the right mechanism — and just as importantly, build docs that make it usable. scopeforged.com