ScreenshotNeo

BlogHow-to

How to Use Shopify’s GraphQL Buy API

Use Shopify’s Storefront GraphQL API and JS Buy SDK to query products, create carts, and send buyers to hosted checkout.

By the ScreenshotNeo team1 October 202610 min read

Short answer: Shopify’s “GraphQL Buy API” is usually the Storefront API used directly through GraphQL or through Shopify’s JavaScript Buy SDK. Query products, let the buyer choose variants, create a Storefront Cart, then redirect the buyer to the cart’s checkoutUrl. Do not build new integrations on Shopify’s legacy Checkout APIs: they were deprecated in API version 2024-04 and sunset in 2025-04.

1. Understand Shopify’s Buy terminology

Shopify uses several names for related tools:

Term What it is Use it when
Storefront API Shopify’s GraphQL-only API for storefront experiences. You want direct control over queries, cart behavior, rendering, or a custom backend.
JS Buy SDK A JavaScript library built on the Storefront API. It includes helpers for products, collections, carts, options, quantities, and checkout URLs. You want JavaScript helpers instead of writing every GraphQL request yourself.
Buy Button JS An embeddable component library for product listings, Buy Now buttons, collections, and carts. It uses the JS Buy SDK underneath. You need Shopify commerce UI embedded in an existing site.
Checkout APIs Legacy checkout operations. Do not use for a new implementation; Shopify says these APIs no longer function after the 2025-04 sunset.

Shopify’s documentation describes the JS Buy SDK as a JavaScript library based on the Storefront API. The JS Buy SDK guide also notes that it is intended for developers experienced with JavaScript and is not supported by Shopify Support.

2. Choose access and version settings

Use a supported API version

Storefront requests go to one versioned endpoint:

https://{store_name}.myshopify.com/api/{version}/graphql.json

The reference used for this guide is version 2026-04; Shopify’s selector showed 2026-07 as the latest version during research. Pin a supported version in your application, then check Shopify’s version selector before upgrading. API versions change, so do not assume a version remains current indefinitely.

Pick the correct token mode

Mode Where it belongs Important details
Public access token Browser or mobile requests where the token is visible to the buyer. Use only for operations appropriate for buyer-visible clients.
Private access token Your server. Keep it secret. Never put it in browser JavaScript, HTML, or a public repository.
Tokenless access Only the subset of Storefront features Shopify allows without a token. Queries have a complexity limit of 1,000.

Token-based access is required for all Storefront API features. Shopify lists product tags, metaobjects and metafields, menus, and customers among features that require a token. For private, buyer-originated requests, forward the buyer’s IP in the case-sensitive Shopify-Storefront-Buyer-IP header. Shopify says omitting it can cause throttling, weaker bot protection, or an unauthenticated checkout flow.

Prepare the shop

  1. Use a development or production Shopify store.
  2. Create catalog items and variants.
  3. Create a custom app and generate the Storefront access credentials appropriate for your client/server model.
  4. Make the products and collections available to the custom app before querying them.
  5. Record the shop’s .myshopify.com hostname and the API version you will pin.

3. Make a first Storefront GraphQL request

The Storefront API is GraphQL-only. Send a POST request with a JSON body containing query and, when needed, variables. This example lists products and their first available variants.

curl https://your-shop.myshopify.com/api/2026-04/graphql.json \
  -X POST \
  -H 'Content-Type: application/json' \
  -H 'X-Shopify-Storefront-Access-Token: YOUR_STOREFRONT_TOKEN' \
  --data-binary @- <<'JSON'
{
  "query": "query Products($first: Int!) { products(first: $first) { nodes { id title handle variants(first: 10) { nodes { id title availableForSale price { amount currencyCode } } } } } }",
  "variables": { "first": 10 }
}
JSON

A successful GraphQL response can still contain an errors array, so check both the HTTP status and the response body. Product availability, publication, token permissions, and API version all affect what appears.

4. Create a cart and hand off to checkout

A Storefront Cart is the buyer’s purchase-session object. Create it with optional merchandise lines, then use its returned checkoutUrl to send the buyer to Shopify-hosted web checkout.

const endpoint = 'https://your-shop.myshopify.com/api/2026-04/graphql.json';
const token = process.env.SHOPIFY_STOREFRONT_TOKEN;

const mutation = `
  mutation CreateCart($input: CartInput) {
    cartCreate(input: $input) {
      cart {
        id
        checkoutUrl
        lines(first: 20) {
          nodes {
            id
            quantity
            merchandise {
              ... on ProductVariant { id title }
            }
          }
        }
      }
      userErrors { field message }
      warnings { code message }
    }
  }
`;

const variables = {
  input: {
    lines: [
      { merchandiseId: 'gid://shopify/ProductVariant/VARIANT_ID', quantity: 1 }
    ],
    attributes: [{ key: 'source', value: 'custom-storefront' }]
  }
};

