Rate limits
The request budgets per store and per caller, the rate-limit headers, per-shopper limits with X-Client-IP, and how to back off on 429.
Rate limits keep one busy caller from slowing a store down for everyone else. Each budget counts requests per store and per caller over a short window. When a budget is spent, the API answers 429 RATE_LIMITED until the window moves on.
The budgets
| Applies to | Budget |
|---|---|
| The storefront and merchant API in general | 600 requests a minute, per store and caller IP |
| Register, sign in, password reset, profile and password changes, guest order lookup | 10 a minute, per store and caller IP |
Form submissions (POST /v1/forms/{handle}) | 30 a minute, per store and caller IP |
Search suggestions (GET /v1/search/suggest) | 1,200 a minute, per store and caller IP |
Search events (POST /v1/search/events) | 300 batches a minute, per store and caller IP |
| Chat | 60 a minute, per conversation (or signed-in customer) |
Marketing consent confirmation (POST /v1/marketing/confirm) | 10 a minute and 30 an hour, per store and caller IP |
Account deletion (DELETE /v1/account) | 3 a minute and 10 an hour per account, and 20 an hour per IP |
Search suggestions, search events and chat have their own budgets instead of the general one, so typing in a search box never uses up the budget your product pages need. The other tighter budgets apply to routes that take passwords or codes, where guessing must stay slow.
Headers
Responses on rate-limited routes tell you where you stand:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | The size of the budget for this route |
X-RateLimit-Remaining | How many requests are left in the current window |
Retry-After | On a 429: how many seconds to wait before trying again |
HTTP/1.1 429 Too Many Requests
Retry-After: 17
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 0
Content-Type: application/json
{ "error": { "code": "RATE_LIMITED", "message": "Too many requests." } }Browser code can read these headers too: they are exposed through CORS.
Per-shopper limits with X-Client-IP
A "caller" is the IP address the request comes from. For a storefront that calls the API from its own server, that is your server's IP, so every shopper would share one budget. Busy shops would run out quickly.
To give each shopper their own budget, send the shopper's IP address in X-Client-IP on every server call. The API trusts it only together with a valid X-Tenant-Secret; on a browser call, or without the secret, it is ignored.
// Next.js route handler: the shopper's IP from your hosting's proxy header.
const shopperIp = request.headers.get('x-forwarded-for')?.split(',')[0]?.trim()
const res = await fetch('https://api.elaraone.app/api/v1/products', {
headers: {
'X-Tenant-Key': process.env.ELARAONE_TENANT_KEY!,
'X-Tenant-Secret': process.env.ELARAONE_TENANT_SECRET!,
...(shopperIp ? { 'X-Client-IP': shopperIp } : {}),
},
})- Send one IPv4 or IPv6 address, with no port and no list.
- Only take the IP from a header your hosting platform sets and overwrites. A header a visitor can set themselves lets them pick their own budget.
- Never forward
X-Forwarded-Forto the API itself. SendX-Client-IPinstead. - The same IP is used for the per-IP parts of the tighter budgets, such as sign-in.
Back off on 429
- Stop sending requests in that budget.
- Wait for the number of seconds in
Retry-After. - Retry. If the request changes something, retry with the same
Idempotency-Key(see idempotency).
For background jobs and integrations, also:
- watch
X-RateLimit-Remainingand slow down before it reaches zero; - spread work out instead of running it in bursts, and run jobs one at a time per store;
- use exponential backoff with jitter for repeated failures, and stop after a few attempts;
- sync CRM records with
filter[updated_since]instead of re-reading everything (see pagination).
For shoppers, keep the page usable: keep the current search results on screen, and show a short "try again in a moment" message rather than an error page.
Staging stores
Staging stores, where your plan includes them, also have a daily ceiling: by default 20,000 API requests and 1,000 form submissions. Over it, the API answers 429 STAGING_LIMIT, with Retry-After set to the end of the day in UTC.
Idempotency keys
Retry requests that change things without doing them twice, which routes accept an Idempotency-Key, and how to generate keys and retry safely.
Versioning and changes
How the API is versioned, which changes we treat as breaking and which as additive, how to write clients that keep working, and where changes are announced.