The Shopify Storefront API is the gateway to building custom commerce experiences on top of Shopify’s backend. Whether you are building a fully headless storefront with Hydrogen, a mobile application, a custom product configurator, or simply enhancing your Liquid theme with dynamic data fetching, the Storefront API is how you access Shopify’s product catalogue, manage carts, and handle customer authentication programmatically.
Unlike the Admin API (which provides authenticated, full access to all store data), the Storefront API is designed for customer-facing applications. It uses public access tokens, returns only publicly available data, and is optimised for high-throughput storefront traffic. Understanding its capabilities, limitations, and query patterns is essential for any developer working with Shopify beyond basic Liquid themes.
This guide covers the practical implementation patterns we use in our Shopify development work — from basic product queries to complex cart management, pagination, caching, and rate limit management. It complements our GraphQL API guide and the non-developer API overview.
What the Storefront API is
The Storefront API is a GraphQL-only API. There is no REST endpoint. Every interaction — reading products, managing carts, authenticating customers — uses GraphQL queries and mutations sent to a single endpoint:
POST https://{store-name}.myshopify.com/api/2026-01/graphql.json
The API provides access to:
- Products and variants — titles, descriptions, prices, images, metafields, availability, options.
- Collections — manual and automated collections with product filtering and sorting.
- Cart — create carts, add/remove/update line items, apply discount codes, retrieve checkout URLs.
- Customer accounts — login, registration, password reset, address management, order history.
- Pages and blog posts — content pages and blog articles with metafields.
- Shop information — store name, currency, payment settings, shipping countries.
- Localisation — multi-currency pricing, translated content, country-specific availability.
The Storefront API is read-heavy by design. You can read all public catalogue data but can only write to carts and customer accounts. Order creation, product management, and inventory updates require the Admin API.

Authentication and access tokens
The Storefront API uses two types of access tokens:
Public access tokens (Storefront API tokens)
Created in the Shopify admin under Apps → Develop apps. These tokens are safe to expose in client-side JavaScript because they only provide access to public data. They are the standard choice for headless storefronts and JavaScript-enhanced themes.
// Basic fetch with Storefront API token
const response = await fetch(
'https://your-store.myshopify.com/api/2026-01/graphql.json',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Shopify-Storefront-Access-Token': 'your-public-token-here',
},
body: JSON.stringify({
query: `{
shop {
name
primaryDomain { url }
paymentSettings { currencyCode }
}
}`
}),
}
);
const { data } = await response.json();
Private access tokens (delegate access tokens)
Available on Shopify Plus, these provide server-side-only access with additional capabilities including buyer-specific pricing and content. Never expose these in client-side code.
Product data queries
Product queries are the foundation of any Storefront API integration. The key principle is to request only the fields you need — over-fetching wastes both query cost budget and network bandwidth.
// Fetch a single product by handle
const PRODUCT_QUERY = `
query ProductByHandle($handle: String!) {
product(handle: $handle) {
id
title
handle
descriptionHtml
productType
vendor
tags
availableForSale
priceRange {
minVariantPrice { amount currencyCode }
maxVariantPrice { amount currencyCode }
}
compareAtPriceRange {
minVariantPrice { amount currencyCode }
}
images(first: 10) {
edges {
node {
id url altText width height
}
}
}
variants(first: 100) {
edges {
node {
id title availableForSale quantityAvailable
price { amount currencyCode }
compareAtPrice { amount currencyCode }
selectedOptions { name value }
image { url altText }
}
}
}
seo { title description }
metafield(namespace: "custom", key: "care_instructions") {
value type
}
}
}
`;
Collection product listing
// Fetch products in a collection with filtering
const COLLECTION_QUERY = `
query CollectionProducts($handle: String!, $first: Int!, $after: String, $filters: [ProductFilter!]) {
collection(handle: $handle) {
id title description
products(first: $first, after: $after, filters: $filters, sortKey: BEST_SELLING) {
pageInfo { hasNextPage endCursor }
edges {
node {
id title handle availableForSale
priceRange {
minVariantPrice { amount currencyCode }
}
featuredImage { url altText width height }
variants(first: 1) {
edges {
node {
compareAtPrice { amount currencyCode }
}
}
}
}
}
}
}
}
`;

