Rate Limits
The public MCP endpoint enforces rate limits to ensure fair usage across all connected agents.
Current Limits
| Requests | 100 per minute |
| Scope | Per (store_id, IP address) combination |
| Algorithm | Sliding window |
| Window Size | 60 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:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
X-RateLimit-Limit | integer | — | The maximum number of requests allowed per minute (100). | |
X-RateLimit-Remaining | integer | — | The number of requests remaining in the current window. | |
X-RateLimit-Reset | integer | — | Unix 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_productswith a reasonablelimitinstead of making many individualget_product_detailscalls. - --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.