Shopify API rate limit: the REST leaky bucket and GraphQL calculated cost

By Sazabi Team

Failure facts checked on the vendor’s docs on Nov 8, 2026.

Shopify does not have one rate limit. Each API counts differently, and the same store can be throttled on one API and fine on another. The Admin REST API counts requests. The GraphQL Admin API counts how expensive each query is. Reading the wrong counter is the usual reason an app looks well under its limit and still fails.

The leaky bucket

Most Shopify limits use a leaky bucket. Each call drops marbles in a bucket, and the bucket empties at a fixed restore rate. Bursts are fine while the average stays under the restore rate, and a full bucket means a throttle error until it drains. Limits are set per app and store pair, so two apps on one store do not compete and one app serving many stores gets a bucket for each.

Shopify also says it may temporarily lower limits to protect the platform, so a limit that worked last week is not a guarantee.

REST Admin API

REST is a legacy API as of October 1, 2024, but many integrations still call it. The default bucket is 40 requests per app per store, leaking at 2 requests per second. Shopify Plus stores get ten times that: 400 and 20. Advanced Shopify stores leak at 4 requests per second, and enterprise (Commerce Components) at 40.

Every response has X-Shopify-Shop-Api-Call-Limit, for example 32/40: 32 requests are in the bucket and the size is 40. The docs give an example of 39/40 falling to 19/40 after ten seconds. When you go over, you get a 429 Too Many Requests with Retry-After in seconds, and anything sent before then is throttled too.

Two quieter 429 sources exist. Page-based requests with an offset above 100,000 (a page of 401 at limit=250) get a 429. And once a store passes 500,000 product variants, creating more than 10,000 per day is throttled on every API unless the store is on Plus.

GraphQL Admin API

GraphQL charges by calculated cost: scalars and enums cost 0, objects 1, connections scale with first and last, and mutations cost 10. Restore rates are 100 points per second on Standard, 200 on Advanced Shopify, 1,000 on Plus and 2,000 on enterprise. One query cannot exceed 1,000 points, checked before it runs.

Shopify checks both a requested cost, which must fit in your bucket before execution starts, and an actual cost, with the difference refunded afterward. A query that asks for first: 250 on a nested connection can be refused at the 1,000 point limit even if the store has few records, because the requested cost is what counts before it runs.

Each response carries a throttleStatus under extensions.cost. Send Shopify-GraphQL-Cost-Debug=1 to get a per-field breakdown of where the cost came from.

import json, logging, time
import requests

log = logging.getLogger("shopify")

def graphql(shop, token, query, variables=None):
    t0 = time.time()
    r = requests.post(
        f"https://{shop}/admin/api/2025-01/graphql.json",
        headers={"X-Shopify-Access-Token": token},
        json={"query": query, "variables": variables or {}},
        timeout=30,
    )
    try:
        body = r.json() if r.content else {}
    except ValueError:  # an HTML error page from a proxy
        body = {}
    cost = (body.get("extensions") or {}).get("cost") or {}
    ts = cost.get("throttleStatus") or {}
    log.info(json.dumps({
        "msg": "vendor call", "vendor": "shopify", "shop": shop, "status": r.status_code,
        "requested_cost": cost.get("requestedQueryCost"),
        "actual_cost": cost.get("actualQueryCost"),
        "available": ts.get("currentlyAvailable"), "max": ts.get("maximumAvailable"),
        "restore_rate": ts.get("restoreRate"),
        "retry_after": r.headers.get("Retry-After"),
        "duration_ms": int((time.time() - t0) * 1000),
    }))
    return body

What to alert on

  • available / max from the GraphQL throttle status. A bucket that regularly dips under 20% means one more job could trigger throttling.
  • For REST, the first number in X-Shopify-Shop-Api-Call-Limit as a share of the second.
  • The share of calls that get a 429, grouped by shop. One large merchant throttling while the rest are healthy points at that merchant's data size, not at Shopify.
  • Requested cost against actual cost for each query shape. A wide gap means you ask for far more than you use, which wastes bucket capacity.
  • Queue depth on the worker that calls Shopify, because throttling shows up as growing lag.

Your bug or their limit

