Rate Limits
The Sellvy API enforces rate limits to ensure fair usage and platform stability. Limits are applied per API key and vary by plan. When you exceed a limit, the API returns a 429 Too Many Requests response.
Limits by plan
Each plan has a per-minute and per-day request ceiling. Requests beyond the limit are rejected until the window resets.
| Plan | Requests / minute | Requests / day |
|---|---|---|
| Free | 60 | 5,000 |
| Pro | 300 | 50,000 |
| Business | 1,000 | Unlimited |
Response headers
Every API response includes headers that report your current rate limit status. Use these to monitor usage and avoid hitting limits:
Rate limit headers
X-RateLimit-LimitintegerThe maximum number of requests allowed in the current window (per minute).
X-RateLimit-RemainingintegerThe number of requests remaining in the current window.
X-RateLimit-ResetintegerUnix timestamp (seconds) indicating when the current window resets.
Retry-AfterintegerNumber of seconds to wait before retrying. Only present on 429 responses.
Example response headers on a successful request:
HTTP/1.1 200 OK
Content-Type: application/json
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 247
X-RateLimit-Reset: 1712419260Rate limit exceeded response
When you exceed the rate limit, the API returns a 429 status with a Retry-After header and an error body:
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 23
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1712419283{
"success": false,
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded. Retry after 23 seconds."
}
}Retry strategy with exponential backoff
When you receive a 429 or 5xx response, use exponential backoff to retry gracefully. The following example respects the Retry-After header when present and falls back to exponential delays:
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const response = await fetch(url, options);
// Success — return immediately
if (response.ok) {
return response.json();
}
// Rate limited or server error — retry
if (response.status === 429 || response.status >= 500) {
if (attempt === maxRetries) {
throw new Error(`Failed after ${maxRetries + 1} attempts: ${response.status}`);
}
// Use Retry-After header if present, otherwise exponential backoff
const retryAfter = response.headers.get("Retry-After");
const delay = retryAfter
? parseInt(retryAfter, 10) * 1000
: Math.min(1000 * Math.pow(2, attempt), 30000);
console.log(`Retrying in ${delay}ms (attempt ${attempt + 1}/${maxRetries})`);
await new Promise((resolve) => setTimeout(resolve, delay));
continue;
}
// Client error (4xx except 429) — do not retry
const body = await response.json();
throw new Error(`API error: ${body.error.code} — ${body.error.message}`);
}
}
// Usage
const data = await fetchWithRetry("https://sellvy.io/api/v1/products", {
headers: {
"X-API-Key": process.env.SELLVY_API_KEY,
"Content-Type": "application/json"
}
});Retry-After header or use increasing delays.Special endpoint limits
Some endpoints have stricter rate limits to prevent abuse, independent of your plan-level limits:
| Endpoint | Limit | Auth required | Notes |
|---|---|---|---|
POST /v1/licenses/validate | 10/min per IP | No | Public endpoint for license key validation. Rate limited by IP address, not API key. |
POST /v1/crypto/sessions | 10/min per IP, 50/hr per seller | Yes | Crypto payment session creation has dual rate limits to prevent address exhaustion. |