Shopify or WooCommerce Returning 429: Building a Sync That Doesn't Get Throttled
Back to blog
full stack·September 5, 2026·4 min read·By Yehonatan Saadia

Shopify or WooCommerce Returning 429: Building a Sync That Doesn't Get Throttled

Shopify's 429 carries a Retry-After header and a 40-request bucket; a WooCommerce 429 comes from the host, not from WooCommerce. How to read both and sync well.

Key takeaways

  • Shopify publishes its limits: a 40-request bucket with a 2-per-second leak rate on standard plans, and ten times that on Shopify Plus.
  • Every REST response carries `X-Shopify-Shop-Api-Call-Limit` in the form `32/40`, so you have a fuel gauge on every call.
  • GraphQL uses a completely different model: a points-per-second budget, and a single query may not exceed a cost of 1,000 points.
  • WooCommerce itself does not rate limit the REST API. A 429 from it belongs to the web server, the CDN or a security plugin.
  • A sync built on a loop with a fixed `sleep` breaks the moment anything else touches the same store.

Shopify's 429 is a documented mechanism: the REST Admin API uses a leaky bucket of 40 requests per app per store on standard plans, and a 429 response carries a Retry-After header telling you how many seconds to wait. WooCommerce is the opposite case - core does not rate limit the REST API, so a 429 there almost always comes from the host or a WAF.

Two different models on one platform

Shopify operates two separate limiting systems, and confusing them is the most common reason a developer "fixes" a 429 that keeps coming back.

REST Admin APIGraphQL Admin API
Unit measuredThe requestCalculated cost in points
Standard-plan budget40-request bucket100 points per second
Recovery rate2 requests per secondContinuous refill, per restoreRate
Where you see the stateX-Shopify-Shop-Api-Call-Limit headerextensions.cost.throttleStatus in the body
On exceeding it429 with Retry-AfterThe query is throttled and the budget is reported back
Additional ceilingNoneA single query capped at 1,000 points

The practical difference: on REST the request is the unit, so pulling 500 products one at a time drains the bucket even though each call is tiny. On GraphQL the cost is derived from how many fields and connections you asked for, so one over-broad query can blow the budget by itself.

Why does WooCommerce return 429 when WooCommerce has no rate limit?

Because the limit is not coming from WooCommerce. A WooCommerce store runs on WordPress, which runs on a web server, which usually sits behind a protection layer. Any of those can return a 429, and none of them knows anything about your store logic.

  • The host. Nginx or Apache limiting rules on wp-json paths and on admin-ajax.php.
  • A security plugin or WAF. Blocks applied to specific paths, sometimes with no Retry-After header at all.
  • The Store API. This is the one layer where WooCommerce does document a rate limit of its own - a default of 25 requests in a 10-second window, on POST requests only, and disabled by default. It returns RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset and RateLimit-Retry-After headers.

So diagnosis on WooCommerce starts from a different question entirely: not "how do I slow the sync down" but "who returned this 429". The answer is visible in the response body - a native WordPress error looks nothing like a WAF block page.

Reading the state instead of guessing it

  • On Shopify REST, read X-Shopify-Shop-Api-Call-Limit on every response. A value of 38/40 means two requests of headroom, and you can slow down before being throttled rather than after.
  • When a 429 arrives, honour Retry-After exactly as given. That is the number of seconds the platform itself is asking for, and it beats any formula you invent.
  • On GraphQL, read throttleStatus on every response: maximumAvailable, currentlyAvailable and restoreRate give you the whole budget picture.
  • Keep requestedQueryCost alongside actualQueryCost. A wide gap means you are requesting pages larger than what comes back.
  • On WooCommerce, log the status code together with the raw response body and every header. Without that you cannot separate a server block from a Store API limit.

