✨ Strapi MCP is now Generally Available - let your agents manage your Strapi content ✨

Ecosystem●12 min read

Webhooks vs. APIs: Key Differences and When to Use Each

●May 28, 2025●Updated on September 29, 2026
Webhooks vs APIs

Webhooks and Application Programming Interfaces (APIs) both move data between systems, but they start from opposite ends. A webhook pushes data to you the moment an event happens; an API waits for you to pull data when you ask for it. The shorthand holds up well in practice: you call an API, a webhook calls you.

In brief

  • Webhooks are Hypertext Transfer Protocol (HTTP) callbacks that push event data to your endpoint in near real time, which suits payments and content sync. They also suit continuous integration and continuous delivery (CI/CD).
  • APIs follow a request-response model and commonly authenticate through OAuth 2.0. JWTs and API tokens are also available. APIs support reads and writes, as well as complex queries.
  • Webhook security is your responsibility as the receiver: Hash-based Message Authentication Code with SHA-256 (HMAC-SHA256) signature verification and timing-safe comparison are the baseline. Timestamp checks are required when the provider includes a signed timestamp or otherwise supports freshness validation.
  • Many integrations combine both: webhooks detect change, and API calls confirm state. Low-frequency polling catches missed deliveries.

What is a webhook?

A webhook is a user-defined HTTP callback: when a specific event occurs in one system, that system sends an HTTP POST request to a Uniform Resource Locator (URL) you registered. The CloudEvents spec states that the HTTP method for a webhook delivery request must be POST. So is a webhook an API? Technically it's an API endpoint you expose and the provider calls, which inverts the usual direction of a request.

The lifecycle has four steps:

  1. Register your endpoint URL with the webhook provider.
  2. Define trigger events, such as a payment confirmation or a content update.
  3. Receive the POST request automatically when the event fires.
  4. Process the payload, for example by updating a database or queuing a job.

A typical payload carries an event type and a timestamp. It also contains a data object:

{
  "event": "payment.completed",
  "timestamp": "2026-01-11T10:30:00Z",
  "data": {
    "payment_id": "pay_123456",
    "amount": 99.99,
    "currency": "USD",
    "customer_id": "cus_789012"
  }
}

The specifics vary by provider. Stripe uses dot-separated event names and signs each delivery, while GitHub delivers JavaScript Object Notation (JSON) payloads capped at 25 MB, according to its payload documentation, with headers like X-GitHub-Event and X-GitHub-Delivery.

Common use cases follow the same shape: an event on one side, an immediate reaction on the other. Payment processors confirm transactions, GitHub pushes kick off CI builds, e-commerce sales update inventory across channels, and content changes in a content management system (CMS) trigger frontend rebuilds through frontend deploy hooks or n8n automation workflows.

What is an API?

An Application Programming Interface (API) is a structured gateway that lets a client request data or operations from a server. The client initiates every interaction: it sends a request to an API endpoint, the server processes it and returns a response, usually JSON. Unlike webhooks, APIs are bidirectional. APIs support reads and writes. Clients can also chain requests and filter exactly what they fetch.

The three main API styles serve different contexts:

  • Representational State Transfer (REST): standard HTTP methods for create, read, update, and delete (CRUD) operations; the default choice for most API-first architectures.
  • GraphQL: clients query only the fields they need, which helps complex frontends; see the REST vs. GraphQL comparison for guidance on when to pick each in Strapi 5.
  • gRPC: Protocol Buffers over HTTP/2 for service-to-service calls. Microsoft's comparison notes that a gRPC message is always smaller than an equivalent JSON message, but browsers cannot call gRPC services directly, so it stays internal. CNCF gRPC guidance suggests exposing REST at the edge and using gRPC between internal services.

These styles all share the same direction of control: the client decides when to call.

