Webhooks vs API: What's the Difference?
A webhook pushes, an API pulls. The real differences, when to use each, code examples for both, and the reliability trade-offs nobody warns you about.
If you're integrating two systems, you'll hit this question fast: should you call an API, or receive a webhook? They both move data over HTTP, but they work in opposite directions — and picking the right one saves you a lot of wasted requests.
The one-line answer
- API: you pull. Your app asks the other system for data when it needs it.
- Webhook: they push. The other system sends you data automatically when something happens.
What is a webhook vs an API?
An API (specifically a web API) is an interface you call. You make the request, you choose the timing, and you get a response containing the current state of something.
A webhook is a request you receive. You register a URL with a provider once, and from then on the provider makes the request — to you — every time a relevant event occurs.
Both are HTTP. The difference isn't the technology, it's who initiates the conversation. That single inversion changes almost everything about how you build the integration: where the code runs, how you handle failure, and what you have to secure.
How an API works (pull)
With a typical web API, your application is the one making requests. You want to know if an order shipped, so you call the provider's endpoint and get the current state back:
GET /v1/orders/ord_123
# -> { "status": "shipped" }
If you want to stay up to date, you have to keep asking — polling every minute or so. Most of those requests return "nothing changed," which wastes time and rate limits.
The arithmetic gets ugly quickly. Polling one endpoint every minute is 1,440 requests per day, or about 43,000 a month. If ten orders actually change state in that time, you made 43,000 requests to learn ten things — a hit rate of roughly 0.02%. Multiply by every customer or resource you're tracking and polling becomes the dominant cost of the integration.
Polling also has a latency floor. If you poll every 5 minutes, you learn about events with an average delay of 2.5 minutes and a worst case of 5. The only way to reduce that is to poll harder, which costs more.
How a webhook works (push)
A webhook flips the direction. You give the provider a URL, and they send you an HTTP POST the moment an event occurs:
POST /your-webhook-endpoint
Content-Type: application/json
X-Stripe-Signature: t=1719849600,v1=5257a869e7...
{ "event": "order.shipped", "id": "ord_123" }
No polling, no wasted calls — you find out as it happens, typically within a second. That's why webhooks are sometimes called "reverse APIs" or "push APIs." (New to them? See what is a webhook.)
Ten events now cost you exactly ten requests instead of 43,000.
Webhooks vs API at a glance
| API (pull) | Webhook (push) | |
|---|---|---|
| Who initiates | Your app | The provider |
| Timing | On demand / polled | Real time, event-driven |
| Efficiency | Wasteful if polling | Only fires on events |
| Typical latency | Half your poll interval | ~1 second |
| Needs a public URL | No | Yes (a URL providers can reach) |
| Best for | Fetching current state | Reacting to events |
| Failure handling | Retry your own call | Provider retries delivery |
| Who's on the hook for uptime | The provider | You |
| Auth direction | You send a key | You verify a signature |
That second-to-last row is the one people underestimate. When you poll, an outage on your side just means you catch up on the next run. When you receive webhooks, an outage on your side means events are arriving at a door nobody is answering.
Is a webhook just a POST API?
Mechanically, yes. There's no special protocol — a webhook is an ordinary HTTP POST with a JSON body. If you've written any endpoint that accepts POST, you already know how to receive one.
What changes is ownership. With an API you control when the call happens, so you can retry, back off, and batch. With a webhook the provider controls the timing, so your engineering problem shifts to a different set of questions:
- Is my endpoint reachable right now?
- Is this request genuinely from the provider, or did someone find my URL?
- Have I already processed this exact event?
- What happens to events that arrive while I'm deploying?
Those four questions are the entire difficulty of webhooks. The HTTP part is trivial.
API vs webhook vs WebSocket vs polling
Push and pull isn't a binary — there are four common options, and they trade off differently:
| Direction | Connection | Latency | Best for | |
|---|---|---|---|---|
| REST API | You → provider | Per request | Immediate on request | On-demand reads and writes |
| Polling | You → provider, on a timer | Per request | Half the interval | Providers with no webhooks |
| Webhook | Provider → you | Per event | ~1 second | Discrete events, server-to-server |
| WebSocket | Bidirectional | Persistent | Milliseconds | High-frequency streams, live UIs |
The rough rule: webhooks for events, WebSockets for streams. A payment succeeding is an event — it happens occasionally and each one matters individually. A price ticker is a stream — it updates constantly and you mostly care about the latest value. See webhook vs WebSocket for the full comparison.
What is an example of a webhook?
The clearest examples come from services you already use:
- Payments — Stripe POSTs
checkout.session.completedwhen a customer pays, so you can fulfil the order without polling for payment status. See receiving Stripe webhooks on localhost. - Code and CI — GitHub POSTs a
pushevent so your build system starts a pipeline the moment code lands. See the GitHub + Jenkins guide. - Ecommerce — Shopify POSTs
orders/createso your fulfilment system sees new orders immediately. - Forms and messaging — Typeform posts new submissions; Slack posts events from your workspace.
If you want to see what real webhook traffic looks like in aggregate — which providers send the most, what payload sizes and content types dominate — we publish live figures from our own ingesters on the State of Webhooks.
When should you use a webhook?
Reach for a webhook when:
- You need to react to events in near real time — payments, deploys, new messages, alerts.
- You'd otherwise be polling constantly and mostly getting "nothing changed."
- The provider is the source of truth for something you can't predict the timing of.
- You're integrating server to server and can keep an endpoint available.
When should you use an API instead?
Reach for an API (or polling) when:
- The provider doesn't offer webhooks for the event you care about.
- You need a full, current snapshot on demand — reconciliation, dashboards, reports.
- You only need the data occasionally, so an event stream is overkill.
- You can't expose an endpoint at all, and a tunnel or gateway isn't an option.
- You need guaranteed completeness and would rather sweep than trust delivery.
What are the disadvantages of webhooks?
This is where most guides stop being useful, so here's the honest list.
You need a publicly reachable URL. Providers can only POST to a public address. Your handler probably runs on localhost or inside a private network, which is why webhook development is annoying in a way API development isn't.
Delivery is at-least-once, not exactly-once. The same event can arrive twice — a retry after a timeout your server actually processed, for example. Handlers must be idempotent: deduplicate on the provider's event ID and make reprocessing a no-op.
Ordering is not guaranteed. order.updated can land before order.created. If sequence matters, sort by a timestamp in the payload or reconcile against the API rather than trusting arrival order.
Retry policies vary wildly, and then stop. Stripe retries with exponential backoff for up to about 3 days. Shopify retries 19 times over 48 hours. GitHub doesn't automatically retry at all — a failed delivery just sits in the log until you manually redeliver it. Check your specific provider's docs, because "the provider retries" is doing a lot of unexamined work in most comparisons. Once the retry window closes, the event is gone.
Anyone can POST to your URL. A public endpoint is a public endpoint. Without signature verification, an attacker who discovers it can forge events — "payment succeeded" being the obvious nightmare.
Silent failure is the default. If a provider disables your endpoint after repeated failures, nothing in your application notices. You have to monitor deliveries deliberately.
None of these are reasons to avoid webhooks. They're the reason webhook infrastructure exists.
They're better together
The most robust integrations use both. A common, reliable pattern is the thin event: the webhook tells you that something happened, and you call the API to fetch the authoritative details.
order.shipped webhook arrives -> GET /orders/ord_123 to fetch full, current data
This avoids trusting a payload that might be stale or out of order, and keeps your webhook handler simple. It also neatly sidesteps the ordering problem: it doesn't matter which order the events arrive in if you always re-read current state.
A good default architecture:
- Receive the webhook and verify its signature.
- Acknowledge with a
2xximmediately — before doing any real work. - Enqueue the event ID for background processing.
- Fetch the authoritative record from the API in the worker.
- Deduplicate on the event ID so replays are safe.
Making webhooks reliable
Three rules cover most production incidents:
Respond 2xx fast. Providers time out aggressively — often in 5 to 10 seconds. Do your real work asynchronously. Returning 200 then processing in a queue is correct; processing inline and returning 200 forty seconds later is how you end up with duplicate deliveries.
Be idempotent. Store processed event IDs and skip repeats. This is the single highest-value thing you can do, because it makes retries — yours and theirs — safe.
Don't lose events during deploys. The gap where your endpoint is restarting is exactly when a provider will POST. Either buffer in front of your app with a webhook gateway, or use durable retries so failed deliveries keep trying rather than evaporating.
See retries and idempotency for the deeper treatment.
Securing a webhook endpoint
With an API, you authenticate outbound — you send a key. With a webhook it's reversed: you must prove the inbound request is genuine.
Almost every provider signs its webhooks with an HMAC over the raw request body, sent in a header like Stripe-Signature or X-Hub-Signature-256. Verification means recomputing that HMAC with your shared secret and comparing in constant time.
Two things trip people up:
- Verify against the raw body, not the re-serialized JSON.
JSON.parsethenJSON.stringifychanges the bytes and every signature will fail. - Check the timestamp where the provider includes one, so a captured request can't be replayed later.
You can check a signature against your secret in the browser with our webhook signature verifier — including provider-specific pages for Stripe, GitHub, Shopify and Slack. The full write-up is in how to verify a webhook signature and webhook security best practices.
The catch with webhooks: you need a reachable URL
The one thing a webhook needs that an API call doesn't is a public URL the provider can POST to — which is awkward when your code runs on localhost or behind a firewall. That's exactly what Webhook Relay solves: inspect the payload in your browser, then forward it to localhost or a private server with no public IP.
relay forward --bucket my-app http://localhost:3000/webhooks
Quick decision checklist
Answer these in order:
- Does the provider offer a webhook for this event? If no, poll — you're done.
- Do you need to know within seconds? If no, polling is simpler and has fewer failure modes.
- Can you keep an endpoint available and verify signatures? If not, use a gateway or a managed relay in front of your app.
- Does the payload contain everything you need? If not — or if ordering matters — use the thin event pattern and call the API on receipt.
Most production integrations end up at: webhook to learn, API to confirm.
Related reading
- What is a webhook — the full primer, with a Node.js example.
- Webhook vs WebSocket — push callbacks vs a persistent connection.
- Webhook architecture diagram — how the pieces fit end to end.
- How to test webhooks — inspect, forward to localhost, and replay.
- The State of Webhooks — live data on real webhook traffic.