How do you build a sync that survives a 429?

  1. Separate collection from processing. One process fetches and writes to a queue, another consumes it. A sync that does both in one loop stops entirely on the first 429.
  2. Give every queue item an attempt counter and a next-attempt time. Retry-After is written into that next-attempt time, not into a sleep that blocks the whole worker.
  3. Run a single worker per store. The Shopify bucket is per app per store, so two parallel workers split the same budget and hit the wall sooner.
  4. Use cursor-based pagination and persist the cursor. A sync that restarts from the beginning after every failure spends its budget re-fetching what it already had.
  5. Make writes idempotent. After a 429 you do not know whether the request was blocked before or after the update landed, and without a unique key per operation you create duplicates.
  6. Record the actual rate, not the intended one. Requests genuinely sent per minute is the only number you can tune against.

What actually breaks

  • A fixed sleep. Works in isolation, fails the moment a second app or a human in the admin draws on the same bucket.
  • Ignoring Retry-After. An immediate retry extends the throttle rather than shortening it.
  • Running in parallel "to speed it up". Doubles consumption, reaches 429 faster, and completes no more work.
  • One enormous GraphQL query. Fails on the 1,000-point ceiling rather than the per-second budget, so slowing down will never fix it.
  • Treating 429 as fatal. The process dies, somebody reruns it from the start, and the cycle closes.
  • Syncing everything every run. A full pull instead of changes since the last timestamp is the fastest way to exhaust a budget.

Diagnosing a stalled sync in five minutes

  1. Pull the full status code and every response header for the failed request out of your logs. Without the headers there is no diagnosis.
  2. On Shopify, look at X-Shopify-Shop-Api-Call-Limit on the requests immediately before the failure. If it was already near the ceiling, this is a pacing problem rather than a bug.
  3. Check whether Retry-After is present at all. Its presence indicates a declared platform limit; its absence suggests an infrastructure-layer block.
  4. Count how many requests actually went out in the minute before the failure. That number, not the intent expressed in the code, is what the platform measured.
  5. Check who else was working against the same store at that moment - another app, a second worker, or a human exporting data from the admin.
  6. On GraphQL, compare requestedQueryCost with actualQueryCost on the last successful calls. A high requested cost is the signal that your pages are too large.

The order matters, because the first four steps separate three problems that look identical from the outside: too high a rate, too expensive a query, and a block that has nothing to do with the store at all.

Related: What API rate limiting is, Shopify vs WooCommerce, Connecting an online store to an ERP.

Sources

#Shopify#WooCommerce#429 error#rate limits#inventory sync#שגיאות

Frequently asked questions

How many requests am I allowed on Shopify?

On the REST Admin API, standard plans get a bucket of 40 requests per app per store, refilling at 2 requests per second. Shopify Plus values are ten times higher. GraphQL is measured in points rather than requests, so the number depends entirely on the query you send.

Does Shopify return a Retry-After header?

Yes. A 429 from the REST Admin API is returned together with a `Retry-After` header containing the number of seconds to wait. That is the value to honour, rather than a schedule you decided in advance. On GraphQL the equivalent information arrives inside `extensions.cost.throttleStatus`.

I got a 429 from WooCommerce. What do I check first?

Who returned it. WooCommerce core does not rate limit the REST API, so check the web-server logs, the WAF or CDN rules, and any security plugins. If the block is on a checkout path, also check whether the Store API rate limiting feature has been enabled.

Can the limit be raised?

On Shopify the limit follows the store's plan tier and is not adjustable per app. On WooCommerce, because the limit comes from infrastructure, the hosting provider is the place to ask. In both cases, reducing the number of requests is a better investment than trying to enlarge the budget.

Keep reading

Related service

Inventory & Purchasing

SKU-level stock, reorder rules and an approval flow that leaves a record.

Learn more →←

About the author

Yehonatan Saadia

Freelance automation, web & MVP developer

I'm Yehonatan Saadia, a senior developer who builds business automation, custom websites, and MVPs for small and mid-sized companies across the US, Europe, and Israel. These guides come from real client work, not theory.

Work with me

Have a project like this?

Tell me what you're trying to automate or build. I reply within 24 business hours with a few targeted questions, then we walk through it on a free 30-minute call, with no commitment. You come away with a scope, a timeline and a fixed price - or a straight answer that it isn't worth building.