Master API Rate Limiting for Shopify with Node.js Development

Shopify API
5 min read
Master API Rate Limiting for Shopify with Node.js Development

Implementing Rate-Limited Syncs for Shopify Admin API with Node.js

Shopify enforces API rate limits on every store, and exceeding them causes sync jobs to fail with 429 errors. Building a rate-limited sync with Node.js, using a retry queue and proper backoff logic, is the most reliable way to keep your Shopify app talking to the Admin API without dropping data.

Understanding API Rate Limiting in Shopify

Shopify uses a leaky bucket algorithm for its REST Admin API. Each store gets a bucket with a maximum capacity of 40 requests, and it drains at 2 requests per second. GraphQL uses a cost-based system where each field query carries a point cost, with the bucket refilling at 50 points per second up to a maximum of 1,000.

Two thresholds matter here. The soft limit is when your bucket is filling up and Shopify starts returning an X-Shopify-Shop-Api-Call-Limit header as a warning. The hard limit is a 429 response, meaning you have been throttled and must back off before retrying.

For a Shopify merchant running bulk syncs, whether that is syncing inventory across hundreds of variants or routing orders into an ERP, hitting the hard limit mid-job means data loss or incomplete writes unless you have a queue in place. A sync that fails silently can corrupt inventory counts or leave orders unrouted to your 3PL. That is the real cost of ignoring rate limiting.

Why Node.js Works Well for Shopify API Development

Node.js fits Shopify API development because of its event-driven, non-blocking I/O model. When you fire multiple API requests, Node.js does not sit idle waiting for each response. It registers a callback or resolves a promise and moves on, which maps directly to the async nature of API pagination and webhook processing.

Modern Node.js with async/await and TypeScript gives you readable, sequential-looking code that is non-blocking underneath. That matters for a retry queue because you need fine control over timing and concurrency without locking your main thread.

For HTTP transport, Axios is a practical choice because it exposes response headers directly, and you need those headers to read X-Shopify-Shop-Api-Call-Limit. Axios interceptors are particularly useful when you want to inspect every response before it reaches your business logic, which maps well to the Express middleware pattern used for HMAC validation on custom endpoints.

Node.js also integrates cleanly with queue management libraries like bottleneck and p-queue, which is exactly what you need for a production-grade Shopify app sync job.

Building Rate-Limited Shopify App Syncs with Node.js

Start with Node.js 18 or later, then install:

npm install axios bottleneck

Bottleneck handles concurrency control, reservoir-based limiting, and retry logic without requiring a custom sleep-and-retry loop. Here is a basic setup that limits requests to 2 per second, matching the REST bucket drain rate:

import Bottleneck from "bottleneck";
import axios from "axios";

const limiter = new Bottleneck({
  minTime: 500,       // 2 requests per second
  maxConcurrent: 1,
});

const shopifyGet = limiter.wrap(async (endpoint) => {
  const response = await axios.get(endpoint, {
    headers: {
      "X-Shopify-Access-Token": process.env.SHOPIFY_ACCESS_TOKEN,
    },
  });
  return response.data;
});

For the retry queue, configure Bottleneck to requeue on 429 responses:

limiter.on("failed", async (error, jobInfo) => {
  if (error.response?.status === 429 && jobInfo.retryCount < 5) {
    const retryAfter = error.response.headers["retry-after"] || 2;
    return retryAfter * 1000;
  }
});

Shopify includes a Retry-After header on every 429 response indicating exactly how long to wait. Always read that value rather than guessing. Returning the wait time in milliseconds tells Bottleneck to requeue the job automatically.

For GraphQL, read the throttle status from the response extensions block rather than response headers. The pattern is identical; the numbers differ.

Best Practices for Shopify API Development at Scale

Cache aggressively. Product metafields, collection memberships, and variant data do not change on every request. Store these in Redis or an in-memory Map with a TTL. Every avoided API call is one that cannot eat into your rate limit bucket.

Schedule bulk syncs during off-peak hours. Shopify's rate limits are per-store and per-app, but your own app's concurrent jobs compete with each other inside that bucket. A US-based store running an inventory sync at 3am sees far less contention than one running at noon.

Log the X-Shopify-Shop-Api-Call-Limit header on every response. Over time you will identify which sync jobs consume the most capacity, which endpoints get called redundantly, and where batching makes sense. The Shopify Admin API supports bulk operations via GraphQL's bulkOperationRunQuery mutation, which bypasses standard rate limits for large dataset reads entirely.

Centralize your header inspection and retry logic using Axios interceptors or Express middleware. This keeps business logic clean and makes swapping in a different rate limiter a one-file change rather than a codebase-wide refactor.

These same patterns apply when building Shopify apps that manage product bundles or wishlists. Apps like Bundle Wave and Wishlist Flow serve as useful reference points for how Admin API calls to sync bundle configurations or wishlist data across storefronts need the same rate limiting treatment as any other sync job.

At Dotmagic Infotech, the typical engagement for this type of work involves building the Node.js middleware layer, the retry queue, and the TypeScript types around the Shopify API response shapes before any business logic gets written. Getting the plumbing right first saves significant debugging time later.

FAQ

How does API rate limiting work for Shopify stores?

Shopify uses a leaky bucket algorithm for REST API calls, capped at 40 requests per bucket with a drain rate of 2 per second. GraphQL uses a cost-based bucket that refills at 50 points per second up to 1,000. When your Shopify app exceeds the bucket capacity, Shopify returns a 429 response with a Retry-After header telling you exactly how long to wait.

What is the best way to implement a retry queue for Shopify API rate limiting in Node.js?

Use the bottleneck library with its failed event handler to intercept 429 errors and requeue jobs automatically. Read the Retry-After header value from the error response and return it in milliseconds so Bottleneck waits the correct amount of time before retrying. This approach keeps your sync job running without data loss even when the Admin API throttles your requests.

Should I use REST or GraphQL for rate-limited Shopify API syncs?

For large dataset reads, GraphQL's bulkOperationRunQuery mutation is the better choice because it runs asynchronously outside standard rate limits. For smaller, more frequent operations, REST with a properly configured Node.js rate limiter works well and is simpler to implement. The right answer depends on your data volume and how frequently the sync runs.

About Dotmagic Infotech

Dotmagic Infotech is a full-stack Shopify and web development agency specializing in custom Node.js and React development, headless Shopify builds, Shopify API development, React Native, and CRM integrations. The team works with merchants and partners who need production-grade solutions beyond what off-the-shelf apps provide. Find Dotmagic Infotech on the Shopify Partner directory or reach out directly if you need a custom sync implementation built correctly.

Leave a comment

Dotmagic Infotech

Expert Team

Specialized in Shopify development and e-commerce solutions with years of experience helping businesses grow online.
Back to blog

Get in Touch With Us

If you are looking for a solid partner for your projects, send us an email. We'd love to talk to you!

Business
Need a mobile app or website?

Get a free consultation!

HR
Passionate about mobile apps & website?

Join our growing team!

Reach out to us!