const response = await fetch(endpoint, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Shopify-Storefront-Access-Token': token,
    // Include this for buyer-originated private server requests.
    'Shopify-Storefront-Buyer-IP': buyerIp
  },
  body: JSON.stringify({ query: mutation, variables })
});

const body = await response.json();
if (!response.ok || body.errors?.length) throw new Error(JSON.stringify(body.errors));
const result = body.data.cartCreate;
if (result.userErrors.length) throw new Error(JSON.stringify(result.userErrors));
if (result.warnings.length) console.warn(result.warnings);
if (!result.cart?.checkoutUrl) throw new Error('Shopify did not return checkoutUrl');

// In a browser: window.location.assign(result.cart.checkoutUrl);
console.log(result.cart.checkoutUrl);

Use Cart API mutations to add, update, or remove lines as the buyer changes quantities and variants. Always inspect userErrors and warnings. A mutation can return HTTP 200 while reporting a business error in the GraphQL payload.

Cart input options

  • lines: merchandise variant IDs and quantities.
  • Discount codes and gift-card codes.
  • buyerIdentity, including the buyer’s country and customer information where supported.
  • Custom attributes for order context.

Keep the cart ID on the client or in your session store, but treat checkout URLs as buyer-specific values. Re-fetch or recreate a cart when a line becomes unavailable, a variant is deleted, or the cart expires according to Shopify’s behavior.

5. Complete JavaScript example with the JS Buy SDK

The SDK reduces the amount of GraphQL you write, while the underlying flow remains product query, cart creation, and checkout URL redirect. Follow Shopify’s JS Buy SDK documentation and its repository instructions for installation and current package details.

import Client from 'shopify-buy';

const client = Client.buildClient({
  domain: 'your-shop.myshopify.com',
  storefrontAccessToken: import.meta.env.VITE_SHOPIFY_STOREFRONT_TOKEN,
  apiVersion: '2026-04'
});

const products = await client.product.fetchAll(10);
for (const product of products) {
  console.log(product.title, product.variants.map(v => ({ id: v.id, title: v.title })));
}

const product = products[0];
const variant = product.variants.find(v => v.available);
if (!variant) throw new Error('No available variant');

const checkout = await client.checkout.create();
const updated = await client.checkout.addLineItems(checkout.id, [
  { variantId: variant.id, quantity: 1 }
]);

window.location.assign(updated.webUrl);

SDK method names and returned object shapes can change with package versions. Pin the package version, read the current SDK guide, and validate the implementation against your own store.

6. Direct API examples in cURL, Python, and Node.js

cURL: query a product

curl https://your-shop.myshopify.com/api/2026-04/graphql.json \
  -X POST \
  -H 'Content-Type: application/json' \
  -H 'X-Shopify-Storefront-Access-Token: YOUR_STOREFRONT_TOKEN' \
  -d '{
    "query":"query Product($handle: String!) { productByHandle(handle: $handle) { id title handle } }",
    "variables":{"handle":"example-product"}
  }'

Python: query products and create a cart

import os
import requests

endpoint = 'https://your-shop.myshopify.com/api/2026-04/graphql.json'
headers = {
    'Content-Type': 'application/json',
    'X-Shopify-Storefront-Access-Token': os.environ['SHOPIFY_STOREFRONT_TOKEN'],
}

def graphql(query, variables=None, buyer_ip=None):
    request_headers = dict(headers)
    if buyer_ip:
        request_headers['Shopify-Storefront-Buyer-IP'] = buyer_ip
    response = requests.post(
        endpoint,
        headers=request_headers,
        json={'query': query, 'variables': variables or {}},
        timeout=30,
    )
    response.raise_for_status()
    body = response.json()
    if body.get('errors'):
        raise RuntimeError(body['errors'])
    return body['data']

products = graphql('''
query Products($first: Int!) {
  products(first: $first) { nodes { id title } }
}
''', {'first': 10})
print(products)

cart = graphql('''
mutation CreateCart($input: CartInput) {
  cartCreate(input: $input) {
    cart { id checkoutUrl }
    userErrors { field message }
    warnings { code message }
  }
}
''', {'input': {'lines': [
    {'merchandiseId': 'gid://shopify/ProductVariant/VARIANT_ID', 'quantity': 1}
]}})['cartCreate']
if cart['userErrors']:
    raise RuntimeError(cart['userErrors'])
print(cart['cart']['checkoutUrl'])

Node.js: direct GraphQL POST

const endpoint = 'https://your-shop.myshopify.com/api/2026-04/graphql.json';
const query = `query Products($first: Int!) {
  products(first: $first) { nodes { id title handle } }
}`;

const res = await fetch(endpoint, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Shopify-Storefront-Access-Token': process.env.SHOPIFY_STOREFRONT_TOKEN
  },
  body: JSON.stringify({ query, variables: { first: 10 } })
});
const body = await res.json();
if (!res.ok || body.errors) throw new Error(JSON.stringify(body.errors || body));
console.log(body.data.products.nodes);

7. Buy Button JS versus the JS Buy SDK

