Notes
Shopify API rate limits and what they mean for an ERP sync
Shopify gives every app a budget per store: 100 cost points per second on the GraphQL Admin API for a standard plan, 1,000 on Shopify Plus. For an ERP sync that budget decides whether a stock update for 50,000 variants takes 20 seconds or 83 minutes, and the difference is in how the calls are built.
Updated
How Shopify limits an integration
Shopify limits each app per store, and the method differs per API. An ERP sync uses the Admin API, which exists in two forms.
The GraphQL Admin API counts cost, not requests. Every query has a cost in points, calculated from the fields it asks for. The app has a bucket of points that refills at a fixed rate per second. A cheap query takes little from the bucket, an expensive one takes a lot.
The REST Admin API counts requests. Every request counts as one, whatever it returns. Shopify has marked the REST Admin API as legacy since October 1, 2024, and directs new integrations to GraphQL.
Both use a leaky bucket. The bucket has a size and empties at a fixed rate. A burst above the rate succeeds as long as the bucket has room, so 40 REST requests in one second pass on a standard plan and the 41st is refused. Over a longer run only the rate counts.
The budget belongs to one app on one store. A second app on the same store has its own bucket.
The numbers per plan
These figures were read from Shopify's developer documentation on October 3, 2026: the GraphQL Admin API rate limits and the REST Admin API rate limits.
| Plan | GraphQL Admin API | REST Admin API | REST bucket |
|---|---|---|---|
| Standard | 100 points/second | 2 requests/second | 40 requests |
| Advanced Shopify | 200 points/second | 4 requests/second | not published |
| Shopify Plus | 1,000 points/second | 20 requests/second | 400 requests |
| Shopify for enterprise | 2,000 points/second | 40 requests/second | not published |
Shopify publishes the GraphQL restore rate per plan and not the bucket size. The bucket size for your store is in every response, in the field maximumAvailable.
The limits that are the same on every plan:
| Limit | Value |
|---|---|
| Cost of a single GraphQL query | 1,000 points at most |
| Items in an input array | 250 at most |
| Objects per page | 250 at most |
| Pagination depth | 25,000 objects |
| Bulk operations at the same time, per app and shop | 5 of each type from API version 2026-01, 1 before |
| Bulk query run time | 10 days |
| Bulk mutation run time | 24 hours |
| Bulk mutation input file (JSONL) | 100 MB |
| New variants per day once a store has 500,000 variants | 10,000 (not on Plus) |
Shopify states that it can lower the rate limits for a short time to protect the platform. An integration that runs at exactly the published rate is throttled on those days.
How the cost of a query is calculated
Shopify assigns a cost to every field. The defaults:
| Field returns | Cost |
|---|---|
| Scalar or enum (a SKU, a price, a status) | 0 |
| Object (a variant, an inventory item) | 1 |
| Connection (a list of objects) | Sized by the first or last argument |
| Mutation | 10 |
By these defaults a page of 250 variants is requested at about 250 points, and more when each variant carries nested objects such as its inventory item and its inventory levels. A query that asks for 250 products with 100 variants each requests 25,000 objects. That is far above the 1,000-point ceiling of a single query, and Shopify refuses it before it runs.
Shopify calculates the cost twice. Before execution it reserves the requested cost. After execution it refunds the difference between the requested and the actual cost. A page that asks for 250 variants and returns 30 is charged for what it returned. The response reports this under extensions.cost:
| Field | Meaning |
|---|---|
requestedQueryCost |
The cost reserved before the query ran |
actualQueryCost |
The cost charged after the refund |
throttleStatus.maximumAvailable |
The bucket size for this app and store |
throttleStatus.currentlyAvailable |
The points left at this moment |
throttleStatus.restoreRate |
The points added back per second |
An integration that reads currentlyAvailable after every call and waits when the next call does not fit rarely sees a throttle error. One that sends calls on a fixed timer finds out from the error.
What throttling looks like
The GraphQL Admin API does not use status 429 for its rate limit. It returns 200 OK with the error in the body:
{
"errors": [
{
"message": "Throttled",
"extensions": {
"code": "THROTTLED"
}
}
]
}
An integration that only checks the HTTP status treats this as a success. The stock update it carried is not applied and nothing is logged. The REST Admin API does return a 429, with two headers:
HTTP/1.1 429 Too Many Requests
X-Shopify-Shop-Api-Call-Limit: 40/40
Retry-After: 2.0
40/40 means the bucket is full. Retry-After is the number of seconds to wait; a request sent earlier is throttled again. Shopify recommends a backoff of one second. How retries with growing pauses and a capped number of attempts work across systems is in API throttling, 429s and backoff.
A stock update for 50,000 variants
Take a catalog of 50,000 variants on a standard plan. The ERP has new stock figures for all of them. Three ways to write them to Shopify:
| Method | Calls | Budget used | Duration |
|---|---|---|---|
| REST, one variant per request | 50,000 | 2 requests/second | 25,000 s, 6 h 57 min |
| GraphQL, one variant per mutation | 50,000 | 500,000 points at 100/second | 5,000 s, 83 min |
| GraphQL, 250 variants per mutation | 200 | 2,000 points at 100/second | 20 s |
The sums: 50,000 divided by 2 requests a second is 25,000 seconds. 50,000 mutations at the default cost of 10 points are 500,000 points, and 500,000 divided by 100 is 5,000 seconds. With 250 variants in one inventorySetQuantities call, 50,000 variants are 200 calls and 2,000 points. The cost of 10 is Shopify's default for a mutation; the actual cost of each call is in the response.
On Plus the middle row drops from 83 minutes to 500 seconds, a little over 8 minutes. The third row is 2 seconds of budget. The plan changes the result by a factor of 10; the batch size changes it by a factor of 250.
The variables of one batched call look like this, with two of the 250 entries shown:
{
"input": {
"name": "available",
"reason": "correction",
"quantities": [
{ "inventoryItemId": "gid://shopify/InventoryItem/30322695",
"locationId": "gid://shopify/Location/124656943",
"quantity": 14, "changeFromQuantity": 20 },
{ "inventoryItemId": "gid://shopify/InventoryItem/30322696",
"locationId": "gid://shopify/Location/124656943",
"quantity": 0, "changeFromQuantity": 3 }
]
},
"idempotencyKey": "0b1e6c1c-52c7-4a7e-9d39-6f1d6e2f8a10"
}
changeFromQuantity makes the write conditional: Shopify sets the stock to 14 only when it is still 20. When an order came in during the sync and the stock is now 19, that entry fails with CHANGE_FROM_QUANTITY_STALE and is recalculated. From API version 2026-04 the mutation requires an idempotency key, so a call that is repeated after a timeout is applied once.
Reading 50,000 variants
Reading is limited in a different way. At 250 objects per page a full read is 200 pages, and pagination stops at 25,000 objects. Shopify's route for a full read is a bulk operation: one bulkOperationRunQuery mutation starts it, Shopify runs the query on its own side outside the rate limit, and the result is a JSONL file with one object per line. The download link is valid for one week. A bulk query can hold five connections at most, nested two levels deep.
A sync that reads the whole catalog page by page every 15 minutes and compares it field by field spends most of each cycle reading data that did not change. The pattern that holds at this size has three parts: a bulk read once a night for the full comparison, webhooks for the changes in between, and batched writes for what differs. Webhooks have their own limits: Shopify waits 5 seconds for an answer and retries 8 times over 4 hours. What that means for completeness is in webhooks are not a guarantee.
The ERP has a limit too
A sync has two ends and the slower one sets the pace. Shopify on a standard plan accepts 2,000 points of batched stock writes in 20 seconds. The ERP on the other end has to deliver 50,000 stock figures first. An API with 60 requests a minute and one item per request needs 833 minutes for that, close to 14 hours. NetSuite has no rate per minute and limits the number of requests in flight at once, 5 on its Standard tier, as described in NetSuite concurrency and governance limits.
Standard connectors and iPaaS platforms document their answer to THROTTLED errors as a smaller page size and a longer schedule interval. The errors stop, every run takes longer and the stock in the store is older. The sum for a whole run, with both ends in it, is in working out integration throughput.
What this means for your integration
We build the Shopify side on the GraphQL Admin API with the limits above as design input: bulk operations for full reads, 250 items per write, a check on THROTTLED in every response, and a queue that paces calls on currentlyAvailable. A throttled call stays in the queue and is sent again, and the dashboard log shows each attempt. Before the first live run a dry run shows which variants would change and to which value.
How we connect Shopify to the systems behind it is on the Shopify integration page and on Shopify ERP integration. The stock flow itself, with locations, buffers and delivery dates, is on stock and availability. Tell us your plan, your variant count and your ERP, and we work out what a full sync and a peak hour look like for your store.
Frequently asked questions
What is the Shopify API rate limit?
On the GraphQL Admin API the limit is a cost budget that restores at 100 points per second on standard plans, 200 on Advanced Shopify, 1,000 on Shopify Plus and 2,000 on Shopify for enterprise. On the legacy REST Admin API it is 2 requests per second with a bucket of 40, and 20 per second with a bucket of 400 on Plus. Both limits apply per combination of app and store.
Do Shopify's API rate limits affect which integration we can use?
They affect how the integration has to be built. An integration that updates one variant per call needs 83 minutes for 50,000 variants on a standard plan. One that sends 250 variants per call and reads through bulk operations stays under a minute of rate budget for the same work. The plan sets the ceiling; the call pattern decides how close you get to it.
Does Shopify Plus raise the API limits?
Yes. Plus raises the GraphQL Admin API budget from 100 to 1,000 points per second and the REST Admin API from 2 to 20 requests per second, with a REST bucket of 400 requests where other plans have 40. Plus stores are also exempt from the cap of 10,000 new variants per day that applies once a store has 500,000 variants.
What happens when an integration exceeds the limit?
The GraphQL Admin API answers with HTTP 200 and an error with the code THROTTLED in the response body. The REST Admin API answers with 429 Too Many Requests and a Retry-After header that gives the number of seconds to wait. The request was not executed in either case and has to be sent again. Shopify recommends a backoff of one second.
Do other apps on the store use up our rate limit?
No. Shopify calculates the limit per combination of app and store. Calls from another app on the same store do not reduce the budget of your integration, and calls from your integration to another store are counted against that store. The exception is the limit on bulk operations, which is also per app and per shop: five of each type at a time from API version 2026-01.
Tell us what you need
Tell us which systems you use and what needs to work better. We reply personally within one working day. We help you define the work, then agree the price and timing before you commit.
- You know the price and the timeline before you commit to anything
- We work out the connection that takes the work off your hands
- We handle the build, hosting, monitoring and maintenance
- Your team has less information to retype and fewer discrepancies to chase