Rate Limits

The public MCP endpoint enforces rate limits to ensure fair usage across all connected agents.

Current Limits

Requests100 per minute
ScopePer (store_id, IP address) combination
AlgorithmSliding window
Window Size60 seconds

Per-store isolation

Rate limits are tracked per store independently. Requests to Store A do not count against your limit for Store B. An agent connecting to multiple stores has a separate 100 req/min allowance for each.

Response Headers

Rate limit status is communicated via the following HTTP response headers:

ParameterTypeRequiredDefaultDescription
X-RateLimit-LimitintegerThe maximum number of requests allowed per minute (100).
X-RateLimit-RemainingintegerThe number of requests remaining in the current window.
X-RateLimit-ResetintegerUnix timestamp (seconds) when the current window resets.

Rate Limit Error

When the rate limit is exceeded, the server returns an MCP error with code -32000:

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "Rate limit exceeded. Retry after 12 seconds."
  }
}

The message field includes the number of seconds to wait before retrying. This value reflects the time remaining until the sliding window has enough capacity.

Retry Strategy

Implement exponential backoff with the server's suggested retry delay:

javascript
async function callToolWithRateLimit(callFn, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await callFn();
    } catch (error) {
      if (error.code !== -32000 || attempt >= maxRetries) {
        throw error;
      }

      // Parse the retry delay from the error message
      const match = error.message?.match(/Retry after (\d+) seconds/);
      const serverDelay = match ? parseInt(match[1], 10) : 0;

      // Use the larger of server delay and exponential backoff
      const backoff = Math.pow(2, attempt); // 1, 2, 4
      const delay = Math.max(serverDelay, backoff);

      console.log(`Rate limited. Retrying in ${delay}s (attempt ${attempt + 1}/${maxRetries})`);
      await new Promise(resolve => setTimeout(resolve, delay * 1000));
    }
  }
}

Best Practices

  • --Cache responses. Product data, FAQs, policies, and brand info change infrequently. Cache for at least 5 minutes to reduce requests.
  • --Batch lookups. Use search_products with a reasonable limit instead of making many individual get_product_details calls.
  • --Respect the retry delay. Always wait at least the number of seconds indicated in the error message before retrying.
  • --Avoid polling loops. Earnings and campaign data do not change frequently enough to justify polling. Fetch once per session or on user request.