Choose the JS Buy SDK when you need low-level control over data fetching, cart state, and your own components. Choose Buy Button JS when you want Shopify’s embeddable product, collection, Buy Now, and cart presentation. Shopify’s Buy Button JS guidance warns that older builds depended on deprecated Checkout APIs. For package users, Shopify directs maintainers toward @shopify/buy-button-js ^3.0.4; CDN users should use the latest script path or generate a new Buy Button. Treat those as Shopify documentation instructions and verify your own store and package configuration after upgrading.

8. Limits, performance, and reliability

  • Query complexity: tokenless access has a complexity cap of 1,000. Request only fields you render and paginate connections.
  • Buyer traffic: Shopify documents no fixed requests-per-minute ceiling for real buyer traffic, but automated traffic and checkout creation are limited.
  • Checkout throttling: checkout creation may return HTTP 200 with a Throttled result. Queue checkout creation and retry with exponential backoff rather than treating every 200 response as success.
  • Security rejection: Shopify documents a 430 Shopify Security Rejection for requests it considers malicious. Inspect request patterns, avoid uncontrolled automation, and preserve the buyer IP header on private buyer-originated traffic.
  • Payload size: keep queries narrow, avoid deeply nested fields you do not need, and paginate products, variants, collections, and cart lines.
  • Failure handling: distinguish transport failures, GraphQL errors, mutation userErrors, warnings, and missing checkout URLs in logs and metrics.
  • Retries: retry transient network failures with bounded exponential backoff. Do not blindly retry mutations unless your operation is idempotent or you can safely detect a duplicate.

9. Common errors and fixes

Error or symptom Likely cause Fix
401 or unauthorized response Missing, invalid, or wrong type of Storefront token. Check the header name, token, shop hostname, and app permissions. Keep private tokens server-side.
Product is missing The product is unpublished, unavailable to the custom app, or queried with the wrong handle/version. Make it available to the app, verify publication and sales-channel availability, and query by the returned ID or correct handle.
userErrors from cartCreate Invalid merchandise ID, quantity, discount, buyer identity, or cart input. Display/log each error’s field and message; validate variant availability before mutation.
Checkout URL is empty The mutation returned errors, warnings, or no cart. Check GraphQL errors, userErrors, warnings, and the cart object before redirecting.
200 response marked throttled Checkout creation or automated traffic limits. Read the response body, queue the operation, and retry with exponential backoff.
430 Shopify Security Rejection Shopify classified the request pattern as malicious. Stop aggressive retries, review automation, use valid access, and forward buyer IP for private buyer traffic.
Old Buy Button stops working The build depended on sunset Checkout APIs. Follow Shopify’s current Buy Button JS update guidance and migrate to the Storefront Cart flow.
GraphQL validation error after an upgrade Field or input changed between API versions. Pin a supported version, consult that version’s reference, update the query, and run a staging checkout.

10. Migration checklist from legacy Checkout APIs

  1. Inventory every legacy checkout mutation and webhook dependency.
  2. Replace checkout creation with cartCreate and related Cart API mutations.
  3. Store the cart ID while the buyer edits the order.
  4. Redirect to the cart’s checkoutUrl for web checkout.
  5. For native mobile apps, evaluate Shopify’s Checkout Kit path separately; it is distinct from the normal website handoff.
  6. Pin and periodically review your Storefront API version.
  7. Test unavailable variants, discount failures, throttling, duplicate clicks, expired sessions, and a complete checkout redirect.

11. Or skip the browser setup

If your goal is to capture a Shopify storefront or checkout page for documentation, QA, or a preview, ScreenshotNeo provides a single screenshot request instead of maintaining browser automation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://your-shop.myshopify.com -o shop.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://your-shop.myshopify.com"}, timeout=90)
open("shop.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://your-shop.myshopify.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing result. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. You get 1,000 screenshots a month free with no card; paid plans start at $5 for 3,000 shots. See the ScreenshotNeo documentation, then sign up for the free plan.

12. FAQ

Is Shopify’s Buy Button API deprecated?

Buy Button JS is still documented, but older builds depended on deprecated Checkout APIs. Follow Shopify’s current Buy Button JS update instructions and use the Storefront Cart flow for new work.

Can I use the Storefront API from browser JavaScript?

Yes, with public access intended for browser or mobile contexts. Never expose a private token. Limit browser queries to data that is safe for buyers to receive.

Do I need the JS Buy SDK?

No. You can send GraphQL POST requests directly to the Storefront endpoint. The SDK is a convenience layer for common JavaScript commerce operations.

Does Storefront API checkout stay on my domain?

The standard web flow redirects the buyer to Shopify’s hosted checkout using the cart’s checkoutUrl. Native mobile implementations may use Checkout Kit as Shopify’s separate mobile path.

What should I log when a cart fails?

Log the API version, operation name, HTTP status, GraphQL errors, mutation userErrors, warnings, and a request correlation ID. Do not log private tokens or unnecessary buyer data.