Why Request-Based Rate Limits Fall Short for GraphQL

Rate limiting exists to protect API stability from abusive clients and runaway request loops. Traditional REST-style rate limits, including Shopify's own Admin REST API, rely on a credit system where each request consumes the same number of credits, with credits refilling over time. That model bounds the overall request rate and still accommodates occasional bursts.

But the request-based approach carries two inefficiencies. First, a client that asks for only a few fields pays the same cost as one that pulls everything an endpoint offers. Second, it ignores asymmetry in server cost between read operations and write operations that mutate state: POST, PUT, PATCH, and DELETE typically demand more server resources than a simple read, yet a flat credit charge treats all requests equally.

GraphQL's structure allows a different contract: compute a cost per query from what is actually being asked, so a small data request is cheap and a heavy one is expensive.

The Calculated Query Cost Model

Shopify's GraphQL Admin API gives each client 50 points per second, with a maximum bucket of 1,000 points. The cost of an individual query is derived by statically analyzing the query text before execution, assigning a point value to each component:

  • Objects cost 1 point. An object represents server-side work, such as a database read or an internal service call.
  • Scalars and enums cost 0 points. These return final leaf values inside objects, which are already charged. Fields like id, name, booleans, and strings add minimal marginal cost.
  • Connections cost 2 points plus the number of objects returned. A Relay-style connection requesting five nodes costs 7 points. The cursor and pageInfo fields are free because they are byproducts of the connection's underlying work.
  • Interfaces and unions cost 1 point. They behave like objects that can return multiple concrete types.
  • Mutations cost 10 points. Mutations can write to databases and indexes and may trigger webhooks or outgoing email, so they carry a premium over reads.

Reading Costs From the Response

You don't have to estimate costs manually. Every response includes an extension object with the calculated cost, which you can inspect in the Shopify Admin API GraphiQL explorer. To see how individual fields contributed, send the request with the X-GraphQL-Cost-Include-Fields: true header.

Requested Cost vs. Actual Cost

Two cost values appear in responses. The requested cost is determined through static analysis before the query runs, based on what the query asks for — for example, a connection requesting five products. The actual cost reflects the work while executing. If the connection only returns one matching product, only the points for that smaller result are charged, and the difference between requested and actual cost is refunded to the client.

Correlating Cost and Server Load

Rate Limiting GraphQL APIs by Calculating Query Complexity

Effect on API Stability

The calculated query complexity and execution time have a linear correlation
The calculated query complexity and execution time have a linear correlation

Because query cost correlates with execution time, the calculated cost model gives Shopify a predictable signal for scaling infrastructure in a way that few other variables could match. Outliers — queries whose cost and execution time diverge — surface immediately, providing targets for performance tuning.

The shift to complexity-based rate limiting is a direct benefit to well-behaved clients: because cost tracks the data actually requested rather than the number of HTTP calls, good API hygiene (asking for only what you need) is rewarded with lower credit consumption, and the platform gains stricter control over query-induced load.