Error Medic

Fixing Shopify API Rate Limits (429), Timeouts, and Errors

Comprehensive guide to resolving Shopify API 429 Too Many Requests, handling 401/403 auth issues, fixing timeouts, and managing webhook failures.

Last updated:
Last verified:
891 words
Key Takeaways
  • Shopify REST API uses a leaky bucket algorithm (40 requests max, 2/sec refill).
  • GraphQL API uses a calculated query cost system (1000 points max, 50/sec refill).
  • Webhooks fail if not acknowledged with a 200 OK within 5 seconds. Offload processing to a background worker.
  • Implement exponential backoff and retry logic for 429 and 5xx errors.
Fix Approaches Compared
MethodWhen to UseTimeRisk
Leaky Bucket QueueREST API integrationMediumLow
GraphQL Cost OptimizationGraphQL API queriesHighLow
Exponential BackoffHandling 5xx and 429sLowLow
Background WorkersWebhook processingMediumLow

Understanding the Error

When working with Shopify's APIs, you will inevitably encounter rate limits or transient errors. Shopify aggressively throttles API requests to ensure platform stability. The most common manifestation of this is the 429 Too Many Requests HTTP status code.

Common Error Messages

  • HTTP 429 Too Many Requests
  • {"errors":"Exceeded 2 calls per second for api client. Reduce request rates to resume uninterrupted service."}
  • GraphQL: {"errors":[{"message":"Throttled"}]}

Other related errors include:

  • 401 Unauthorized: Invalid or expired X-Shopify-Access-Token.
  • 403 Forbidden: Your app lacks the required access scopes (e.g., trying to read orders without read_orders scope).
  • 500, 502, 503: Server-side errors from Shopify. These require retry logic.
  • Webhooks not working: Often caused by your endpoint taking longer than 5 seconds to respond, causing Shopify to timeout and eventually drop the webhook subscription.

Step 1: Diagnose Rate Limits

Before fixing, you need to know which API you are hitting and what your limits are.

REST API: Shopify includes a special header in every REST response: X-Shopify-Shop-Api-Call-Limit. It looks like X-Shopify-Shop-Api-Call-Limit: 39/40. This means you have used 39 out of 40 available calls in your bucket. If you hit 40/40, the next request will trigger a 429 error.

GraphQL API: GraphQL limits are based on query complexity. Check the extensions.cost object in your GraphQL response. It shows requestedQueryCost, actualQueryCost, and your throttleStatus (currentlyAvailable, maximumAvailable, restoreRate).

Step 2: Implement the Fix

1. Handling REST Limits (Leaky Bucket)

Standard Shopify plans allow a bucket size of 40 requests, which empties (refills your allowance) at a rate of 2 requests per second. Shopify Plus stores get 80 requests and a refill rate of 4 per second. You must build a queueing system or a client-side rate limiter that tracks the X-Shopify-Shop-Api-Call-Limit header and pauses execution when you approach the limit (e.g., pausing when you hit 35/40).

2. Handling GraphQL Limits

You start with 1000 points and regain 50 points per second. To optimize:

  • Only request the exact fields you need.
  • Reduce the first or last arguments in pagination.
  • If a query costs more than your available capacity, pause your script based on the required restore rate before making the call.
3. Fixing Webhook Timeouts

Shopify expects a 200 OK response within 5 seconds of dispatching a webhook. If you process the payload synchronously (e.g., creating a user in your database, resizing an image), you will likely timeout. Fix: Immediately return a 200 OK upon receiving the webhook, and push the actual processing logic into a background queue like Celery, Sidekiq, or AWS SQS.

4. Handling 5xx and Transient Errors

Implement Exponential Backoff with Jitter. When you receive a 429, 500, 502, or 503 error, wait for 1 second, then retry. If it fails again, wait 2 seconds, then 4 seconds, etc., up to a reasonable maximum.

Frequently Asked Questions

python
import time
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

def get_shopify_session():
    session = requests.Session()
    # Define retry strategy for 429 and 5xx errors
    retry_strategy = Retry(
        total=5,  # Maximum number of retries
        backoff_factor=1,  # Exponential backoff factor (1s, 2s, 4s...)
        status_forcelist=[429, 500, 502, 503, 504],
        allowed_methods=["HEAD", "GET", "OPTIONS", "POST", "PUT", "DELETE"]
    )
    adapter = HTTPAdapter(max_retries=retry_strategy)
    session.mount("https://", adapter)
    session.mount("http://", adapter)
    return session

def make_safe_request(url, headers):
    session = get_shopify_session()
    response = session.get(url, headers=headers)
    
    # Manual tracking for REST leaky bucket
    if 'X-Shopify-Shop-Api-Call-Limit' in response.headers:
        usage, limit = map(int, response.headers['X-Shopify-Shop-Api-Call-Limit'].split('/'))
        print(f"API Usage: {usage}/{limit}")
        # If we are close to the limit, proactively sleep
        if usage >= (limit - 5):
            print("Approaching rate limit, sleeping for 2 seconds...")
            time.sleep(2)
            
    response.raise_for_status()
    return response.json()
E

Error Medic Editorial

Error Medic Editorial is a team of senior SREs and DevOps engineers dedicated to providing actionable, code-first solutions to complex infrastructure and API problems.

Sources

Related Articles in Shopify

Explore More API Errors Guides