Skip to content

Shopify Dude Fix

Shopify REST Admin API Is Legacy: How to Move a Product Integration to GraphQL

The REST Admin API became legacy in October 2024. Learn what still works, what changed for new apps, and how to migrate product reads to GraphQL safely.

Quick answer

Use the GraphQL Admin API for new Shopify integrations. Shopify classified the REST Admin API as legacy on October 1, 2024, and requires new public apps to use GraphQL exclusively starting April 1, 2025. That does not mean every REST request in every existing integration stopped working on that date. Audit the endpoints you use, migrate product and variant operations deliberately, and test pagination, scopes, mutations, and throttling before switching traffic.

On this page

Why old REST tutorials are misleading now

Many tutorials still start with GET /admin/api/{version}/products.json, then use the same pattern for variants and inventory. That can teach the shape of a legacy endpoint, but it is no longer a sound default architecture for a new app. New Shopify features may be GraphQL-only, and product/variant migration requirements are more specific than the general “REST is legacy” notice.

Do not describe April 1, 2025 as a universal REST shutdown. Existing integrations and some REST resources can still work, subject to their own deprecations and supported versions. The right question is which endpoint, for which app type, on which API version?

Practical implementation: replace a product list request

1. Inventory every REST call

Search the integration for /admin/api/, .json endpoint paths, REST client methods, and old product or variant writes. Record the app type, API version, scopes, request volume, and expected response fields. Group calls by business operation: read catalog, update product, create variants, adjust inventory, sync metafields, fulfill orders. Do not try to map every REST URL to a single generic GraphQL mutation.

2. Query only the fields you need

A server-side product list can use this GraphQL query. It requests IDs, titles, handles, and pagination metadata; no access token belongs in storefront JavaScript.

query ProductsPage($cursor: String) {
  products(first: 25, after: $cursor) {
    nodes {
      id
      title
      handle
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

Send it with POST to /admin/api/2026-07/graphql.json (or another currently supported, explicitly pinned version), using the app’s server-side access token and X-Shopify-Access-Token header. Pass {"cursor": null} for the first page. While hasNextPage is true, pass the previous endCursor into the next request. Do not substitute page numbers for cursors.

3. Handle GraphQL errors correctly

A successful HTTP status is not the whole success condition. Check top-level errors on GraphQL responses, then inspect userErrors on mutations. Product and variant writes use different mutations and inputs than legacy REST; verify each mutation’s required arguments and avoid sending partial lists to a mutation that treats them as the desired complete state.

GraphQL Admin API throttling uses calculated query cost, not the old REST request-count bucket. Inspect the response’s extensions.cost.throttleStatus, back off when necessary, and avoid overly wide nested connections. For large catalog exports, evaluate GraphQL bulk operations instead of hammering a small paginated query.

4. Migrate writes by behavior

For reads, a field-by-field replacement is often straightforward. For writes, decide whether you are creating a product, changing product metadata, managing options and variants, or updating inventory. These are separate operations with different validation rules. In particular, a product’s GraphQL GID is not the same string as its old numeric REST ID. Preserve an explicit ID mapping in integrations that persist identifiers.

Run old and new reads side by side for a sample catalog, normalize the output, and compare counts, handles, variant quantities, and metafields. For writes, use isolated test products and assert the final Shopify state after the mutation—not just the absence of a thrown exception.

Common misunderstanding

“REST is deprecated, so our existing order sync must have stopped on April 1, 2025.” Not necessarily. Shopify’s legacy designation, new-public-app requirement, and endpoint-specific deprecations are distinct. Audit your actual calls before diagnosing a failure.

“GraphQL is one request, so it cannot hit a rate limit.” GraphQL queries have calculated costs, and nested queries can be expensive. Efficient selection sets, pagination, backoff, and bulk operations matter.

How to test this

  1. Record one REST product query’s result on a representative test store.
  2. Run the GraphQL query with the same app scopes and compare product IDs after normalizing numeric IDs versus GIDs.
  3. Page until hasNextPage is false and verify the full count, not just the first 25 products.
  4. Test a product with many variants, a missing optional field, and a metafield used by your integration.
  5. Trigger an intentionally invalid mutation in a test store to confirm userErrors handling.
  6. Review API cost data and retry behavior under realistic batch load; pin and periodically review the API version.

Sources and further reading