Across these styles, APIs benefit from mature authentication standards. The Request for Comments (RFC) standard OAuth 2.0 (RFC 6749) covers delegated access, with RFC 9700 as the current security best practice. OAuth 2.1 consolidates these but is still an Internet Engineering Task Force (IETF) draft, not a ratified standard. JWTs (RFC 7519) handle stateless claims. Applications can instead be identified with API keys, though OWASP auth guidance from the OWASP API Security Top 10 warns that API keys should only be used for API client authentication, not user authentication. Understanding API authorization depends on this distinction.

Webhook vs. API: core differences at a glance

DimensionWebhooksAPIs
Communication modelPush-basedPull-based
TriggerEvent-driven (automatic)Request-driven (client-initiated)
DirectionOne-way (provider to your endpoint)Bidirectional (request and response)
Data freshnessNear real time; latency varies by provider and receiverDepends on when the client asks
Resource costFires only on eventsPolling can produce empty responses when changes are infrequent
Error handlingAsynchronous; retries or redelivery are provider-dependentImmediate HTTP status codes
Security modelHMAC signature verificationOAuth, JWT, API tokens
Best forNotifications, sync, automationCRUD, complex queries, writes

When to use webhooks vs. APIs

Latency and traffic influence the decision. Failure handling also affects the design.

Latency: push beats polling

With polling, your worst-case detection delay equals your polling interval; a 30-second poll can miss an event for 30 seconds. Push delivery closes that gap. Amazon Web Services (AWS) documents typical Amazon Simple Notification Service (SNS) delivery latency under 30 milliseconds.

Cloud providers have moved their own guidance in the push direction. Google Cloud's unary pull guidance states that unary pull does not guarantee low latency without multiple simultaneous outstanding requests. Microsoft documents up to a 10-minute delay in its blob trigger guidance for polling-based Azure Functions blob triggers on the Consumption plan and recommends the Event Grid implementation because it has lower latency.

Polling still wins when your receiver sits behind Network Address Translation (NAT) and cannot expose a public Hypertext Transfer Protocol Secure (HTTPS) endpoint, or for one-off or infrequent queries. It also works when you need to control exactly what data you retrieve and when.

Traffic and rate limits

A webhook system delivers only the meaningful events.

Providers enforce this preference through rate limits. GitHub's webhook guidance is direct: subscribe to webhook events instead of polling the API for data, because polling can push your integration past the API rate limit. Stripe caps live-mode API traffic at 100 requests per second (rate limits docs) and discourages polling payment status because it is less reliable and can trigger those limits.

Reliability: retries, idempotency, and dead-letter queues

Webhook delivery depends on your server being up and answering fast. Providers compensate with exponential backoff, which the AWS Builders' Library describes as increasing the wait time exponentially after every attempt, ideally with jitter to spread out retry bursts. Retry policies differ sharply by provider:

ProviderRetry policySource
Stripe (live mode)Exponential backoff for up to three daysStripe webhooks docs
PayPalUp to 25 attempts over three daysPayPal webhooks docs
ShopifyEight retries over four hours; subscription deleted after eight consecutive failures (Admin API)Shopify changelog
AWS SNS (HTTP/S customer-managed endpoints)50 attempts over approximately six hoursSNS delivery retries
GitHubNo automatic retry; manual redelivery within three daysHandling failed deliveries

Two receiver-side patterns matter once retries exist. First, dead-letter queues (DLQs) capture events that exhaust all retries; without one, SNS discards the message permanently, according to SNS dead-letter guidance, and note the DLQ attaches at the subscription level, not the topic. Second, idempotency: retries mean the same event can arrive twice, so your handler must produce the same result either way. The Idempotent Receiver pattern handles this by storing processed event IDs and skipping duplicates, which is exactly what Stripe's deduplication guidance prescribes: log the IDs you've handled and return a 2XX for repeats.

Acknowledgement deadlines are tight and vary. Shopify expects a 200 OK within five seconds; GitHub's 10-second timeout marks a delivery failed if the server responds too slowly. Acknowledge first, then process asynchronously via a queue.

Where WebSockets and server-sent events fit

Webhooks and APIs cover server-to-server integration; two other patterns cover the server-to-browser gap.

