Rate Limits
The License Server API implements intelligent rate limiting to protect the service while allowing legitimate usage patterns. Different endpoint categories have different rate limits based on their expected usage frequency.
Rate Limit Categories
High Frequency - Validation Endpoints
1000 requests / 15 minutes
These endpoints are designed for high-frequency real-time validation:
- POST
/v1/verify-license- Verify license validity - POST
/v1/validate-license- Validate license for instance - POST
/v1/track-usage- Report usage metrics - POST
/v1/usage-report- Get usage history
Use case: Real-time license checks in your application, usage tracking, monitoring.
Moderate Frequency - Activation Endpoints
300 requests / 15 minutes
These endpoints handle license activation and file operations:
- POST
/v1/activate-license- Activate license for instance - POST
/v1/verify-license-file- Verify uploaded license file - POST
/v1/verify-license-file-base64- Verify Base64 license file
Use case: License activation during application startup, license file verification.
Standard Frequency - Public Endpoints
500 requests / 15 minutes
Public information endpoints with generous limits:
- GET
/v1/export-license/{key}- Get license information - GET
/v1/export-license/{key}/file- Download license file - GET
/v1/list-activations/{key}- List license activations
Use case: Exporting license data, downloading license files, checking activation history.
Low Frequency - Management Endpoints
100 requests / 15 minutes (Authentication Required)
Sensitive license management operations:
- POST
/v1/issue-license- Create new license - POST
/v1/revoke-license- Revoke/reactivate license - DELETE
/v1/delete-license- Delete license
Use case: Issuing, revoking, and deleting licenses.
Lowest Frequency - Admin-Info Endpoints
50 requests / 15 minutes (Authentication Required)
Administrative reporting operations with the strictest limits:
- GET
/v1/list-licenses- List all licenses - GET
/v1/admin/stats- Get admin statistics - GET
/v1/recent-activations- Get recent activations
Use case: Administration dashboards, periodic reporting.
Rate Limit Headers
Every API response includes rate limit information in the headers:
HTTP/1.1 200 OK
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1640995200| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed in the time window |
X-RateLimit-Remaining | Requests remaining in current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
Handling Rate Limits
When You Hit the Limit
When you exceed the rate limit, you'll receive a 429 Too Many Requests response:
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1640995200
Retry-After: 60
{
"error": "Rate limit exceeded. Try again in 60 seconds."
}Best Practices
1. Respect Rate Limits
Always check rate limit headers and implement backoff:
async function makeAPIRequest(url, options) {
const response = await fetch(url, options);
// Check rate limit headers
const remaining = parseInt(response.headers.get('X-RateLimit-Remaining'));
const resetTime = parseInt(response.headers.get('X-RateLimit-Reset'));
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('Retry-After')) * 1000;
console.warn(`Rate limit exceeded. Retrying in ${retryAfter}ms`);
await new Promise(resolve => setTimeout(resolve, retryAfter));
return makeAPIRequest(url, options); // Retry
}
// Warn when approaching limit
if (remaining < 10) {
console.warn(`Approaching rate limit: ${remaining} requests remaining`);
}
return response;
}2. Implement Exponential Backoff
class LicenseClient {
constructor(baseURL) {
this.baseURL = baseURL;
this.maxRetries = 3;
}
async request(endpoint, options, retryCount = 0) {
try {
const response = await fetch(`${this.baseURL}${endpoint}`, options);
if (response.status === 429 && retryCount < this.maxRetries) {
const retryAfter = parseInt(response.headers.get('Retry-After')) || 1;
const delay = Math.min(1000 * Math.pow(2, retryCount), retryAfter * 1000);
console.log(`Rate limited. Retrying in ${delay}ms (attempt ${retryCount + 1})`);
await new Promise(resolve => setTimeout(resolve, delay));
return this.request(endpoint, options, retryCount + 1);
}
return response;
} catch (error) {
if (retryCount < this.maxRetries) {
const delay = 1000 * Math.pow(2, retryCount);
await new Promise(resolve => setTimeout(resolve, delay));
return this.request(endpoint, options, retryCount + 1);
}
throw error;
}
}
}3. Cache License Verification Results
class CachedLicenseClient {
constructor(baseURL, cacheTimeout = 300000) { // 5 minutes
this.baseURL = baseURL;
this.cache = new Map();
this.cacheTimeout = cacheTimeout;
}
async verifyLicense(key) {
const cacheKey = `verify:${key}`;
const cached = this.cache.get(cacheKey);
// Return cached result if still valid
if (cached && Date.now() - cached.timestamp < this.cacheTimeout) {
return cached.result;
}
// Make API request
const response = await fetch(`${this.baseURL}/verify-license`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ key })
});
const result = await response.json();
// Cache successful results
if (response.ok && result.valid) {
this.cache.set(cacheKey, {
result,
timestamp: Date.now()
});
}
return result;
}
}4. Batch Operations When Possible
Instead of making many individual requests, batch operations:
// ❌ Bad: Multiple individual requests
for (const key of licenseKeys) {
await verifyLicense(key);
}
// ✅ Good: Batch verification (if your app supports it)
const results = await Promise.all(
licenseKeys.map(key => verifyLicense(key))
);
// ✅ Better: Use rate-aware batching
async function batchVerifyLicenses(keys, batchSize = 10, delayBetweenBatches = 1000) {
const results = [];
for (let i = 0; i < keys.length; i += batchSize) {
const batch = keys.slice(i, i + batchSize);
const batchResults = await Promise.all(
batch.map(key => verifyLicense(key))
);
results.push(...batchResults);
// Add delay between batches to stay within rate limits
if (i + batchSize < keys.length) {
await new Promise(resolve => setTimeout(resolve, delayBetweenBatches));
}
}
return results;
}Rate Limiting by IP
Rate limits are applied per IP address by default. This means:
- Each client IP gets its own rate limit bucket
- Multiple users behind the same NAT/proxy share limits
- Load balancers should preserve client IP addresses
Handling Shared IP Scenarios
If multiple users share an IP (corporate NAT, shared hosting), consider:
- User-based rate limiting (custom implementation)
- API key based limits (for authenticated users)
- Request higher limits for shared environments
Monitoring Rate Limits
Client-Side Monitoring
Track your rate limit usage:
class RateLimitMonitor {
constructor() {
this.limits = new Map();
}
trackResponse(endpoint, response) {
const limit = parseInt(response.headers.get('X-RateLimit-Limit'));
const remaining = parseInt(response.headers.get('X-RateLimit-Remaining'));
const reset = parseInt(response.headers.get('X-RateLimit-Reset'));
this.limits.set(endpoint, {
limit,
remaining,
reset,
usagePercent: ((limit - remaining) / limit * 100).toFixed(1)
});
// Log high usage
if (remaining < limit * 0.1) { // Less than 10% remaining
console.warn(`High rate limit usage for ${endpoint}: ${remaining}/${limit} remaining`);
}
}
getLimitStatus(endpoint) {
return this.limits.get(endpoint);
}
getAllLimitStatus() {
return Object.fromEntries(this.limits);
}
}Server-Side Monitoring
If you run the License Server, monitor rate limit metrics:
- Requests per endpoint category
- Rate limit violations by IP
- Peak usage times
- Client retry patterns
Fixed Rate Limits
Rate limits are fixed values set in the server implementation and are not configurable via environment variables.
Need Higher Limits?
If your application requires higher rate limits:
- Optimize your usage - Cache results, batch requests
- Contact support - For enterprise license limits
- Self-host - Deploy your own instance with custom limits
- Use multiple endpoints - Distribute load across endpoint categories
Next Steps
- Learn about authentication for admin endpoints
- Explore integration patterns that respect rate limits
- Check out caching strategies