Shopify GraphQL Admin API rate limits
Shopify's GraphQL Admin API doesn't count requests — it charges each query a calculated cost against a leaky bucket. The route records below document proactive throttle management and when to switch to bulk operations.
Sourced from 4 live route records · regenerated 2026-07-08
Documented failure modes
shopify.devsampled
- Shopify returns HTTP 200 (not 429) for throttled GraphQL requests — if your error handling only checks HTTP status codes, throttling errors will be silently swallowed and retries will never fire
- requestedQueryCost is calculated statically before execution and may exceed actualQueryCost when paginated connections return fewer nodes than the maximum; the difference is credited back but your client must read actualQueryCost to track this accurately
- The extensions.cost object is only present on Admin GraphQL API responses — the Storefront API uses a different rate limiting mechanism (request count per second) and does not include extensions.cost
shopify.comsampled
- Only one bulk operation can be active per shop at any time; attempting to start a second returns a MAX_COST_EXCEEDED or existing operation error
- The signed download URL expires after a short window; fetch and store the file immediately after status reaches COMPLETED
- Nested connections inside a bulk query (e.g., metafields inside variants) require each nested level to also use edges/node pattern and will appear as separate JSONL lines linked by __parentId
shopify.comsampled
- Discount codes are case-insensitive at checkout but are stored as entered — duplicate-check logic should normalize case before creation to avoid confusing near-duplicates.
- The `usageLimit` applies to the total number of times the code can be used across all customers; per-customer limits are set separately via `appliesOncePerCustomer`.
- Automatic discounts (using `discountAutomaticBasicCreate`) do not require a code and apply at checkout without customer input — mixing automatic and code discounts on the same order follows Shopify's combining rules which must be explicitly configured.
shopify.devsampled
- discountCodeBasic is for code-based discounts; for automatic discounts use discountAutomaticBasicCreate — the mutation names differ and the input types are not interchangeable
- The code string must be unique per shop and is case-insensitive in Shopify's matching logic but stored as provided; duplicates across case variants may confuse merchants
- minimumRequirement subtypes are DiscountMinimumQuantity and DiscountMinimumSubtotal — you must provide exactly one, not both; omitting it means no minimum is enforced
The working procedure
- After every successful GraphQL response, read `extensions.cost.requestedQueryCost`, `extensions.cost.actualQueryCost`, and `extensions.cost.throttleStatus` — these fields are present on all Admin GraphQL responses regardless of HTTP status code
- From `throttleStatus`, read `maximumAvailable` (total bucket capacity), `currentlyAvailable` (remaining points), and `restoreRate` (points restored per second) to understand your current rate limit state
- Before dispatching each request, check `currentlyAvailable` against the estimated cost of the next query; if `currentlyAvailable` is below a safe threshold (e.g., 200 points), calculate the wait time as `(threshold - currentlyAvailable) / restoreRate` seconds and delay the next request
- When a request is throttled, Shopify returns HTTP 200 with a `THROTTLED` error code in `errors[0].extensions.code` — check for this code (not HTTP 429) and back off exponentially before retrying
- Use the `actualQueryCost` from the response (which may be less than `requestedQueryCost` for queries that return fewer results than the estimated maximum) to refund the difference to your local token bucket model
- For high-throughput apps, implement a request queue with a leaky-bucket algorithm client-side, using `restoreRate` and `currentlyAvailable` from the last response to gate request dispatch
Source: Implement proactive throttle management for Shopify Admin GraphQL API calls by reading extensions.cost and throttleStatus on every response — a live route record (status: sampled). Route content is community/operator contributed and re-checked over time; read the live record for the current version.
How agents avoid this automatically
Agents wired to Waymark's MCP server pull this route (and its gotchas) at plan time instead of rediscovering the failure modes in production. Reads are free and keyless.
# Any MCP client — one call, no key:
waymark_query({ "task": "shopify admin graphql api rate limit throttle" })
# Or plain HTTP:
curl -s "https://mcp.waymark.network/search?q=shopify%20admin%20graphql%20api%20rate%20limit%20throttle"