WebSockets (RFC 6455) provide a persistent, full-duplex channel over a single Transmission Control Protocol (TCP) connection where each side can independently send data at will. They fit live chat and collaborative editing. Dashboards can also use them when both ends push data continuously. Connection management lands on you: you must handle reconnection logic and state, then scale the long-lived connections.

Server-Sent Events (SSE) are the lighter one-way option: the server streams text/event-stream responses to the browser's EventSource API, with automatic reconnection and last-event-ID tracking built into the spec. One constraint: under HTTP/1.1, browsers cap connections at six per domain, which bites when several tabs each open a stream. HTTP/2 removes the practical ceiling; RFC 9113 recommends servers allow at least 100 concurrent streams, with the server setting its own limit. SSE has become the default transport for large language model (LLM) streaming: OpenAI's streaming docs explain that OpenAI streams tokens as server-sent events, with a separate WebSocket mode available for tool-heavy Responses API workflows.

Webhook security best practices

Your webhook endpoint is a public URL that accepts POSTs from the internet, and unlike API auth, there is no OAuth-style standard: each provider defines its own headers, encodings, and verification quirks. That makes verification your job as the receiver. For a broader take on securing your content layer, the headless CMS security guide covers the wider picture.

Verify HMAC signatures with a timing-safe comparison

GitHub, Stripe, and Shopify all use HMAC-SHA256 with a shared secret, but their signed inputs, headers, and encodings differ. GitHub signs the raw request body and sends a sha256=-prefixed value in X-Hub-Signature-256, as shown in its signature validation guidance. Shopify sends a Base64-encoded signature in X-Shopify-Hmac-SHA256, according to its delivery verification guidance. Stripe's signature documentation specifies Stripe-Signature and signs the timestamp, a period, and the raw request body.

A GitHub-style receiver recomputes and compares the signature like this:

received  = request.headers["X-Hub-Signature-256"]
expected  = "sha256=" + HMAC_SHA256(key=shared_secret, message=raw_request_body)

if not timing_safe_compare(received, expected):
    return 403

Two implementation details commonly break webhook integrations:

  • The raw request body matters. Stripe requires the body string in UTF-8 encoding without any changes; framework middleware that re-parses or re-serializes JSON will invalidate the signature.
  • A constant-time comparison is necessary. GitHub's docs warn against using a plain == operator. Use crypto.timingSafeEqual() in Node.js or hmac.compare_digest() in Python. CVE-2022-43412 demonstrates that this timing flaw has caused a documented vulnerability in the Jenkins Generic Webhook Trigger Plugin, as detailed in the Jenkins vulnerability record, catalogued as Common Weakness Enumeration (CWE) entry CWE-208 in timing discrepancy guidance.

These two points apply to any webhook integration, not just these three providers.

Harden the endpoint beyond signatures

Four additional controls help harden a webhook endpoint beyond signature verification:

  • Timestamp validation: Stripe signs a timestamp into each delivery and recommends a five-minute tolerance to block replay attacks; setting the tolerance to zero disables the recency check entirely. You need to keep your server clock synchronized through the Network Time Protocol (NTP).
  • HTTPS only: Stripe accepts only Transport Layer Security (TLS) 1.2 and 1.3 endpoints, and the OWASP TLS Cheat Sheet advises API-only endpoints to disable HTTP altogether.
  • Internet Protocol (IP) allowlisting: Stripe publishes 15 IPv4 addresses in its IP address docs for webhook traffic; GitHub exposes its delivery IPs in the hooks element of the /meta API endpoint and notes they change, so consider refreshing the list periodically.
  • Rate limiting: cap inbound requests per second so a flood of forged deliveries cannot exhaust your receiver, the risk OWASP labels Unrestricted Resource Consumption.

These controls layer on top of HMAC verification rather than replacing it.

How webhooks and APIs work together in production

Many production integrations use both. Two common patterns emerge.

Thin webhook, full API fetch

