Understand Zenovay API rate limits and optimize your integration for reliability and performance.
Rate Limit Overview
External API Limits by Plan
These limits apply to the External API (endpoints under /api/external/v1/):
| Plan | Requests/Minute | Requests/Month |
|---|---|---|
| Free | 10 | 1,000 |
| Pro | 30 | 10,000 |
| Scale | 60 | 100,000 |
| Enterprise | 120 | 1,000,000 |
What Counts as a Request
Each API call counts as one request:
- GET requests
- POST requests
- PUT/PATCH requests
- DELETE requests
What Doesn't Count
- Failed authentication (401)
- Malformed requests (400)
- Server errors (500)
Rate Limit Headers
Response Headers
Every External API response includes:
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 27
X-Usage-Monthly: 4521
X-Usage-Limit: 10000
X-Usage-Reset: 2025-02-01T00:00:00Z
| Header | Description |
|---|---|
| X-RateLimit-Limit | Your per-minute request limit |
| X-RateLimit-Remaining | Requests left this minute |
| X-Usage-Monthly | Requests used this month |
| X-Usage-Limit | Monthly request limit |
| X-Usage-Reset | When monthly usage resets |
Reading Headers
const response = await fetch(url, { headers });
const limit = response.headers.get('X-RateLimit-Limit');
const remaining = response.headers.get('X-RateLimit-Remaining');
const reset = response.headers.get('X-RateLimit-Reset');
console.log(`${remaining}/${limit} requests remaining`);
console.log(`Resets at ${new Date(reset * 1000)}`);
Rate Limit Exceeded
Response
When limit exceeded:
HTTP/1.1 429 Too Many Requests
Retry-After: 3600
{
"error": {
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded. Please retry after 3600 seconds.",
"retry_after": 3600
}
}
Handling 429 Errors
async function apiRequest(url, options, retries = 3) {
const response = await fetch(url, options);
if (response.status === 429 && retries > 0) {
const retryAfter = response.headers.get('Retry-After') || 60;
console.log(`Rate limited. Waiting ${retryAfter}s...`);
await sleep(retryAfter * 1000);
return apiRequest(url, options, retries - 1);
}
return response;
}
Best Practices
Caching
Cache responses when possible:
const cache = new Map();
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
async function getWithCache(endpoint) {
const cached = cache.get(endpoint);
if (cached && Date.now() - cached.time < CACHE_TTL) {
return cached.data;
}
const response = await api.get(endpoint);
cache.set(endpoint, { data: response, time: Date.now() });
return response;
}
Request Only What You Need
Use time range filters to limit the data returned:
# Filter to a specific time range:
GET /api/external/v1/analytics/WEBSITE_ID?timeRange=7d
Use Date Filters
Limit data returned to specific periods:
# Fetch only the last 30 days:
GET /api/external/v1/analytics/WEBSITE_ID?timeRange=30d
Pagination
Default Pagination
GET /api/external/v1/analytics/WEBSITE_ID/visitors?page=1&per_page=50
Response
{
"data": [...],
"meta": {
"total": 1250,
"page": 1,
"per_page": 50,
"total_pages": 25
}
}
Efficient Pagination
async function getAllVisitors() {
let allVisitors = [];
let page = 1;
let hasMore = true;
while (hasMore) {
const response = await api.get(`/visitors?page=${page}&per_page=100`);
allVisitors = allVisitors.concat(response.data);
hasMore = page < response.meta.total_pages;
page++;
// Respect rate limits
await sleep(100);
}
return allVisitors;
}
Exponential Backoff
Implementation
async function requestWithBackoff(fn, maxRetries = 5) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
if (error.status === 429 && attempt < maxRetries - 1) {
const delay = Math.pow(2, attempt) * 1000 + Math.random() * 1000;
console.log(`Retry ${attempt + 1} after ${delay}ms`);
await sleep(delay);
} else {
throw error;
}
}
}
}
Backoff Schedule
| Attempt | Wait Time |
|---|---|
| 1 | 1-2 seconds |
| 2 | 2-4 seconds |
| 3 | 4-8 seconds |
| 4 | 8-16 seconds |
| 5 | 16-32 seconds |
Monitoring Usage
Check Current Usage
curl https://api.zenovay.com/api/external/v1/usage \
-H "X-API-Key: zv_YOUR_API_KEY"
This returns your current API usage and remaining quota for the month.
Usage Dashboard
View in API Keys from the sidebar:
- Hourly usage graph
- Daily totals
- Top endpoints
- Error rates
Usage Alerts
Set up alerts:
- 80% of hourly limit
- 90% of daily limit
- Rate limit exceeded
Optimizing High-Volume Use
Use Aggregated Endpoints
Use the analytics overview endpoint instead of fetching multiple separate data points:
# Single request for analytics overview:
GET /api/external/v1/analytics/WEBSITE_ID?timeRange=30d
This returns visitors, page views, bounce rate, top countries, and daily stats in one response.
Enterprise Rate Limits
Enterprise PlanCustom Limits
Enterprise customers can request:
- Higher hourly limits
- Higher burst limits
- Dedicated capacity
Priority Queue
Enterprise requests get:
- Priority processing
- Dedicated resources
- Better SLA
Requesting Increase
Contact your CSM or email support@zenovay.com with:
- Current usage patterns
- Expected growth
- Use case details
Troubleshooting
Frequently Rate Limited
If hitting limits often:
- Review usage patterns
- Implement caching
- Use aggregated endpoints
- Upgrade plan
Burst Limit Issues
If hitting burst limits:
- Add delays between requests
- Implement request queue
- Use exponential backoff
Unexpected Low Limits
If limits seem wrong:
- Verify plan level
- Check all API keys
- Contact support