Shopify webhook retry policy: 8 retries, 4 hours, then dropped
Shopify's current webhook retry policy was set on September 10, 2024: up to 8 retries over a 4-hour window using exponential backoff. After that, the event is dropped and the subscription may be removed.
The current policy at a glance
| Aspect | Value |
|---|---|
| Max retries per event | 8 |
| Total retry window | 4 hours |
| Backoff strategy | Exponential |
| Timeout per attempt | 5 seconds |
| Success status codes | 2xx (any) |
| Failure conditions | Non-2xx, timeout, TLS error, DNS failure, connection refused |
| Subscription auto-removal | After persistent failure across multiple events |
| Notification on removal | Email to Partner emergency developer email |
| Recovery path after drop | Manual reconciliation via Admin API |
| Effective date of current policy | September 10, 2024 |
Sourced directly from Shopify's docs
"Shopify retries failed webhook calls up to eight times in a four-hour period." — Shopify dev docs · Troubleshooting webhooks
"If your app doesn't respond within five seconds, then the delivery fails." — Shopify dev docs · Troubleshooting webhooks
"Webhooks will now be retried a total of 8 times over 4 hours using an exponential backoff schedule." — Shopify dev changelog · September 10, 2024
What this means in practice
The retry curve is front-loaded
Exponential backoff over 8 attempts in 4 hours puts most attempts in the first 30 minutes. The remaining attempts are spread thin across the back half of the window. By the time hour 2 rolls around, you've burned about 5 of 8 attempts. Outages longer than 2 hours have a low probability of catching the remaining retries.
The 5-second timeout is the most common failure mode
Shopify treats anything slower than 5 seconds as a failure regardless of the eventual response. That makes synchronous processing dangerous. If your handler does anything that occasionally crosses the threshold — a third-party API call, a slow DB write, an image resize — you'll see intermittent failures that look mysterious in logs because the work eventually completed. The fix is universal: read the raw body, persist it to a queue, return 200, then process async.
Subscription removal compounds the loss
The 8/4-hour rule is per event. If your endpoint is consistently failing across many events (e.g., every webhook returns 401 because of a botched HMAC secret rotation), the subscription itself gets removed. After that, future events stop firing — they aren't queued, they're not sent. Re-registration via the Admin API is required to resume delivery, and any data created during the gap has to be reconciled from the Admin API.
Compared to the previous policy
Before September 10, 2024, Shopify documented a longer retry window — closer to 19 attempts over 48 hours. The 2024 update compressed the curve dramatically.
| Pre-Sept 10, 2024 | Current | |
|---|---|---|
| Retries per event | ~19 | 8 |
| Total window | ~48 hours | 4 hours |
| Tolerable outage | Up to ~2 days | ~4 hours max |
If you wrote your webhook reliability code before late 2024, the timing assumptions baked into your retry strategy may no longer be safe. Outages of 5+ hours that used to be recoverable through Shopify's curve are now permanent losses without a reconciliation layer.
A quirk worth knowing: API version fall-forward
Webhook payloads are versioned the same way as Admin API responses. Shopify releases a new API version every 3 months and supports each for at least 12 months, with 9 months of overlap between consecutive versions. If you subscribe a webhook with api_version = "2024-04" and that version becomes unsupported, Shopify falls forward to the oldest supported stable version (e.g., "2025-01"). The webhook still arrives, but the payload shape may have changed.
The signal is the X-Shopify-Api-Version response header in each webhook. Compare it to the version you intended to subscribe — any mismatch means you're on a fall-forward and your parser may quietly miss new fields or break on renamed ones.
Failure conditions in detail
- 2xx — Success. Shopify considers the delivery complete and won't retry.
- 3xx — Counted as failure. Shopify does not follow redirects.
- 4xx — Failure. The most common in webhook contexts is 401 (HMAC verification failed). Every retry hits the same 401 unless your secret config changes.
- 5xx — Failure. Generally retryable from Shopify's perspective since 5xx implies temporary upstream issues.
- No response within 5 seconds — Timeout failure. Counted same as a 5xx.
- TLS / connection errors — Failure. Cert expiry, hostname mismatch on a wildcard cert, connection refused.
- DNS resolution failure — Failure. Less common but happens during DNS provider incidents.
What production-grade webhook handling looks like
Given the policy's tight constraints, the production pattern is roughly the same across every Shopify app that ships reliably:
- Respond 200 in under 100ms. The only synchronous work in the request lifecycle is HMAC verification and raw-body persistence. Everything else moves to a background job.
- Add your own retry layer. Sidekiq, BullMQ, AWS SQS, anything with exponential backoff over days, not hours.
- Reconcile against the Admin API. Hourly for high-value topics like
orders/*. The only safety net for events Shopify dropped or never fired. - Monitor subscription health. Daily cron calling
webhookSubscriptions. Alert your engineers on any missing topic. - Use
X-Shopify-Event-Idfor deduplication. The same value persists across retries. - Watch
X-Shopify-Api-Version. Detect fall-forward before your parser breaks.
Where HookRescue fits
HookRescue implements all of the above between Shopify and your existing handler. We extend the retry curve to 7 days, reconcile hourly against the Admin API, and re-register subscriptions before they get auto-removed. Your existing webhook handler doesn't change — we re-sign every payload with your client secret so HMAC verification keeps working exactly the same way.