Talk Shop
Home
Learn More
About Us
Follow Us
Blog
Tools
Newsletter
Join Discord
Join

Community

  • Developers
  • Growth
  • Entrepreneurs
  • Support
  • Experts
  • Tools

Location

123 Mars, Crater City, Red Planet

(WiFi may be spotty)

Hours

Who has time for breaks? We're here 24/7!

Contact

hello@letstalkshop.com

Talk Shop
Talk Shop

Built for real builders. Not affiliated with Shopify Inc.

Home
Privacy
Terms
  1. Home
  2. >Blog
  3. >Shopify Development
  4. >Shopify Admin API: A Developer's Guide to Building Custom Integrations
Shopify Development14 min read

Shopify Admin API: A Developer's Guide to Building Custom Integrations

Master the Shopify Admin API — authentication, GraphQL vs REST, key resources, rate limiting, pagination, webhooks, and practical code examples for building custom integrations.

Talk Shop

Talk Shop

Mar 28, 2026

Shopify Admin API: A Developer's Guide to Building Custom Integrations

In this article

  • What the Shopify Admin API Does and Why It Matters
  • Authentication: Getting Access to Store Data
  • GraphQL vs REST: Which API to Use
  • Core Resources: Products, Orders, Customers, and Inventory
  • Rate Limiting: How to Stay Within Bounds
  • Pagination: Fetching Large Datasets
  • Webhooks: Real-Time Event Notifications
  • Common Use Cases and Integration Patterns
  • Common Mistakes and What to Avoid
  • GraphQL Mutations: Writing Data Back to Shopify
  • Building Your Integration: A Step-by-Step Approach
  • Admin API vs Storefront API: When to Use Each
  • Frequently Asked Questions
  • Start Building with the Shopify Admin API

What the Shopify Admin API Does and Why It Matters

The Shopify Admin API is the programmatic backbone of every Shopify store. It gives developers direct access to manage products, orders, customers, inventory, fulfillment, and virtually every resource that a merchant interacts with through the Shopify admin dashboard. If the Storefront API is the customer-facing front door, the Shopify Admin API is the operations center.

Over 5.6 million live Shopify stores depend on this API — whether through the apps they install, the custom integrations their teams build, or the automation workflows that keep their businesses running. Every time an app syncs inventory to a warehouse, generates a shipping label, or sends a personalized email after purchase, the Admin API is the engine behind it.

This guide covers everything you need to build production-ready integrations: authentication flows, the GraphQL vs REST decision, core resources, rate limiting strategies, pagination patterns, webhooks, and common use cases with code you can adapt. If you are exploring the broader Shopify development ecosystem, this is the API you will spend most of your time with.

Authentication: Getting Access to Store Data

Every Shopify Admin API request requires a valid access token. How you obtain that token depends on whether you are building a public app (distributed through the Shopify App Store) or a custom app (built for a single store).

OAuth 2.0 for Public and Custom Apps

As of 2026, both public and custom apps use OAuth 2.0 for authentication. Shopify discontinued the creation of legacy custom apps directly from the admin — all new apps must be created through the Shopify Developer Dashboard with an OAuth flow.

The OAuth process follows these steps:

  1. Redirect the merchant to Shopify's authorization URL with your app's requested scopes
  2. Merchant approves the permissions your app needs
  3. Shopify redirects back to your app with a temporary authorization code
  4. Exchange the code for a permanent access token via a server-side POST request

Here is the token exchange step in Node.js:

typescripttypescript
const response = await fetch(
  `https://${shop}/admin/oauth/access_token`,
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      client_id: SHOPIFY_API_KEY,
      client_secret: SHOPIFY_API_SECRET,
      code: authorizationCode,
    }),
  }
);

const { access_token, scope } = await response.json();

Access Scopes

Access scopes define what resources your app can read or write. You request them during the OAuth flow, and the merchant must approve them explicitly. Some commonly used scopes:

ScopeAccess
read_products / write_productsProduct catalog, variants, images
read_orders / write_ordersOrder data, fulfillments, refunds
read_customers / write_customersCustomer records, addresses
read_inventory / write_inventoryInventory levels, locations
read_shipping / write_shippingShipping rates, carrier services

Best practice: Request the minimum scopes your app needs. Merchants are more likely to install apps that ask for less access, and Shopify's review team will flag unnecessarily broad scope requests during App Store submission.

Token Exchange (Session Tokens)

For embedded apps, Shopify recommends the token exchange flow instead of the traditional authorization code grant. Your app receives a session token from App Bridge, exchanges it server-side for an access token, and avoids redirect-based OAuth entirely. The Remix app template handles this automatically.

GraphQL vs REST: Which API to Use

Side-by-side dark-themed code structures comparing GraphQL and REST APIs.

Shopify offers two Admin API surfaces — GraphQL and REST — but the direction is clear. The REST Admin API became a legacy API on October 1, 2024, and as of April 2025, all new public apps submitted to the Shopify App Store must use GraphQL exclusively.

Why GraphQL Is the Future

FactorGraphQLREST (Legacy)
Data fetchingRequest exactly the fields you needFixed response shapes, often over-fetching
RelationshipsTraverse connections in a single queryMultiple round-trip requests
Rate limitingCost-based (more efficient)Request-count-based (40/second)
New featuresAll new resources ship here firstNo new features since deprecation
Bulk operationsNative support for large data exportsNot available
App Store requirementRequired for new public appsNot accepted for new submissions

When REST Still Makes Sense

Existing custom apps that use REST endpoints for basic operations can continue running — Shopify has not announced a hard shutdown date. If you have a stable integration doing simple CRUD operations and do not need new features, migrating is not urgent. But for any new development, GraphQL is the only supported path forward.

Your First GraphQL Query

Every Shopify Admin API GraphQL request goes to a single endpoint:

texttext
POST https://{store}.myshopify.com/admin/api/2026-01/graphql.json

Include your access token as a header:

typescripttypescript
const response = await fetch(
  `https://${shop}/admin/api/2026-01/graphql.json`,
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Shopify-Access-Token": accessToken,
    },
    body: JSON.stringify({
      query: `{
        shop {
          name
          primaryDomain { url }
          plan { displayName }
        }
      }`,
    }),
  }
);

const { data } = await response.json();
console.log(data.shop.name); // "My Store"

Core Resources: Products, Orders, Customers, and Inventory

The Shopify Admin API exposes dozens of resource types, but four account for the majority of integration work. Understanding their data models and relationships is essential for productive development.

Products and Variants

Products are the most queried resource in the Admin API. Each product contains variants (size/color combinations), images, metafields, and collection associations.

graphqlgraphql
query GetProducts {
  products(first: 10) {
    edges {
      node {
        id
        title
        status
        totalVariants
        variants(first: 5) {
          edges {
            node {
              id
              title
              price
              inventoryQuantity
              sku
            }
          }
        }
        images(first: 1) {
          edges {
            node {
              url
              altText
            }
          }
        }
      }
    }
  }
}

Key detail: Shopify supports up to 2,000 variants per product (up from 100 in earlier API versions). This change is only available through the GraphQL API — another reason to move off REST.

Orders

Orders include line items, shipping details, payment status, fulfillment records, and refund history. A single order query can pull all of this in one request:

graphqlgraphql
query GetOrder($id: ID!) {
  order(id: $id) {
    name
    displayFinancialStatus
    displayFulfillmentStatus
    totalPriceSet { shopMoney { amount currencyCode } }
    lineItems(first: 50) {
      edges {
        node {
          title
          quantity
          variant { sku }
          originalUnitPriceSet { shopMoney { amount } }
        }
      }
    }
    shippingAddress {
      city
      provinceCode
      countryCodeV2
    }
  }
}

Customers

Customer records store contact information, order history, tags for segmentation, and metafields for custom data. The customers connection supports filtering by email, tag, or creation date.

Inventory

Inventory management in Shopify is location-aware. Each variant tracks inventory levels independently across multiple locations (warehouses, retail stores, third-party fulfillment centers). Use the inventoryAdjustQuantities mutation to update stock levels:

graphqlgraphql
mutation AdjustInventory($input: InventoryAdjustQuantitiesInput!) {
  inventoryAdjustQuantities(input: $input) {
    inventoryAdjustmentGroup {
      reason
      changes {
        name
        delta
      }
    }
    userErrors { field message }
  }
}

Rate Limiting: How to Stay Within Bounds

A close-up of a server rack indicator panel showing data flow and a rate limit spike.

Rate limiting is where most developers first run into trouble with the Shopify Admin API. The GraphQL and REST APIs use fundamentally different throttling models, and understanding the GraphQL model is critical for building reliable integrations.

GraphQL: Calculated Query Cost

The GraphQL Admin API uses a calculated query cost system based on a leaky bucket algorithm. Instead of counting requests, Shopify assigns a point cost to each query based on the fields and connections you request.

Plan TierBucket SizeRestore Rate
Standard1,000 points50 points/second
Advanced2,000 points100 points/second
Shopify Plus10,000 points500 points/second

Every GraphQL response includes a cost extension showing both the requested cost (estimated before execution) and the actual cost (calculated during execution). If the actual cost is lower than requested, the difference is refunded to your bucket:

jsonjson
{
  "extensions": {
    "cost": {
      "requestedQueryCost": 42,
      "actualQueryCost": 12,
      "throttleStatus": {
        "maximumAvailable": 1000,
        "currentlyAvailable": 988,
        "restoreRate": 50
      }
    }
  }
}

Pro tip: Add the Shopify-GraphQL-Cost-Debug: 1 header to any request to see a field-by-field breakdown of where your query cost comes from. This is invaluable for optimizing expensive queries.

REST: Request-Based Throttling

The legacy REST API uses a simpler model — 40 requests per second per app per store, with a bucket size of 80. Each request costs one unit regardless of complexity.

Handling Throttle Errors

When you exceed your rate limit, Shopify returns a THROTTLED error in GraphQL or a 429 Too Many Requests in REST. Implement exponential backoff:

typescripttypescript
async function shopifyGraphQL(query: string, variables?: Record<string, unknown>) {
  let retries = 0;
  const maxRetries = 3;

  while (retries < maxRetries) {
    const response = await fetch(endpoint, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-Shopify-Access-Token": accessToken,
      },
      body: JSON.stringify({ query, variables }),
    });

    const json = await response.json();

    if (json.errors?.[0]?.extensions?.code === "THROTTLED") {
      const waitTime = Math.pow(2, retries) * 1000;
      await new Promise((resolve) => setTimeout(resolve, waitTime));
      retries++;
      continue;
    }

    return json;
  }

  throw new Error("Max retries exceeded for Shopify API");
}

Pagination: Fetching Large Datasets

Shopify's Admin API uses cursor-based pagination exclusively. Every connection (products, orders, customers) returns a pageInfo object with cursors for navigating through results.

Forward Pagination Pattern