A webhook tells you something changed; an API call tells you the current truth. Stripe's flow is the canonical example: an invoice.paid event arrives, your server returns a 2xx before running any complex logic, then asynchronously fetches the full invoice from the API and updates your database. Stripe's event-ordering policy states that it does not guarantee the delivery of events in the order that they are generated, so treating the webhook as a trigger and the API as the source of truth makes your handlers correct regardless of arrival order. It also keeps payloads thin, which is the direction composable architectures generally push toward.

API polling as the reconciliation backstop

Every retry window in the table above eventually closes, and events that exhaust it are gone unless you go looking. Shopify's webhook docs say this plainly: your app should not rely on receiving data from Shopify webhooks because webhook delivery is not always guaranteed. Shopify recommends reconciliation jobs that periodically fetch records filtered by updated_at. Stripe offers the same escape hatch through its List Events API, which retains events for up to 30 days and can filter on delivery_success=false to backfill exactly what you missed. Consider running the backstop at low frequency; it's a safety net, not a primary channel.

Webhooks and APIs in Strapi 5

Strapi 5 ships both halves of the hybrid pattern, with some plan and plugin boundaries worth knowing before you design around them.

Configure Strapi 5 webhooks

Webhook configuration is managed in the Admin Panel under Settings > Webhooks or in ./config/server.js. Webhooks fire on content events: entry.create, entry.update, entry.delete, entry.publish, and entry.unpublish (when draft and publish is turned on), plus media.create, media.update, and media.delete. For more on how content moves through these stages, the content lifecycle guide covers the broader workflow.

Two webhook events are plan-gated: releases.publish requires the Growth or Enterprise plan, and review-workflows.updateEntryStage is Enterprise-only, per Strapi's pricing.

Every delivery includes an X-Strapi-Event header naming the trigger. Private fields are excluded from payloads, and webhooks do not fire for the User Content-Type; use lifecycle hooks for that. The webhook documentation recommends HMAC-SHA256 verification with constant-time comparison and timestamp checks, the same pattern described above.

You can also set default headers for every delivery:

// ./config/server.js
module.exports = {
  webhooks: {
    defaultHeaders: {
      Authorization: "Bearer my-very-secured-token",
    },
  },
};

Use Strapi 5 REST and GraphQL APIs

Strapi 5 APIs split by transport. The REST API is on by default and generates endpoints per Content-Type with a flattened Strapi 5 response format. Attributes appear at the first level, and documents are addressed by documentId. All Content-Types are private by default, and relations must be explicitly requested with populate.

The GraphQL API requires installing the @strapi/plugin-graphql plugin. It exposes only documentId and currently lacks support for media upload or aggregations. For a deeper look at querying patterns, the Strapi GraphQL comparison covers when each transport fits.

For machine-to-machine access, API tokens are free and available by default on every plan, with read-only, full-access, or custom options. The Users and Permissions feature is also free and available by default. It provides JWT authentication and access control for end users. The Strapi auth guide walks through JWTs, tokens, and role-based access control (RBAC) together.

Your frontend build or sync job can fetch authoritative state through REST or GraphQL, while a scheduled reconciliation query covers missed deliveries.

To try the pattern hands-on with Strapi 5, the webhook tutorial is a practical starting point, and the webhook docs cover the configuration details. If you want to see how Strapi's API layer works with a real frontend, the Launchpad starter gives you a working project in minutes.

Paul BratslavskyDeveloper Advocate

Related Posts

Adding Webhooks in Strapi 🎯
Webhooks·3 min read

Adding Webhooks in Strapi 🎯

Webhooks are already implemented on your favorite headless CMS by now.

·October 31, 2018
18 API Project Ideas
Ecosystem·14 min read

18 API Project Ideas to Boost Your Developer Portfolio

Explore beginner to advanced API project ideas with real APIs, code examples, and skills each project builds. Level up your developer portfolio in 2026.

·April 22, 2026
6 min read

Implementing Webhooks in Strapi for Real-time Notifications

Learn how to set up webhooks in Strapi for effective real-time notifications. This guide provides a straightforward approach to keeping users informed insta...

·March 6, 2024