Security & Rate Limiting
The SDK is designed so that theme and app developers get safe defaults for free. This page explains what protects each request, where the limits are, and the rules for using the SDK responsibly.
How a request is protected
Every call to /api/sdk/* is validated server-side before your handler runs. The SDK attaches the required headers automatically; you never assemble them yourself.
| Protection | How it works |
|---|---|
| CSRF token | X-SITEPACK-CSRF must match the server-issued sitepack_sdk token for the page. |
| Site binding | X-SITEPACK-SITE-UUID must match the site the request is served from. |
| Same-origin session | Requests are sent with credentials: 'include'; the customer's session cookie identifies them. |
| Initiator | X-SITEPACK-INITIATOR-TYPE (theme/app) and -ID attribute the call to your theme or app. |
| Rate limiting | Per-IP limiter on the server, plus a client-side token bucket (see below). |
X-SITEPACK-CLIENT-ID is a hash derived from the visitor's request context. It ties a call to the page it was issued for — but it is not a secret and provides no authentication on its own. The real guarantees come from the CSRF token, the same-origin session cookie, and the site UUID. Never treat the client-id as a credential.
The two rate-limit layers
1. Client-side token bucket
The SDK shares one token bucket across all instances on the page: 15 tokens, refilling fully over 10 seconds. Each request spends one token. When the bucket is empty, the call is rejected locally — before any network request — with:
Too Many Requests (Client-side Rate Limit)
Concurrency is also capped: at most 2 requests are in flight at once; the rest queue.
2. Server-side per-IP limiter
Independently, the server enforces a per-IP limit on /api/sdk/* (15 requests / 10 seconds). Exceeding it returns HTTP 429, which the SDK surfaces as a rejected promise with a "Too many requests" message.
Stricter limits on sensitive endpoints
Some endpoints add their own throttle on top of the generic limiter:
| Endpoint | Extra limit |
|---|---|
account.login, account.resetPassword | Strict per-IP and per-email throttle (anti credential-stuffing). |
contactSiteOwner | 5 submissions / hour per visitor, plus honeypot spam detection. |
Handling 429 gracefully
try {
await sdk.search.query(term);
} catch (err) {
if (err.message.includes('Too Many Requests') || err.message.includes('Too many requests')) {
sdk.ui.notify('Please slow down for a moment.', 'warning');
return;
}
throw err;
}
Safe-usage rules
- Handle every promise rejection — network errors, validation errors,
401, and429all surface as rejections. - Debounce and batch — debounce search-as-you-type, batch UI updates, reuse the cached cart.
- Let the SDK cache —
getCart,checkOrderCapability, and order-capability lookups are cached; don't force-refresh unnecessarily. - Read the Account rules before touching login.
- Don't hardcode secrets (API keys, tokens, passwords) in theme or app JavaScript — it ships to the browser and is fully visible.
- Don't overwrite the
SITEPACK_*globals — the SDK relies on them for request signing and will throw if they're missing. - Don't store passwords or personal data in cookies or
localStorage— keep them on the server session. - Don't poll in tight loops or fire speculative requests — you'll trip the rate limiter for the whole page.
- Don't rely on the client-side rate limiter for security — it's a UX smoother; the server is the real boundary.
Related pages
- Account — credential-handling rules.
- Getting Started — initialization and error handling.
- API Reference — every method and its HTTP verb.