graphqlgraphql
query GetProducts($cursor: String) {
  products(first: 50, after: $cursor) {
    edges {
      node {
        id
        title
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

Loop through pages by passing endCursor as the $cursor variable until hasNextPage returns false. Each page request costs points against your rate limit bucket, so factor pagination into your throttle strategy.

Bulk Operations for Large Exports

When you need to export thousands (or millions) of records, individual paginated queries become impractical. Shopify's bulk operations solve this by running your query asynchronously on Shopify's infrastructure and providing a downloadable JSONL file when complete.

graphqlgraphql
mutation BulkExportProducts {
  bulkOperationRunQuery(
    query: """
    {
      products {
        edges {
          node {
            id
            title
            variants {
              edges {
                node {
                  sku
                  inventoryQuantity
                }
              }
            }
          }
        }
      }
    }
    """
  ) {
    bulkOperation {
      id
      status
    }
    userErrors { field message }
  }
}

Poll the operation status, and when it completes, download the results from the provided URL. As of API version 2026-01, apps can run up to five concurrent bulk query operations per store — a significant improvement from the previous limit of one.

MethodBest ForRate Limit Impact
Paginated queriesSmall datasets (< 1,000 records)Each page costs query points
Bulk operationsLarge exports (1,000+ records)Minimal — runs asynchronously

Webhooks: Real-Time Event Notifications

An illustrated data pipeline showing the flow of physical ecommerce events triggered by webhooks.

Instead of polling the Admin API for changes, webhooks push event notifications to your app in real time. Whenever a product is updated, an order is placed, or a customer record changes, Shopify sends an HTTP POST to your registered endpoint.

Subscribing to Webhooks via GraphQL

Use the webhookSubscriptionCreate mutation to register a webhook:

graphqlgraphql
mutation CreateWebhook {
  webhookSubscriptionCreate(
    topic: ORDERS_CREATE
    webhookSubscription: {
      callbackUrl: "https://your-app.com/webhooks/orders"
      format: JSON
    }
  ) {
    webhookSubscription {
      id
      topic
      endpoint {
        ... on WebhookHttpEndpoint {
          callbackUrl
        }
      }
    }
    userErrors { field message }
  }
}

Common Webhook Topics

TopicFires When
ORDERS_CREATEA new order is placed
ORDERS_UPDATEDAny order field changes
ORDERS_PAIDPayment is captured
PRODUCTS_UPDATEA product or variant is modified
PRODUCTS_DELETEA product is removed
INVENTORY_LEVELS_UPDATEStock levels change at any location
CUSTOMERS_CREATEA new customer account is created
APP_UNINSTALLEDA merchant removes your app

Verifying Webhook Authenticity

Every webhook request includes an X-Shopify-Hmac-Sha256 header. Always verify this signature before processing the payload — otherwise your endpoint is vulnerable to spoofed requests:

typescripttypescript
import crypto from "crypto";

function verifyWebhook(body: string, hmacHeader: string): boolean {
  const hash = crypto
    .createHmac("sha256", SHOPIFY_API_SECRET)
    .update(body, "utf8")
    .digest("base64");

  return crypto.timingSafeEqual(
    Buffer.from(hash),
    Buffer.from(hmacHeader)
  );
}

Webhook Delivery and Retries

Shopify expects your endpoint to respond with a 2xx status within 5 seconds. If it does not, Shopify retries delivery up to 19 times over 48 hours with increasing intervals. Design your webhook handler to acknowledge receipt immediately and process the payload asynchronously — never do heavy computation inside the response cycle.

Common Use Cases and Integration Patterns

The Shopify Admin API powers a wide range of integrations. Here are the patterns that cover the majority of real-world use cases, and that you will likely build variations of in your own app development work.

Inventory Sync

Syncing inventory between Shopify and an external system (ERP, warehouse management, POS) is one of the most common Admin API integrations. The pattern involves:

  1. Initial sync — bulk export all inventory levels using a bulk operation
  2. Ongoing sync — subscribe to INVENTORY_LEVELS_UPDATE webhooks for real-time changes
  3. Write-back — use inventoryAdjustQuantities to push external changes back to Shopify

Order Fulfillment Automation

When an order is placed, your integration can automatically create fulfillment records, generate shipping labels, and notify the customer:

graphqlgraphql
mutation FulfillOrder($fulfillment: FulfillmentInput!) {
  fulfillmentCreate(fulfillment: $fulfillment) {
    fulfillment {
      id
      status
      trackingInfo {
        number
        url
      }
    }
    userErrors { field message }
  }
}

Custom Reporting and Analytics

The Admin API provides access to raw order, product, and customer data that you can aggregate into custom reports beyond what the Shopify admin offers. Combine orders queries with date filters to build revenue breakdowns, product performance reports, or customer lifetime value calculations. For large stores, use bulk operations to export the data to your own analytics infrastructure.

Common Mistakes and What to Avoid

Building with the Shopify Admin API is straightforward once you understand the patterns, but these mistakes trip up developers consistently.

Over-Fetching Data

GraphQL lets you request exactly the fields you need — but many developers copy example queries without trimming unused fields. Every additional field adds to your query cost. Audit your queries and remove any field you are not actively using.

Ignoring Webhook Verification

Skipping the HMAC signature check on incoming webhooks is a security vulnerability. Attackers can send fake payloads to your endpoint to trigger unintended actions. Always verify the X-Shopify-Hmac-Sha256 header.

Not Handling Pagination

If your integration queries products or orders without paginating, you will only get the first page of results and silently miss the rest. Always check pageInfo.hasNextPage and loop until all pages are consumed.

Hardcoding API Versions

Shopify releases new API versions quarterly and deprecates old ones after a year. Hardcoding a version like 2024-04 means your integration will eventually break. Store the API version in a configuration variable and update it during your regular maintenance cycle.

Blocking on Webhook Processing

Doing heavy computation inside your webhook handler (database writes, external API calls, file processing) causes timeouts and failed deliveries. Accept the webhook with a 200 response immediately, queue the payload, and process it asynchronously.

MistakeConsequenceFix
Over-fetching fieldsFaster rate limit exhaustionTrim queries to needed fields only
Skipping HMAC checkSecurity vulnerabilityVerify every webhook signature
No pagination handlingMissing dataAlways loop through pageInfo
Hardcoded API versionBreaking changes on deprecationUse a configurable version string
Blocking webhook handlerMissed deliveries, retriesAcknowledge immediately, process async

GraphQL Mutations: Writing Data Back to Shopify

An isometric scene of a robotic arm writing a data block into a stylized warehouse structure.

Reading data is only half the story. Most integrations also need to create, update, or delete resources. Mutations in the Shopify Admin API follow a consistent pattern.

Creating a Product

graphqlgraphql
mutation CreateProduct($product: ProductCreateInput!) {
  productCreate(product: $product) {
    product {
      id
      title
      handle
      status
    }
    userErrors {
      field
      message
    }
  }
}

Variables:

jsonjson
{
  "product": {
    "title": "Classic Cotton Tee",
    "productType": "T-Shirts",
    "status": "DRAFT",
    "variants": [
      {
        "price": "29.99",
        "sku": "CCT-BLK-M",
        "inventoryQuantities": [
          {
            "locationId": "gid://shopify/Location/12345",
            "name": "available",
            "quantity": 100
          }
        ]
      }
    ]
  }
}

Updating Existing Resources

Every mutation that modifies an existing resource requires the resource's global ID (the gid://shopify/... format). Always check the userErrors array in the response — a successful HTTP response does not mean the mutation succeeded:

typescripttypescript
const { data } = await shopifyGraphQL(mutation, variables);

if (data.productUpdate.userErrors.length > 0) {
  console.error("Mutation failed:", data.productUpdate.userErrors);
  // Handle validation errors — missing required fields, invalid values, etc.
}

Building Your Integration: A Step-by-Step Approach

Whether you are building a standalone app or a custom integration for a single store, the development workflow follows the same sequence. If you want a deeper walkthrough of the scaffolding process, our guide on how to create a Shopify app from scratch covers the full setup.

Step 1: Define Your Scope

Before writing code, list the exact resources your integration needs to read and write. Map each to the corresponding access scope. This determines your OAuth configuration and what the merchant sees during installation.

Step 2: Set Up Authentication

Use the Shopify CLI and the official Remix template for the fastest path to a working OAuth flow. For custom integrations that do not need a full app framework, implement the authorization code grant manually.

Step 3: Build and Test Queries

Use Shopify's built-in GraphiQL explorer to prototype your queries against a development store before writing application code. This lets you validate field availability, test filters, and check query costs interactively.

Step 4: Implement Webhook Handlers

Register webhooks for the events your integration depends on. Build your handlers with immediate acknowledgment and async processing from day one — retrofitting this later is painful.

Step 5: Handle Rate Limits and Errors

Implement the exponential backoff pattern from the rate limiting section above. Log your query costs and throttle status so you can monitor usage in production.

Step 6: Test with Real Data

Shopify development stores have limitations (no real payment processing, limited data volume). Before launching, test against a store with realistic data volume to catch pagination edge cases and rate limit issues.

Admin API vs Storefront API: When to Use Each

A close-up of two tablets displaying connected Admin and Storefront APIs.

Understanding the boundary between the Admin API and the Storefront API is essential for architecting your integration correctly.

CharacteristicAdmin APIStorefront API
PurposeBackend store managementCustomer-facing experiences
AuthenticationOAuth access token (server-side)Storefront access token (can be public)
Rate limitsCost-based, 50 pts/sec (standard)No rate limits on reads
AccessFull CRUD on all resourcesRead products, manage carts, initiate checkout
Typical consumersApps, integrations, automationHeadless storefronts, mobile apps, kiosks
Sensitive dataYes — orders, customers, financialsNo — only public catalog data

Use the Admin API when you need to manage store operations: creating products, fulfilling orders, adjusting inventory, managing customers, or reading financial data.

Use the Storefront API when you are building a customer-facing experience: displaying products, managing shopping carts, or routing customers to checkout. Our Storefront API getting started guide walks through the full setup.

Many production architectures use both APIs together. A headless storefront reads product data through the Storefront API for performance and then uses the Admin API server-side for order management, fulfillment, and reporting.

Frequently Asked Questions

Is the Shopify REST Admin API still available?

Yes. The REST Admin API was marked as legacy in October 2024, but existing integrations continue to function. However, no new features are being added to REST, and all new public apps must use GraphQL as of April 2025. Plan your migration to GraphQL proactively.

How do I test the Admin API without a live store?

Create a free development store through your Shopify Partner account. Development stores give you full Admin API access, unlimited test orders, and no subscription costs.

What happens when my app exceeds the rate limit?

Shopify returns a THROTTLED error in GraphQL or a 429 HTTP status in REST. Your bucket refills at a steady rate (50 points/second on standard plans), so implementing a short backoff is usually sufficient to recover.

Can I use the Admin API from a frontend application?

No. Admin API access tokens must be kept server-side — they grant access to sensitive store data (orders, customers, financials). Exposing an Admin API token in client-side code is a critical security vulnerability. Use the Storefront API for any client-side data needs.

How many API calls can I make per second?

For GraphQL, it depends on your query cost, not the number of calls. A simple query might cost 2 points while a complex one costs 500. On standard plans, you get 50 points restored per second with a 1,000-point bucket. For REST, the limit is 40 requests per second with a bucket size of 80.

Start Building with the Shopify Admin API

The Shopify Admin API is the most powerful tool in a Shopify developer's toolkit. It provides complete programmatic access to every aspect of store management — from product catalog operations to order fulfillment to customer data.

The path forward is clear: build with GraphQL, implement proper authentication with the minimum required scopes, handle rate limits gracefully, use webhooks instead of polling, and leverage bulk operations for large datasets. These fundamentals will serve you whether you are building a single custom integration or a public app for the Shopify App Store.

If you are ready to go deeper, explore our guides on Shopify app development and building an app from scratch. And if you have questions about your specific integration architecture, the Talk Shop community is where Shopify developers share solutions and help each other build.

What are you building with the Shopify Admin API? Drop into our community and share your project.

Shopify DevelopmentApps & Integrations
Talk Shop

About Talk Shop

The Talk Shop team — insights from our community of Shopify developers, merchants, and experts.

Related Insights

Related

Best Shopify SEO Apps: 10 Tools Ranked for 2026

Related

Shopify Salesforce Integration: Connect Your Store to Your CRM in 2026

The ecommerce newsletter that's actually useful.

Daily trends, teardowns, and tactics from the top 1% of ecommerce brands. Delivered every morning.

No spam. Unsubscribe anytime. · Learn more

New

Business Name Generator

Generate unique, brandable business names with AI. Check domain availability instantly.

Generate Names

Talk Shop Daily

Daily ecommerce news, teardowns, and tactics.

No spam. Unsubscribe anytime. · Learn more

Try our Business Name Generator