The Shopify Admin API is the backbone of modern e-commerce automation, custom integrations, and store management tools. Whether you're building a marketing automation platform, inventory management system, or custom reporting dashboard, understanding the Admin API is essential for any developer working in the Shopify ecosystem.
This comprehensive guide walks you through everything you need to know: from choosing between REST and GraphQL to implementing production-grade solutions with proper error handling and performance optimization.
What is the Shopify Admin API?
The Shopify Admin API provides programmatic access to Shopify stores, allowing you to read and modify store data without using the Shopify admin interface. It powers everything from automated inventory management to custom analytics platforms.
Key Capabilities
The Admin API lets you:
- Manage products: Create, update, delete products and variants with images, variants, and metadata
- Process orders: Track orders, update fulfillment status, create refunds, and manage order notes
- Manage customers: Create customer records, track purchase history, manage customer groups
- Handle inventory: Track inventory levels across locations, implement low-stock alerts
- Manage fulfillment: Create and update fulfillment orders, track shipment status
- Webhooks: Subscribe to store events for real-time notifications
- Analytics: Access sales data, customer information, and performance metrics
Use Cases
Common Shopify Admin API implementations include:
- Inventory Sync Tools - Synchronize inventory across multiple sales channels
- Marketing Automation - Segment customers, create targeted campaigns based on purchase history
- Analytics Dashboards - Build custom reporting on sales, traffic, and customer data
- Order Management Systems - Create custom fulfillment workflows
- ERP Integration - Connect Shopify with enterprise resource planning systems
- Marketplace Connectors - Sync Shopify inventory with Amazon, eBay, Etsy
- Customer Data Platforms - Aggregate customer data for unified marketing
- Custom Apps - Build store-specific functionality and automations
REST API vs GraphQL API
Shopify offers two API approaches, each with strengths. Understanding when to use each is crucial for efficient development.
REST API Overview
The REST API follows traditional HTTP methods (GET, POST, PUT, DELETE) organized into resource endpoints.
REST Example - Get Product:
// REST API - GET /admin/api/2024-01/products/{id}.json
fetch('https://yourstore.myshopify.com/admin/api/2024-01/products/123456.json', {
headers: {
'X-Shopify-Access-Token': 'your_access_token'
}
})
.then(res => res.json())
.then(data => console.log(data.product));
REST Strengths:
- Simple and intuitive for developers familiar with HTTP APIs
- Better for straightforward CRUD operations
- Excellent documentation and tutorials
- Easier debugging with standard HTTP tools
- Good for batch operations using bulk operations
REST Limitations:
- Over-fetching: Returns all fields even if you only need a few
- Under-fetching: Getting related data requires multiple requests
- Less flexible for complex queries
- Endpoint proliferation for different resource combinations
GraphQL API Overview
GraphQL provides a query language that returns exactly the data you request.
GraphQL Example - Get Product:
# GraphQL Query
query {
product(id: "gid://shopify/Product/123456") {
title
handle
variants(first: 5) {
edges {
node {
id
title
price
}
}
}
}
}
GraphQL Strengths:
- Query exactly what you need - no over-fetching
- Get related data in a single request - no under-fetching
- Strongly typed schema enables IDE autocomplete
- More efficient for mobile and limited bandwidth
- Easier to evolve API without breaking changes
- Better performance with fewer network requests
GraphQL Limitations:
- Steeper learning curve for REST developers
- More complex query debugging
- Requires proper query optimization to avoid N+1 problems
- Caching is more complex than HTTP caching
Comparison Table
| Factor | REST API | GraphQL API |
|---|---|---|
| Learning Curve | Easy for HTTP developers | Moderate for REST developers |
| Data Fetching | Over/under fetching | Precise data fetching |
| Network Requests | Multiple requests for relations | Single request for complex data |
| Query Complexity | Limited | Highly flexible |
| Caching | HTTP caching friendly | Application-level caching |
| Error Handling | HTTP status codes | GraphQL errors in response |
| Rate Limiting | Simpler point system | Point-based with query cost |
| Documentation | Extensive tutorials | Good reference docs |
| Use Case | Simple CRUD, migrations | Complex queries, real-time |
When to Use Each
Use REST API when:
- Building simple integrations (basic product imports)
- Migrating data from another platform
- Building tools where documentation abundance matters
- Your needs are primarily CRUD operations
- You need simple HTTP caching strategies
Use GraphQL when:
- Building custom apps requiring multiple related resources
- Optimizing for bandwidth and network requests
- Creating mobile or low-bandwidth applications
- You need maximum flexibility in data requests
- Building real-time features with webhooks
Authentication Methods
Shopify offers different authentication approaches depending on your integration type and use case. Understanding these is critical before writing your first API call.
1. Public Apps (OAuth 2.0)
Public apps are installable on any Shopify store and use OAuth 2.0 for authentication.
OAuth Flow:
1. User clicks "Install" in your app listing
2. Shopify redirects to your authorization endpoint with code
3. Your app exchanges code for access token
4. Store access token securely and use for API calls
5. Access token never leaves server (never expose client-side)
Implementation:
// Express.js example
app.get('/auth/callback', async (req, res) => {
const { code, hmac, shop, timestamp } = req.query;
// Verify HMAC to ensure request came from Shopify
const hmacVerified = verifyShopifyHmac(req.query);
if (!hmacVerified) return res.status(401).send('Invalid HMAC');
// Exchange code for access token
const accessToken = await exchangeCodeForToken(shop, code);
// Store token securely (database, environment, etc.)
await saveAccessToken(shop, accessToken);
res.redirect(`https://${shop}/admin`);
});
Best for:
- Apps sold in Shopify App Store
- Multi-store integrations
- Apps where merchants control permissions
2. Custom Apps (Admin-Generated Access Tokens)
Custom apps are store-specific apps created directly in the Shopify Admin.
Setup:
- In Shopify Admin: Settings > Apps and integrations > App and sales channel settings
- Click "Develop apps"
- Create new app and set scopes
- Generate access token (shown once - copy immediately)
- Use token for API calls
Implementation:
// Node.js fetch example
const shopName = 'your-store';
const accessToken = 'shpat_xxxxx...';
const query = `
query {
products(first: 10) {
edges {
node {
id
title
}
}
}
}
`;
fetch(`https://${shopName}.myshopify.com/admin/api/2024-01/graphql.json`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Shopify-Access-Token': accessToken
},
body: JSON.stringify({ query })
})
.then(res => res.json())
.then(data => console.log(data));
Best for:
- Private integrations for a single store
- Rapid development and testing
- Store-specific custom functionality
- Internal tools and automations
3. Private Apps (Legacy)
Private apps use API credentials (API key + password) instead of tokens. This method is being deprecated in favor of custom apps.
Not recommended for new projects - use custom apps instead.
Required Scopes
Apps must request specific scopes to access data. Common scopes include:
write_products # Create/modify products
read_products # Read product data
write_orders # Modify orders
read_orders # Read order data
write_customers # Create/modify customers
read_customers # Read customer data
write_inventory # Modify inventory
read_inventory # Read inventory
write_fulfillments # Create fulfillments
read_fulfillments # Read fulfillment data
write_analytics # Write analytics data
read_analytics # Read analytics data
Request only the scopes you need - merchants are more likely to trust apps with minimal permissions.
Common Operations
Here are the most frequent Admin API operations you'll need, with working examples.
Managing Products
Create a Product:
mutation CreateProduct($input: ProductInput!) {
productCreate(input: $input) {
product {
id
title
handle
}
userErrors {
field
message
}
}
}
Variables:
{
"input": {
"title": "Custom T-Shirt",
"productType": "Apparel",
"vendor": "Your Brand",
"bodyHtml": "<p>Premium custom t-shirt</p>",
"status": "ACTIVE"
}
}
Update Product Variants:
mutation UpdateVariant($input: ProductVariantInput!) {
productVariantUpdate(input: $input) {
productVariant {
id
price
compareAtPrice
}
userErrors {
field
message
}
}
}
Bulk Product Import:
For importing hundreds or thousands of products, use Shopify's Bulk Operations API:
mutation {
bulkOperationRunMutation(
mutation: "mutation {productCreate(input:{title:\"Test\"})}"
) {
bulkOperation {
id
status
}
userErrors {
message
}
}
}
Managing Orders
Get Order Details:
query GetOrder($id: ID!) {
order(id: $id) {
id
orderNumber
email
createdAt
totalPrice
lineItems(first: 5) {
edges {
node {
id
title
quantity
price
}
}
}
customer {
id
email
firstName
lastName
}
}
}
Update Order Status:
mutation UpdateFulfillment($input: FulfillmentInput!) {
fulfillmentCreate(input: $input) {
fulfillment {
id
status
lineItems(first: 5) {
edges {
node {
id
}
}
}
}
userErrors {
field
message
}
}
}
Create Refund:
mutation CreateRefund($input: RefundInput!) {
refundCreate(input: $input) {
refund {
id
status
totalRefunded
}
userErrors {
field
message
}
}
}
Managing Customers
Create Customer:
mutation CreateCustomer($input: CustomerInput!) {
customerCreate(input: $input) {
customer {
id
email
firstName
lastName
}
userErrors {
field
message
}
}
}
Variables:
{
"input": {
"email": "customer@example.com",
"firstName": "John",
"lastName": "Doe",
"phone": "+1-555-123-4567",
"defaultAddress": {
"address1": "123 Main St",
"city": "San Francisco",
"province": "CA",
"country": "US",
"zip": "94102"
}
}
}
Search Customers:
query SearchCustomers($query: String!) {
customers(first: 10, query: $query) {
edges {
node {
id
email
firstName
lastName
ordersCount
totalSpent
}
}
}
}
Add Customer Tags:
mutation AddCustomerTag($input: CustomerInput!) {
customerUpdate(input: $input) {
customer {
id
tags
}
userErrors {
field
message
}
}
}
Rate Limits and Performance
Understanding Shopify's rate limiting is essential for building scalable integrations. Hit rate limits and your app's functionality suffers.
Rate Limiting System
Shopify uses a bucket-based rate limiting system:
- Each app gets a bucket that refills over time
- API calls consume points from the bucket
- When bucket is empty, requests are rejected with 429 status
- Bucket refills automatically (approximately 2 points/second for most apps)
Point Costs:
- Simple queries (single product): 1 point
- Medium complexity (product + variants + inventory): 2-3 points
- Complex queries (multiple resources): 4-10 points
- Bulk operations: Variable based on size
Monitoring Rate Limits
Shopify returns rate limit info in response headers:
X-Request-Id: abc-123-def
X-Shop-Api-Call-Limit: 32/40
The header format is: current/limit
Check remaining capacity in code:
fetch(url, options)
.then(res => {
const [used, limit] = res.headers
.get('X-Shop-Api-Call-Limit')
.split('/')
.map(Number);
console.log(`Used: ${used}/${limit}`);
console.log(`Remaining: ${limit - used}`);
return res.json();
});
Rate Limit Best Practices
- Implement Exponential Backoff:
async function apiCallWithRetry(fn, maxRetries = 5) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
if (error.status === 429) {
const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s, 8s...
console.log(`Rate limited. Retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
} else {
throw error;
}
}
}
throw new Error('Max retries exceeded');
}
- Batch Operations When Possible:
query GetMultipleProducts($ids: [ID!]!) {
nodes(ids: $ids) {
... on Product {
id
title
}
}
}
- Use Webhooks Instead of Polling:
Instead of constantly polling for order updates, subscribe to webhooks:
// Setup webhook endpoint
app.post('/webhooks/orders/create', (req, res) => {
const { body } = req;
const hmacHeader = req.headers['x-shopify-hmac-sha256'];
if (verifyWebhook(body, hmacHeader)) {
console.log('New order:', body);
res.status(200).send('OK');
} else {
res.status(401).send('Unauthorized');
}
});
- Cache Query Results:
const NodeCache = require('node-cache');
const cache = new NodeCache({ stdTTL: 3600 });
async function getCachedProduct(productId) {
const cached = cache.get(productId);
if (cached) return cached;
const product = await fetchProductFromAPI(productId);
cache.set(productId, product);
return product;
}
- Monitor and Alert:
Track your API usage patterns and set up alerts when approaching limits:
const apiUsagePercentage = used / limit * 100;
if (apiUsagePercentage > 80) {
alertSlack(`API usage at ${apiUsagePercentage.toFixed(1)}%`);
}
Production Best Practices
Building reliable integrations requires more than just knowing the API. Here are essential practices for production applications.
Error Handling
Implement Comprehensive Error Handling:
async function makeApiCall(query, variables) {
try {
const response = await fetch(
`https://${shopName}.myshopify.com/admin/api/2024-01/graphql.json`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Shopify-Access-Token': accessToken
},
body: JSON.stringify({ query, variables })
}
);
if (!response.ok) {
if (response.status === 429) {
throw new RateLimitError('Rate limited - implement backoff');
}
throw new HttpError(`HTTP ${response.status}`);
}
const data = await response.json();
// GraphQL errors
if (data.errors) {
throw new GraphQLError(data.errors);
}
// User errors in mutation
if (data.data?.productCreate?.userErrors?.length) {
throw new ValidationError(data.data.productCreate.userErrors);
}
return data;
} catch (error) {
logger.error('API call failed', { error, query });
throw error;
}
}
Security
- Never expose access tokens client-side
- Use environment variables for credentials
- Verify webhook HMAC signatures
- Rotate custom app tokens regularly
- Use HTTPS for all requests
- Implement request signing for sensitive operations
Monitoring and Logging
// Log all API interactions
function logApiCall(method, endpoint, status, duration) {
logger.info('API call', {
method,
endpoint,
status,
duration_ms: duration,
timestamp: new Date()
});
}
// Monitor error rates
setInterval(() => {
const errorRate = errorCount / totalRequests;
if (errorRate > 0.05) { // Alert if >5% errors
alertTeam(`High error rate: ${(errorRate*100).toFixed(1)}%`);
}
}, 60000);
Testing
Use Shopify's test credentials for development:
// Development
const apiVersion = '2024-01';
const shop = 'development-store';
const accessToken = process.env.SHOPIFY_DEV_TOKEN;
// Production uses Shopify-provided tokens from OAuth flow
Choosing a Shopify API Client Library
Several libraries simplify Admin API integration:
Official Shopify Library:
import shopifyApp from '@shopify/shopify-app-express';
const app = shopifyApp({
apiKey: process.env.SHOPIFY_API_KEY,
apiSecret: process.env.SHOPIFY_API_SECRET,
scopes: ['write_products', 'read_orders']
});
Popular Community Options:
- @shopify/shopify-api - Official JavaScript client
- shopify-python-api - Official Ruby client
- shopifython - Python client
- shopify-go - Go client
Common Integration Challenges
Challenge: Handling Webhook Retries
Shopify retries webhooks if your endpoint doesn't respond with 200 within 5 seconds. Handle retries gracefully:
const deliveryIdCache = new Set();
app.post('/webhook', (req, res) => {
const deliveryId = req.headers['x-shopify-webhook-id'];
// Prevent duplicate processing
if (deliveryIdCache.has(deliveryId)) {
return res.status(200).send('Already processed');
}
// Process webhook
processWebhook(req.body);
deliveryIdCache.add(deliveryId);
res.status(200).send('OK');
});
Challenge: Syncing Large Product Catalogs
For stores with thousands of products, use cursor-based pagination and bulk operations:
query GetProductsPaginated($cursor: String) {
products(first: 250, after: $cursor) {
pageInfo {
hasNextPage
endCursor
}
edges {
node {
id
title
}
}
}
}
Challenge: Real-Time Inventory Updates
Combine webhooks with periodic syncing:
// Webhook for immediate updates
app.post('/webhook/inventory', (req, res) => {
updateLocalInventory(req.body);
res.status(200).send('OK');
});
// Periodic sync for consistency
setInterval(() => {
syncInventoryFromShopify();
}, 3600000); // Every hour
Getting Started: Your First API Call
Ready to build? Here's a minimal working example:
// 1. Set up environment
const shopName = process.env.SHOPIFY_STORE;
const accessToken = process.env.SHOPIFY_ACCESS_TOKEN;
// 2. Define query
const query = `
query {
shop {
name
plan {
displayName
}
}
}
`;
// 3. Make request
const response = await fetch(
`https://${shopName}.myshopify.com/admin/api/2024-01/graphql.json`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Shopify-Access-Token': accessToken
},
body: JSON.stringify({ query })
}
);
const data = await response.json();
console.log(`Connected to ${data.data.shop.name}`);
Next Steps
- Explore the API Documentation - Visit developers.shopify.com/docs/api/admin-rest for complete API reference
- Try GraphQL Explorer - Use Shopify's GraphQL explorer to test queries interactively
- Set up a Custom App - Create your first custom app in the Shopify Admin
- Build Your First Integration - Start with a simple product or order sync
- Implement Webhooks - Move beyond polling to event-driven architecture
- Monitor and Optimize - Track performance and optimize queries
Need Expert Help With Shopify Integration?
The Shopify Admin API is powerful, but building production-grade integrations requires expertise in authentication, error handling, rate limiting, and performance optimization. Whether you're building a custom app, integrating with external systems, or optimizing your store's automation, our team of e-commerce specialists can help.
Get a free Shopify integration consultation - Let's discuss your integration challenges and find the right solution.
Or if you're looking to improve your store's visibility and performance, take a free audit of your store to identify opportunities for growth.
Looking to build a Shopify integration? Consider Shopify Plus for enterprise-grade features, or start with a standard Shopify plan and grow from there. Shopify's ecosystem provides excellent tools and resources to build sophisticated e-commerce applications. The Admin API is the foundation that makes all of it possible - master it, and you unlock unlimited possibilities for store automation and custom functionality. For comprehensive resources on Shopify development, explore their developer documentation.
Last updated: February 21, 2026