Skip to main content

Handle errors and budgets

When you run ShopifyQL from an app, you need to handle queries that fail, requests that hit the rate limit, and reporting periods that are still filling in. This page covers how to catch each error, read your remaining budget before you hit the limit, and keep the numbers you display accurate.


Anchor to Handle failed queriesHandle failed queries

When a query fails, the error shows up in one of two fields, parseErrors or errors, depending on what went wrong. Check both fields before you read the results.

A syntactically invalid query still returns a 200 OK response. tableData is null, and the error appears in the shopifyqlQuery response's parseErrors array. Check parseErrors before you read tableData.

JSON response

{
"data": {
"shopifyqlQuery": {
"tableData": null,
"parseErrors": [
"Column Not Found: Column 'total_revenue' not found"
]
}
}
}

Authentication failures or a missing read_reports access scope return as standard errors. The data payload is null. Check errors before you read data, and handle them like any other GraphQL Admin API error.

JSON response

{
"data": null,
"errors": [
{
"message": "Access denied for shopifyqlQuery field. Required access: read_reports access scope.",
"extensions": {
"code": "ACCESS_DENIED",
"documentation": "https://shopify.dev/api/usage/access-scopes"
}
}
]
}

For the full response shape and status codes, refer to error handling in the GraphQL Admin API.


Anchor to Stay within the complexity budgetStay within the complexity budget

ShopifyQL rate limits are based on query complexity. Heavier queries cost more against your budget. A query's complexity grows with:

  • The number of keywords and clauses you use.
  • The number of metrics in SHOW.
  • The number of dimensions in GROUP BY.

To lower a query's cost, request only the metrics and dimensions you'll display, and drop clauses you don't need.

Anchor to Read your remaining budgetRead your remaining budget

The shopifyqlQuery response returns a shopifyqlCost object in the GraphQL extensions. Read it to check a query's cost and your remaining budget before you hit a 429.

JSON response

{
"extensions": {
"shopifyqlCost": {
"requestedQueryCost": 1,
"maximumAvailable": 1000,
"currentlyAvailable": 999,
"windowResetAt": "2026-07-16T07:06:00+00:00"
}
}
}

The object has four fields:

  • requestedQueryCost: The complexity cost of the query you ran.
  • maximumAvailable: The most budget available in a window.
  • currentlyAvailable: The budget left in the current window.
  • windowResetAt: When the window resets and the budget refills.

A shopifyqlQuery request draws down two budgets, both returned in extensions. shopifyqlCost, shown above, is the ShopifyQL-specific budget. The standard GraphQL Admin API cost budget applies too, and every other GraphQL Admin API call your app makes shares it. Either budget can return a 429. The ShopifyQL budget is the smaller of the two, and it's usually the first to run out.

When a query exceeds the ShopifyQL budget, the API returns a 429. Wait until the windowResetAt timestamp before you retry.


Anchor to Account for data freshnessAccount for data freshness

The current reporting period is still filling in. A query that includes today returns a partial day, and today's total climbs as new orders land. Querying the same range an hour later can return a higher number.

What you do about it depends on what you're showing:

  • For stable totals, like a monthly report or a number you reconcile against the store's own reports, exclude the in-progress period. Running SINCE startOfDay(-30d) UNTIL endOfDay(-1d) snaps the range to whole days with date functions and drops today's partial data. The total won't move after you read it.
  • For a live view, like sales so far today, expect the latest point to climb as orders land. Label it with the fetch time to signal that the number is still in progress.

Completed periods don't change. A cached result stays correct until the period you're querying advances. Caching cuts latency for users and lowers the query complexity you spend against the rate limit.

Match how long you cache to the query's smallest time bucket:

  • Totals over months or all time can cache for hours.
  • A daily or hourly view can cache for a few minutes.

Key each cache entry by the store and the exact query string. Results differ by both, and a shared key returns the wrong store's data or stale rows for a query you changed.


You've now handled what changes when a ShopifyQL query runs in a live app. To see it wired into a full build, work through the sales dashboard. To write leaner queries that cost less, learn the full query language.


Was this page helpful?