Shopify API cost means one thing to merchants ("how much does the API add to my bill?") and something completely different to developers ("why is my sync getting 429'd at 2 AM?"). The answer to the first question is simple: Shopify does not charge per API call. The answer to the second question is where the real work starts, because the GraphQL Admin API uses a complexity-based cost model that behaves nothing like the REST endpoint most developers learned on.
Key takeaways
- Shopify charges nothing per API call. Rate limits are technical, not financial.
- The GraphQL Admin API meters calculated query cost in points, not request count.
- A single query is hard-capped at 1,000 points, regardless of your plan.
- Restore rates by plan: 100 pts/sec (Standard), 200 pts/sec (Advanced), 1,000 pts/sec (Plus), 2,000 pts/sec (Enterprise).
- From API version 2026-01, each app can run up to five concurrent bulk operations per shop per type, up from one.
Why "Shopify API cost" means two different things
Merchants searching this query usually want to know whether calling the API adds to their Shopify invoice. It does not. The Admin API, Storefront API, and Customer Account API are all included in your plan with no per-call billing. What the plan does control is your rate limit budget, and that budget determines how fast your integrations can move.
Developers hit this topic from the other direction: a sync that worked fine on a Standard plan starts failing on a client's store after a spike in catalog size, or a headless build on one store gets throttled while the same code runs fine on a Plus account. Both symptoms trace back to the same cost model.
How GraphQL query cost is calculated
Calls to the GraphQL Admin API are limited based on calculated query cost, which means you should consider the cost of requests over time rather than the number of requests.
Shopify scrapped request counting for GraphQL. The server runs static analysis on your query before execution, scores it by complexity, and deducts points from a bucket.
The scoring rules are fixed and documented. Here is the full cost table from the Shopify developer docs:
| Field type | What it represents | Cost |
|---|---|---|
| Scalar / enum | A final value: id, title, email | 0 points |
| Object | A single related entity: order.customer | 1 point |
| Connection | A paginated list: products(first: 50) | 2 + 1 per returned item |
| Mutation | Any write: create, update, delete | 10 points |
The requested cost is based on the composition of fields selected in the request. The actual cost is based on the query results, and may be lower than requested cost due to the actual objects returned or connections that return fewer edges than requested.
Rate limits use a combination of the requested and actual query cost. Before execution begins, an app's bucket must have enough capacity for the requested cost of a query. When execution is complete, the bucket is refunded the difference between the requested cost and the actual cost of the query.
This is a genuinely useful property. If you query products(first: 250) but only 12 products exist, you reserved 252 points but you are refunded most of them after execution. The dangerous case is the inverse: you ask for first: 250 on a nested connection inside a loop, and the static analysis sees worst-case cost before your results arrive.
Worked example
This query illustrates the scoring in practice:
{
products(first: 50) {
edges {
node {
id
title
variants(first: 10) {
edges {
node {
id
sku
price
}
}
}
}
}
}
}
products(first: 50)connection: 2 + 50 = 52 points- Each
variants(first: 10)connection inside 50 products: (2 + 10) x 50 = 600 points - Scalar fields (
id,title,sku,price): 0 points - Requested cost: ~652 points
That single query is more than half a Standard plan's entire bucket. A single poorly structured query can cost 1,000 points. A well-optimized one may cost under 50.
To get per-field breakdowns without guessing, add X-GraphQL-Cost-Include-Fields: true to the request header. You can get detailed per-field query costs in the extension object by adding the X-GraphQL-Cost-Include-Fields: true header to your request.
The hard ceiling that no plan lifts
A single query may not exceed a cost of 1,000 points, regardless of plan limits. This limit is enforced before a query is executed based on the query's requested cost.
This is the trap that catches developers who upgrade to Plus thinking a bigger bucket will fix their problem. The bucket size and restore rate both scale with plan, but a query that hits 1,001 requested points is refused on every plan. Exponential backoff will never fix it. You need to restructure the query.
Rate limits by plan (verified October 2026)
The official Shopify plan restore rates are as follows:
| Plan | Rate limit |
|---|---|
| Standard | 100 points/second |
| Advanced Shopify | 200 points/second |
| Shopify Plus | 1,000 points/second |
| Shopify for Enterprise (Commerce Components) | 2,000 points/second |
As of September 2026, bucket capacity scales to match: 2,000 points on Standard and 20,000 on Plus.
For comparison, the legacy REST Admin API still uses a request-based leaky bucket. The legacy REST Admin API uses a leaky bucket of 40 requests refilling at 2 per second, raised tenfold on Plus.
The practical consequence: the same connector behaves like a different product on two different stores, and why the fix for a throttled sync is almost never a bigger plan. Upgrading from Standard to Plus gives you 10x the restore rate, but it does not fix a query that was already over the 1,000-point hard cap or a sync pattern that ignores the throttleStatus response.
Reading throttleStatus in production
Every successful and throttled response from the Shopify Admin GraphQL API includes an extensions.cost.throttleStatus object. This object contains three critical integers: maximumAvailable, currentlyAvailable, and restoreRate.
Here is what a throttled response looks like:
{
"errors": [{
"message": "Throttled",
"extensions": {
"code": "THROTTLED",
"cost": {
"requestedQueryCost": 500,
"throttleStatus": {
"maximumAvailable": 2000,
"currentlyAvailable": 120,
"restoreRate": 100
}
}
}
}]
}
From currentlyAvailable (120) and restoreRate (100 points/sec), you can calculate exactly how long to wait before retrying: (requestedCost - currentlyAvailable) / restoreRate = (500 - 120) / 100 = 3.8 seconds. That is a deterministic delay, not a guess.
A reliable production pattern reads currentlyAvailable from the extensions block after every response and sleeps proactively when the remaining budget drops below the next query's requestedQueryCost. Most developers only react to THROTTLED errors, which means they burn a round-trip on every rejection. Reading the budget ahead of time eliminates those wasted calls entirely.
When to stop fighting the bucket and use Bulk Operations
For catalog work on the 10,000+ SKU stores I maintain, paginated GraphQL loops are the wrong tool the moment the record count exceeds a few hundred. The Bulk Operations API exists precisely for this.
When you need to extract large datasets, you must switch to the Bulk Operations API. This API allows you to submit a single GraphQL query that Shopify executes asynchronously. The results are written to a JSONL file, and Shopify provides a secure URL to download the file once the operation completes.
The concurrency model changed significantly in early 2026. In API versions 2026-01 and higher, each app can run up to five bulk mutation operations concurrently per shop. This enables you to import multiple datasets simultaneously without waiting for each operation to complete. In API versions prior to 2026-01, each app can run only one bulk operation of each type at a time per shop.
This is a meaningful change for migration pipelines. In pre-2026-01 code, a running product export blocks an order export from the same app on that shop. With 2026-01 you can fan out five parallel mutation jobs and five parallel query jobs simultaneously, which cuts large import times substantially.
Bulk operations decision guide
| Situation | Use standard GraphQL | Use Bulk Operations |
|---|---|---|
| Fetch a single product or order | Yes | No |
| Paginate up to ~200 records interactively | Yes | No |
| Export or sync 1,000+ records | No | Yes |
| Deeply nested connections that approach 1,000-point cap | No | Yes |
| Real-time user-facing query (checkout, PDP) | Yes | No |
| Nightly catalog backfill or ETL sync | No | Yes |
| Mutations across thousands of variants | No | Yes |
Bulk query results remain available for seven days after completion, so your downstream worker has a generous window to stream and process the JSONL before the URL expires.
Five patterns that actually reduce query cost
These are the adjustments that consistently move the needle on throttling across the apps and integrations I build:
- Request only the fields you need. Scalar fields cost 0 points. Adding
idandtitleto a 50-item product connection costs nothing extra. But every nested connection multiplies. Remove any field you are not actively using in the response. - Shrink your
firstargument. A products connection withfirst: 250costs 252 points before any nesting. Dropping tofirst: 25costs 27 points. If your UI only shows 24 items, you never needed 250. - Pace against restoreRate, not THROTTLED errors. Read
currentlyAvailableon every response. If the next query'srequestedQueryCostexceedscurrentlyAvailable, sleep the calculated gap. This eliminates rejected requests. - Batch mutations correctly. Mutations are inherently more expensive than queries. Every mutation carries a default cost of 10 points, and Shopify reserves the right to price individual fields higher. Plan your batch sizes against the cost model, not just array length.
- Switch to Bulk Operations at the right threshold. Use Bulk Operations API for any data operation touching 1,000+ records. Attempting to paginate your way through 50,000 orders on a Standard plan will exhaust the bucket repeatedly. A single bulk query handles the same data asynchronously with no point cost per record.
Resource-based limits: the less-talked-about ceiling
Beyond the leaky bucket, there is a second class of limit worth knowing. Once a store has 500,000 product variants, no more than 10,000 new variants can be created per day, on any API. Past that threshold, the relevant GraphQL Admin API mutations are subject to the daily throttle, and an app that reaches it receives a 429 Too Many Requests response with a message that a throttle has been applied.
For the glossary on concepts like leaky bucket throttling and connection pagination, those terms come up repeatedly when debugging these limits in production.
Verifying your own query costs
Before shipping any new sync or data-heavy feature, run the target query in Shopify's built-in GraphiQL explorer (available at https://your-store.myshopify.com/admin/apps/graphiql) and check the extensions.cost block in the response. The requestedQueryCost tells you exactly what your bucket reservation is. The actualQueryCost tells you what you actually paid. If the gap is large, you have headroom to increase your first argument or add connections without hitting the cap.
For any query where requestedQueryCost exceeds 800, treat it as a candidate for refactoring before it reaches a Standard plan store. That 200-point buffer is the difference between a query that works on every client's account and one that silently fails at scale.
Frequently asked questions
Does Shopify charge for API calls?
No. Shopify does not charge per API call. The Admin, Storefront, and Customer Account APIs are all included in your plan. What your plan controls is your rate limit budget, which determines how fast your integrations can run, not what you are billed.
What is the maximum cost a single Shopify GraphQL query can have?
A single query is hard-capped at 1,000 requested points, regardless of your Shopify plan. This limit is checked before the query executes. A query that exceeds 1,000 points is rejected outright and cannot be retried with backoff alone as it must be restructured.
When should I use the Bulk Operations API instead of paginated GraphQL queries?
Switch to the Bulk Operations API for any data operation involving 1,000 or more records, deeply nested connections that approach the 1,000-point single-query ceiling, or nightly catalog syncs and ETL pipelines. From API version 2026-01 onward, each app can run up to five concurrent bulk operations per shop per type, and results are delivered as a JSONL file available for seven days.