Collection filtering and sorting
The Storefront API supports filtering products within collections by multiple attributes. Filters are combined with AND logic — all conditions must be true.
// Available filter types
const filters = [
// Price range
{ price: { min: 20, max: 100 } },
// Availability
{ available: true },
// Product type
{ productType: "T-Shirt" },
// Vendor
{ productVendor: "Brand Name" },
// Variant option (e.g., size or colour)
{ variantOption: { name: "Size", value: "Large" } },
// Metafield value
{ productMetafield: { namespace: "custom", key: "material", value: "Cotton" } },
// Tag
{ tag: "sale" },
];
Cart management
The Cart API is the Storefront API’s write interface. It replaces the deprecated Checkout API and provides a complete cart management workflow.
// Create a new cart
const CREATE_CART = `
mutation CartCreate($input: CartInput!) {
cartCreate(input: $input) {
cart {
id
checkoutUrl
lines(first: 10) {
edges {
node {
id quantity
merchandise { ... on ProductVariant { id title price { amount currencyCode } } }
}
}
}
cost {
totalAmount { amount currencyCode }
subtotalAmount { amount currencyCode }
totalTaxAmount { amount currencyCode }
}
}
userErrors { field message }
}
}
`;
// Add items to cart
const ADD_TO_CART = `
mutation CartLinesAdd($cartId: ID!, $lines: [CartLineInput!]!) {
cartLinesAdd(cartId: $cartId, lines: $lines) {
cart {
id
lines(first: 50) {
edges { node { id quantity merchandise { ... on ProductVariant { id title } } } }
}
cost { totalAmount { amount currencyCode } }
}
userErrors { field message }
}
}
`;
// Apply discount code
const APPLY_DISCOUNT = `
mutation CartDiscountCodesUpdate($cartId: ID!, $discountCodes: [String!]) {
cartDiscountCodesUpdate(cartId: $cartId, discountCodes: $discountCodes) {
cart {
discountCodes { applicable code }
cost { totalAmount { amount currencyCode } }
}
userErrors { field message }
}
}
`;
Customer account operations
The Storefront API provides customer authentication, registration, and account management. Customer access tokens are returned on login and used for subsequent authenticated requests.
// Customer login
const CUSTOMER_LOGIN = `
mutation CustomerAccessTokenCreate($input: CustomerAccessTokenCreateInput!) {
customerAccessTokenCreate(input: $input) {
customerAccessToken { accessToken expiresAt }
customerUserErrors { code field message }
}
}
`;
// Fetch customer data with access token
const CUSTOMER_QUERY = `
query Customer($customerAccessToken: String!) {
customer(customerAccessToken: $customerAccessToken) {
id firstName lastName email phone
defaultAddress { address1 city province country zip }
orders(first: 10) {
edges {
node {
id orderNumber processedAt financialStatus fulfillmentStatus
totalPrice { amount currencyCode }
}
}
}
}
}
`;

Cursor-based pagination
The Storefront API uses cursor-based pagination (not page numbers). Each connection returns pageInfo with hasNextPage and endCursor. Pass the cursor as the after argument to fetch the next page.
// Paginate through all products
async function fetchAllProducts(storefront) {
let allProducts = [];
let hasNextPage = true;
let cursor = null;
while (hasNextPage) {
const { data } = await storefront.query(PRODUCTS_QUERY, {
variables: { first: 50, after: cursor }
});
const products = data.products.edges.map(e => e.node);
allProducts = [...allProducts, ...products];
hasNextPage = data.products.pageInfo.hasNextPage;
cursor = data.products.pageInfo.endCursor;
}
return allProducts;
}
Caching strategies
Effective caching is critical for Storefront API performance. Without caching, every page load makes multiple API calls, each adding latency.
- HTTP cache headers — Shopify returns
Cache-Controlheaders on Storefront API responses. Respect them in your caching layer. - CDN edge caching — cache API responses at the edge for static or semi-static data (product catalogues change infrequently compared to cart data).
- In-memory caching — for server-side applications, cache frequently accessed data (shop info, navigation, featured collections) in memory with appropriate TTLs.
- Stale-while-revalidate — serve cached data immediately while fetching fresh data in the background. This provides instant responses without serving severely stale data.
// Simple in-memory cache with TTL
const cache = new Map();
async function cachedQuery(key, queryFn, ttlMs = 60000) {
const cached = cache.get(key);
if (cached && Date.now() - cached.timestamp < ttlMs) {
return cached.data;
}
const data = await queryFn();
cache.set(key, { data, timestamp: Date.now() });
return data;
}
// Usage: cache product data for 60 seconds
const product = await cachedQuery(
`product:${handle}`,
() => storefront.query(PRODUCT_QUERY, { variables: { handle } }),
60000
);
Rate limits and cost management
The Storefront API uses a calculated cost model. Each query has a cost based on its complexity — the number of objects requested, the depth of connections, and the first/last argument values.
// Response includes cost information in extensions
{
"extensions": {
"cost": {
"requestedQueryCost": 42,
"actualQueryCost": 38,
"throttleStatus": {
"maximumAvailable": 1000,
"currentlyAvailable": 962,
"restoreRate": 50
}
}
}
}
Strategies for managing query costs:
- Request only the fields you need — remove unused fields from queries.
- Use appropriate
first/lastvalues — do not request 250 products when you only display 12. - Batch related data into single queries — one query requesting product + recommendations is cheaper than two separate queries.
- Cache aggressively — cached responses cost zero API calls.

Practical integration patterns
Based on our Shopify development experience, here are the most common integration patterns:
Pattern 1: Ajax cart in Liquid themes
Use the Storefront API’s Cart mutations from client-side JavaScript to build a dynamic cart drawer without full page reloads. This is the most common non-headless use of the API.
Pattern 2: Custom product search
Build a custom search experience using the Storefront API’s predictive search or product filtering capabilities, providing faster and more relevant results than Shopify’s default search.
Pattern 3: Headless product pages
Build specific high-value pages (product configurators, lookbooks, editorial content) as standalone applications consuming the Storefront API, while keeping the rest of the store on standard Liquid. This is the hybrid approach we often recommend.
Pattern 4: Mobile application
Build native iOS/Android applications using the Storefront API for all commerce functionality — browsing, cart, checkout, and customer accounts.
The Storefront API is the most important API in the Shopify ecosystem for frontend developers. Whether you are building a fully headless storefront or enhancing a Liquid theme with dynamic functionality, understanding its query patterns, caching strategies, and rate limits is essential for building performant, reliable commerce experiences.
If you need help integrating the Storefront API into your Shopify store or building a headless commerce solution, get in touch. API integration is a core part of our Shopify development services.