THROTTLED on ShopifyqlQuery graphql endpoint despite headroom

Hi friends!

Started seeing a THROTTLED message on many of our graphql queries today, despite plenty of headroom:

For example, this is a simple query on a Plus store, replicated in postman for clarity:

Is ShopifyqlQuery throttled by some other mechanism?

Hi @bailey

It does seem that ShopifyqlQuery has different rules around rate limiting - can you share the query you’re making, removing any sensitive info?

hey @Liam-Shopify !

We use ShopifyqlQuery to help merchants understand their data and interpret how are app is impacting key metrics. It’s crucial for us to have a reliable baseline.

For example, this query started giving us issues lately:

"FROM sales SHOW returning_customer_rate GROUP BY customer_sms_subscription_status WITH TOTALS VISUALIZE returning_customer_rate”

We are getting the same exact issue here. This was working properly through 1/30/2026 on an out-of-band batch process.

It has nothing to do with rate limiting, as running a single call to shopifyqlQuery is failing

Our call:

query RunShopifyQL($query: String!) {
shopifyqlQuery(query: $query) {
__typename
parseErrors
tableData {
columns {
name
dataType
displayName
}
rows
}
}
}

Body:

{
“query”: "\n query RunShopifyQL($query: String!) {\n shopifyqlQuery(query: $query) {\n __typename\n parseErrors\n tableData {\n columns {\n name\n dataType\n displayName\n }\n rows\n }\n }\n }\n ",
“variables”: {
“query”: “\nFROM inventory_adjustment_history\n SHOW inventory_adjustment_change\n WHERE product_variant_sku = ‘PUT-SKU-HERE’\n AND inventory_location_name = ‘LOCATION NAME’\n AND inventory_change_reason = ‘purchase’\n AND inventory_state = ‘committed’\n GROUP BY product_variant_sku, inventory_app_name, inventory_change_reason,\n inventory_state, day WITH TOTALS\n HAVING inventory_adjustment_change != 0\n SINCE startOfDay(-365d) UNTIL today\n ORDER BY day ASC\n LIMIT 1000\n”
}

This is the query, and it works fine within the Reporting console:

FROM inventory_adjustment_history
SHOW inventory_adjustment_change
WHERE product_variant_sku = ‘SKU-HERE’
AND inventory_location_name = ‘LOCATION-HERE’
AND inventory_change_reason = ‘purchase’
AND inventory_state = ‘committed’
GROUP BY product_variant_sku, inventory_app_name, inventory_change_reason,
inventory_state, day WITH TOTALS
HAVING inventory_adjustment_change != 0
SINCE startOfDay(-365d) UNTIL today
ORDER BY day ASC
LIMIT 1000

The response payload (ALWAYS returns a rate limit)

{
“errors”: [
{
“message”: “Rate limited. Please retry later.”,
“locations”: [
{
“line”: 3,
“column”: 7
}
],
“extensions”: {
“code”: “THROTTLED”,
“requestId”: “6bfb632f-2e2e-4ed6-a9a0-7756730afbce-1770393362”
},
“path”: [
“shopifyqlQuery”
]
}
],
“data”: {
“shopifyqlQuery”: null
},
“extensions”: {
“cost”: {
“requestedQueryCost”: 3,
“actualQueryCost”: 1,
“throttleStatus”: {
“maximumAvailable”: 20000.0,
“currentlyAvailable”: 19999,
“restoreRate”: 1000.0
}
}
}
}

yes, sorry if I wasn’t clear in the original post, we’re not doing this on any sort of interval where we would be rate limiting. we see this as throttling on a single request.

Has something changed around the limits for shopifyQL because I am also getting THROTTLED / rate limited for queries that were working a couple of days ago

I heard back from Shopify developer support. This was their exact response: ”This limit is based on time and other factors like number of fields included. Although the specifics of this limiting isn’t currently exposed or documented, there are future plans to do so although they aren’t currently scheduled and we don’t have a timeline we’re able to share.”

Translation: “There’s a limit. We won’t tell you what it is. We might document it eventually. No promises. Good luck.”

@NickP-Shopify can you provide any more information on this please?

1 Like

Also seeing THROTTLED issues all of the sudden, for the exact same stores & queries that have been working just fine for months. I’m not using ShopifyqlQuery though, these are on inventorySetOnHandQuantities mutation calls.

Bumping this up for visibility.

Hi folks - we’re still working on improving the current experience. Our team will post here when there’s more to share.

Hi @Liam-Shopify thanks for the update, I thought that the throttling was a bug that would be fixed but are you saying that this is expected behaviour? Because it’s broken functionality in my app that was working last week

us too - causing issues for merchants using our app!

Thank you all for reaching out about the throttling you’re experiencing with your ShopifyQL inventory queries.

Starting January 30, we implemented stricter throttling for inventory-specific ShopifyQL queries to protect analytics platform stability. This change was made to protect platform stability and affected existing integrations. We understand this changed behavior that was previously working, and we did not document this in our changelog at the time.

The throttling limits are permanent. Queries requesting extended date ranges of inventory data may exceed the per-request complexity limits we’ve implemented. These limits are based on multiple factors including date range, number of fields requested, and query complexity, not simply a fixed number of days.

Our testing shows that queries with shorter date ranges (approximately 20-30 days) are more likely to succeed, though the exact threshold varies based on the specific fields and complexity of each request. We recommend reducing your query date ranges and field complexity to work within the new limits.

We appreciate the trust you place in Shopify to power your business and apologize for any inconvenience.

1 Like

Hey @Paige-Shopify , thanks for the detailed reply.

Is it a bug if we’re seeing this throttle on non-inventory related queries?

@bailey we also have this throttling for payment related queries and queries specifying a long date range.

I have a sample query that I used to try and extrapolate the cost for my usage (your usage will vary)

FROM inventory_adjustment_history
SHOW inventory_adjustment_change
WHERE product_variant_sku = ‘XXXXX’
AND inventory_location_name = ‘XXXXX’
AND inventory_change_reason = ‘purchase’
AND inventory_state = ‘committed’
GROUP BY day WITH TOTALS
HAVING inventory_adjustment_change != 0
SINCE startOfDay(-XXd) UNTIL today
ORDER BY day ASC
LIMIT 1000

My results:

Days(XX) Status     ShopifyQL Cost     Max Allowed    Elapsed

1        SUCCESS    405                1000           0.49s
2        SUCCESS    405                1000           0.44s
3        SUCCESS    405                1000           0.58s
4        SUCCESS    405                1000           0.57s
5        SUCCESS    405                1000           0.63s
6        SUCCESS    405                1000           0.70s
7        SUCCESS    405                1000           0.51s
8        SUCCESS    405                1000           0.56s
9        SUCCESS    405                1000           0.58s
10       SUCCESS    405                1000           0.59s
11       SUCCESS    405                1000           0.81s
12       SUCCESS    405                1000           0.66s
13       SUCCESS    405                1000           0.55s
14       SUCCESS    405                1000           0.58s
15       SUCCESS    405                1000           0.55s
16       SUCCESS    405                1000           0.59s
17       SUCCESS    405                1000           0.67s
18       SUCCESS    405                1000           0.68s
19       SUCCESS    405                1000           0.61s
20       SUCCESS    405                1000           0.70s
21       SUCCESS    405                1000           0.64s
22       SUCCESS    405                1000           0.77s
23       SUCCESS    405                1000           0.82s
24       SUCCESS    405                1000           0.66s
25       SUCCESS    405                1000           0.74s
26       SUCCESS    405                1000           1.15s
27       SUCCESS    405                1000           0.68s
28       SUCCESS    405                1000           0.68s
29       SUCCESS    405                1000           0.77s
30       SUCCESS    405                1000           0.69s
31       SUCCESS    810                1000           0.66s
32       SUCCESS    810                1000           0.76s
33       SUCCESS    810                1000           0.98s
34       SUCCESS    810                1000           0.65s
35       SUCCESS    810                1000           0.70s
36       SUCCESS    810                1000           0.75s
37       SUCCESS    810                1000           0.69s
38       SUCCESS    810                1000           0.62s
39       SUCCESS    810                1000           0.57s
40       SUCCESS    810                1000           0.76s
41       SUCCESS    810                1000           0.72s
42       SUCCESS    810                1000           0.64s
43       SUCCESS    810                1000           0.95s
44       SUCCESS    810                1000           0.66s
45       SUCCESS    810                1000           0.63s
46       SUCCESS    810                1000           0.73s
47       SUCCESS    810                1000           0.71s
48       SUCCESS    810                1000           0.67s
49       SUCCESS    810                1000           0.76s
50       SUCCESS    810                1000           0.68s
51       SUCCESS    810                1000           0.71s
52       SUCCESS    810                1000           0.67s
53       SUCCESS    810                1000           0.72s
54       SUCCESS    810                1000           0.74s
55       SUCCESS    810                1000           0.84s
56       SUCCESS    810                1000           0.73s
57       SUCCESS    810                1000           0.67s
58       SUCCESS    810                1000           0.74s
59       SUCCESS    810                1000           0.67s
60       SUCCESS    810                1000           0.70s
61       FAILED     1215               1000           0.25s
62       FAILED     1215               1000           0.21s
63       FAILED     1215               1000           0.21s
64       FAILED     1215               1000           0.26s
65       FAILED     1215               1000           0.19s
66       FAILED     1215               1000           0.23s
67       FAILED     1215               1000           0.29s
68       FAILED     1215               1000           0.22s
69       FAILED     1215               1000           0.21s
70       FAILED     1215               1000           0.28s
71       FAILED     1215               1000           0.31s
72       FAILED     1215               1000           0.25s
73       FAILED     1215               1000           0.22s
74       FAILED     1215               1000           0.20s
75       FAILED     1215               1000           0.22s
76       FAILED     1215               1000           0.26s
77       FAILED     1215               1000           0.29s
78       FAILED     1215               1000           0.19s
79       FAILED     1215               1000           0.22s
80       FAILED     1215               1000           0.24s
81       FAILED     1215               1000           0.26s
82       FAILED     1215               1000           0.25s
83       FAILED     1215               1000           0.36s
84       FAILED     1215               1000           0.24s
85       FAILED     1215               1000           0.25s
86       FAILED     1215               1000           0.25s
87       FAILED     1215               1000           0.24s
88       FAILED     1215               1000           0.27s
89       FAILED     1215               1000           0.28s
90       FAILED     1215               1000           0.20s
91       FAILED     1620               1000           0.24s
92       FAILED     1620               1000           0.24s
93       FAILED     1620               1000           0.35s
94       FAILED     1620               1000           0.20s
95       FAILED     1620               1000           0.19s
96       FAILED     1620               1000           0.28s
97       FAILED     1620               1000           0.25s
98       FAILED     1620               1000           0.32s
99       FAILED     1620               1000           0.29s
100      FAILED     1620               1000           0.30s
...

What’s frustrating here, is once you hit 1000 or even try a query that is > 1000, there’s no drip rate (restore rate, etc) – it’s just a minute delay. Even when it fails fast – why this this the case?

If I try to do a query that costs 1500 ShopifyQL bux, and it fails instantly, then I need to wait a minute.

So, on a fresh start, I get a response that looks like:

{
“errors”: [
{
“message”: “Rate limited. Please retry later.”,
“locations”: [
{
“line”: 3,
“column”: 7
}
],
“extensions”: {
“code”: “THROTTLED”,
“requestId”: “41868552-61c8-4b2b-85a5-8d0411e77e2b-1771353585”,
“cost”: {
“requestedQueryCost”: 1215,
“maximumAvailable”: 1000,
“currentlyAvailable”: 0,
“windowResetAt”: “2026-02-17T18:40:45.302089901+00:00”
}
},
“path”: [
“shopifyqlQuery”
]
}
],
“data”: {
“shopifyqlQuery”: null
},
“extensions”: {
“cost”: {
“requestedQueryCost”: 3,
“actualQueryCost”: 1,
“throttleStatus”: {
“maximumAvailable”: 20000.0,
“currentlyAvailable”: 19999,
“restoreRate”: 1000.0
}
}
}
}

ERROR FOUND:
Code:       THROTTLED
Message:    Rate limited. Please retry later.
Request ID: 41868552-61c8-4b2b-85a5-8d0411e77e2b-1771353585

PER-REQUEST SHOPIFYQL COST:
Requested Cost:  1215
Maximum Allowed: 1000
Currently Avail: 0
Window Resets:   2026-02-17T18:40:45.302089901+00:00
Drip Rate:       16.7 pts/sec  (60.0s until full)

OUTER GRAPHQL BUCKET COST:
Requested Cost:  3
Actual Cost:     1
Available:       19999/20000.0
Restore Rate:    1000.0/sec

I should be able to do a threshold regression in my own code without having to wait a minute to test a new value.

1 Like

Thanks for the detailed feedback and the example responses, it’s super helpful.

If I try to do a query that costs 1500 ShopifyQL bux, and it fails instantly, then I need to wait a minute

We’ve shipped a fix for this, so it should no longer happen. If you run into this again, or see something similar, please let us know.

What’s frustrating here, is once you hit 1000 or even try a query that is > 1000, there’s no drip rate (restore rate, etc) – it’s just a minute delay. Even when it fails fast – why this this the case?

The drip isn’t necessary, since queries that exceed the limit don’t consume the limit.

@Paige-Shopify The LIMIT keyword doesn’t seem to affect the query cost, I get the same cost whether I do LIMIT 1000 or LIMIT 25000.

For queries that return a large number of records (e.g. inventory adjustment history), we’ve been doing paginated requests via LIMIT and OFFSET, 1000 records at a time to play it safe since that’s the default.

With this change where we have to wait 60 seconds to refill, pulling the data has gotten very slow so we’d like to change the LIMIT to a higher number, but concerned this might break all of a sudden without notice because of another change in the query cost calculation

Is there a plan to factor that into the query cost calculation in the future? Also, is there certain LIMIT value you would recommend not to go over?

Hi @Jonathan-HA,

Great question, because I don’t believe we’ve explained this thoroughly anywhere.

Essentially, the LIMIT doesn’t affect query cost. The rate limiting is based on structural query complexity (dimensions, etc.), not on how many rows are returned.

The 60-second refill is the rate limit window resetting. If you’re hitting it, it’s because accumulated query complexity across multiple requests exceeded 1000 points within that window, not because of a single high-LIMIT query. Reviewing query patterns (how many queries per minute, and the complexity of each) would help identify where you can reduce consumption.

Regarding future changes, we’re not able to share any plans publicly at this time and we don’t have a public guarantee that the query cost calculation is frozen. If it changes in the future, we’ll make sure to release a changelog.

If you’re building something latency-sensitive, the safest approach is to:

  1. Spread queries over time rather than sending bursts
  2. Simplify queries where possible (fewer GROUP BY dimensions, avoid BENCHMARKS if not needed)
  3. Cache results on your side where the data doesn’t need to be real-time

Hope that helps, let me know if you have any other questions.

1 Like