our REST Admin API is deprecated and receives no new features — GraphQL is now the only supported path for new endpoints, metafield types and bulk operations. Migrate incrementally by mapping each REST call to its GraphQL equivalent, adopting the query-cost budgeting model, and using the Bulk Operations API for anything that used to loop through paginated REST responses.
- As of the 2024-04 API version, new features ship GraphQL-only.
- GraphQL uses a query-cost model, not request-per-second — think budget, not throttling.
- Bulk Operations replace pagination loops for any query over ~250 records.
- REST cursors and GraphQL cursors are not interchangeable — plan a re-sync when migrating.
- The Storefront API mirrors Admin GraphQL, so skills transfer directly to headless work.
REST for maintenance only. GraphQL for everything new. The Bulk Operations API is the single biggest performance unlock during migration.
Why Shopify is deprecating REST
REST endpoints have three structural problems that GraphQL fixes. First, over-fetching: a `GET /products/{id}.json` returns 40+ fields even when you need three. Second, under-fetching: to get a product's inventory levels at each location you need N+1 requests. Third, versioning: adding a field to a REST response is a breaking change; GraphQL clients select their fields explicitly, so the API can evolve without breaking anyone. Shopify has been public about this since 2022 — REST is in maintenance and no new resources will ship there.
The cost model
REST throttles per-second. GraphQL charges per query, measured in points, against a bucket that refills continuously. A simple product fetch costs 1 point; a complex nested product-with-variants-with-inventory query might cost 20. Every app gets a bucket of 1,000 points that refills at 50 points/second (Basic plan) or 100 points/second (Advanced+).
| Query complexity | Approx cost | REST equivalent |
|---|---|---|
| Single product by ID | 1–3 points | 1 request |
| Product + variants + images | 10–15 points | 3–5 requests |
| 50 products with metafields | 50–150 points | ~50 requests |
| Bulk export of full catalog | 10 points (start) + async | 10,000+ requests |
Shopify calculates the maximum possible cost from your query shape. If it exceeds your available budget, the response is a 429 with the exact cost — throttle proactively, don't retry blindly.
Migrating a common REST pattern
Take the classic 'fetch all products and their inventory levels' — a REST implementation loops through paginated products, then makes an inventory call per variant. In GraphQL, it's one query.
query ProductsWithStock($cursor: String) {
products(first: 50, after: $cursor) {
pageInfo { hasNextPage endCursor }
edges {
node {
id
title
variants(first: 100) {
edges {
node {
id
sku
inventoryQuantity
inventoryItem {
inventoryLevels(first: 10) {
edges {
node {
available
location { name }
}
}
}
}
}
}
}
}
}
}
}When to use Bulk Operations
For anything over ~250 records, pagination becomes the bottleneck — not because GraphQL is slow, but because you're paying cost points on every page. Bulk Operations turn any GraphQL query into an async job. Shopify runs it against the entire catalog on their infrastructure, writes the result to a JSONL file on their CDN, and returns the download URL when complete. One start-mutation, one poll, one file download — regardless of catalog size.
- 1Start
Send a `bulkOperationRunQuery` mutation with your query as a string.
- 2Poll
Query `currentBulkOperation` every 5–30 seconds. Status transitions CREATED → RUNNING → COMPLETED.
- 3Download
When status is COMPLETED, fetch the returned `url`. It expires in 7 days.
- 4Parse
The file is JSONL — one JSON object per line. Use a streaming parser; a 500k product export is a 2GB+ file.
Authentication changes
GraphQL uses the same `X-Shopify-Access-Token` header as REST. The migration surface is the schema and the endpoint URL (`/admin/api/2026-01/graphql.json`), not auth. OAuth scopes remain identical — `read_products`, `write_products` — but some newer resources (Markets, Metaobjects) have their own scope names that don't exist in REST.
Cursor incompatibility
REST uses `since_id` or `page_info` cursors that don't work in GraphQL. GraphQL uses opaque base64 cursors from `pageInfo.endCursor`. If you have a running REST sync job, you cannot resume it in GraphQL — plan a full re-sync during the migration window and store both cursor types until you're fully cut over.
Every mutation in REST and every mutation in GraphQL both fire the same webhooks. If you have parallel syncs writing the same resource, you'll double-invoke every downstream integration.
The deprecation timeline
Shopify publishes a rolling deprecation schedule at `/admin/oauth/access_scopes` in their developer changelog. Key milestones every app maintainer should track.
- 2024-04: REST officially in maintenance — no new features, security fixes only.
- 2024-10: New metafield reference types available only via GraphQL.
- 2025-01: New Markets/localisation endpoints GraphQL-only.
- 2025-10: First deprecated REST endpoints (products/count.json) removed.
- 2026 onward: annual sunset waves for legacy REST resources.
Migration checklist
For anyone maintaining a Shopify integration in 2026, run this checklist quarterly.
- Inventory every REST call your app makes.
- Map each to a GraphQL equivalent using the Shopify docs migration table.
- Rewrite pagination loops as Bulk Operations where record counts exceed 250.
- Add cost-budget headers to your monitoring — 429s tell you exactly what to fix.
- Version-pin your GraphQL requests (`/admin/api/2026-01/graphql.json`) and bump quarterly.
- Subscribe to the Shopify Developer Changelog RSS for deprecation warnings.
Frequently asked questions
The most common questions merchants ask us about developers.