docs/api-reference/store-api/rate-limitting.mdx
The Store API enforces rate limits to protect against abuse and ensure fair usage. Rate limits are applied per API key or per IP address depending on the endpoint.
| Endpoint | Limit | Scope | Window |
|---|---|---|---|
| All endpoints | 300 requests | Per API key | 1 minute |
POST /auth/login | 5 requests | Per IP | 1 minute |
POST /customers | 3 requests | Per IP | 1 minute |
POST /auth/refresh | 10 requests | Per IP | 1 minute |
POST /auth/oauth/callback | 5 requests | Per IP | 1 minute |
The global rate limit is tracked by your publishable API key (X-Spree-Api-Key). If the key is not provided, the limit falls back to the client's IP address.
Authentication endpoints have stricter per-IP limits to prevent brute-force attacks.
Every Store API response includes headers that show your current rate limit usage:
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum number of requests allowed per window |
X-RateLimit-Remaining | Number of requests remaining in the current window |
Retry-After | Seconds to wait before retrying (only present when limit is reached) |
Example response headers:
HTTP/1.1 200 OK
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 295
When you exceed the rate limit, the API returns a 429 Too Many Requests response:
{
"error": {
"code": "rate_limit_exceeded",
"message": "Too many requests. Please retry later."
}
}
The response includes rate limit headers and a Retry-After header indicating how many seconds to wait before retrying:
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 0
Retry-After: 60
The Spree SDK automatically handles rate-limited responses with built-in retry logic and exponential backoff:
import { createClient } from '@spree/sdk'
const client = createClient({
baseUrl: 'http://localhost:3000',
publishableKey: 'pk_xxx',
retry: {
maxRetries: 2, // Number of retry attempts (default: 2)
baseDelay: 300, // Initial delay in ms (default: 300)
maxDelay: 10000, // Maximum delay in ms (default: 10000)
},
})
The SDK respects the Retry-After header and only retries on 429 status codes for non-GET requests. For GET/HEAD requests, it also retries on 500, 502, 503, and 504 errors.
If you're self-hosting Spree, you can adjust rate limits in your initializer:
# config/initializers/spree.rb
Spree::Api::Config[:rate_limit_per_key] = 300 # Global limit per API key
Spree::Api::Config[:rate_limit_window] = 60 # Window in seconds
Spree::Api::Config[:rate_limit_login] = 5 # Login attempts per IP
Spree::Api::Config[:rate_limit_register] = 3 # Registration attempts per IP
Spree::Api::Config[:rate_limit_refresh] = 10 # Token refresh per IP
Spree::Api::Config[:rate_limit_oauth] = 5 # OAuth callbacks per IP
Rate limiting uses Rails.cache as the backing store. For production environments with multiple application servers, ensure you're using a shared cache store like Redis:
# config/environments/production.rb
config.cache_store = :redis_cache_store, { url: ENV['REDIS_URL'] }