A single shop failing and others fine usually points to a big catalog or a hot loop in your code for that shop. Every shop failing at once, with low bucket use on your side, suggests Shopify lowered limits or has a platform problem, so check Shopify's status page and respect Retry-After. Shopify's own advice: catch errors, stop sending until time has passed, and use a one second backoff as the baseline.

Example: a sync with an oversized page size

This is an example, not output from a real project. A made-up catalog-sync job reads products 250 at a time with nested variants.

03:15:02Z msg="vendor call" vendor=shopify shop=example-store requested_cost=900 actual_cost=312 available=1000 max=1000 status=200
03:15:03Z msg="vendor call" vendor=shopify shop=example-store requested_cost=900 actual_cost=298 available=688 max=1000 status=200
03:15:03Z msg="vendor call" vendor=shopify shop=example-store requested_cost=900 actual_cost=0 available=502 max=1000 outcome=throttled

The first queries refund most of what they reserve, but a 900-point request cannot start with 502 available. The outcome field is the example app's own label for a throttled response. A smaller page, or a bulk operation, would keep the bucket healthier.

Checklist

  1. Record the bucket state on every response, from the REST header or extensions.cost.
  2. Slow down before the bucket is empty, not after a 429.
  3. Respect Retry-After, with at least one second of backoff.
  4. Keep pages small and trim nested connections to cut requested cost.
  5. Use bulk operations for large reads and writes.
  6. Handle 429s on all endpoints, not only the ones Shopify lists.

Related: third-party dependency monitoring, capacity risks and vendor degradation. Sazabi can watch these logs once you send them.

Shopify rate limits facts

  • REST Admin: leaky bucket per app and store: The default bucket holds 40 requests and leaks at 2 per second, ten times larger and faster for Shopify Plus stores. (Shopify docs, accessed Nov 8, 2026)
  • REST header shows the bucket: X-Shopify-Shop-Api-Call-Limit returns the current request count and bucket size, such as 32/40, and a 429 adds Retry-After in seconds. (Shopify docs, accessed Nov 8, 2026)
  • GraphQL Admin limits by calculated cost: The GraphQL Admin API charges points per query, with a restore rate of 100 points per second on Standard plans up to 2,000 for enterprise. (Shopify docs, accessed Nov 8, 2026)
  • Throttle state in every GraphQL response: The extensions.cost object reports requestedQueryCost, actualQueryCost and throttleStatus with maximumAvailable, currentlyAvailable and restoreRate. (Shopify docs, accessed Nov 8, 2026)
  • Variant creation throttle: Once a store has 500,000 product variants, no more than 10,000 new variants can be created per day on any API, except on Shopify Plus. (Shopify docs, accessed Nov 8, 2026)

With Sazabi

The Sazabi agent automatically sets up sensors for your project and maintains them over time. When your code calls Shopify, it watches a question like this one:

Tell me when the share of Shopify API calls that get a 429 is well above its usual level, per store.

When the answer turns abnormal, Sazabi investigates and opens an issue. Vendor rate limiting covers the signal behind it.

Frequently Asked Questions

What is the Shopify API rate limit?

It depends on the API. The REST Admin API uses a 40-request bucket that leaks at 2 requests per second on standard stores. The GraphQL Admin API uses calculated query cost, from 100 points per second on Standard to 1,000 on Plus.

Are limits shared across apps?

No. Shopify bases Admin API limits on the combination of app and store, so one app does not affect another on the same store, and one store does not affect another for the same app.

What does a Shopify 429 tell me to do?

On the REST Admin API the response includes Retry-After, the number of seconds to wait, and requests before then are throttled. Shopify also recommends a backoff of one second as a baseline for retries.

How do I avoid limits for large exports?

Shopify says bulk operations do not have the max cost limits or rate limits that single GraphQL queries have, and recommends them for large amounts of data.

Sources

  1. REST bucket size, leak rate, X-Shopify-Shop-Api-Call-Limit, Retry-After and the page offset 429 (accessed Nov 8, 2026)
  2. Cost calculation, plan restore rates, the 1,000 point single query limit, extensions.cost and bulk operations (accessed Nov 8, 2026)
  3. The leaky bucket model, avoiding rate limit errors, the one second backoff and the 250 item input limit (accessed Nov 8, 2026)