Skip to content

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
HTTP/1.1 200 OK
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1640995200
HeaderDescription
X-RateLimit-LimitMaximum requests allowed in the time window
X-RateLimit-RemainingRequests remaining in current window
X-RateLimit-ResetUnix 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
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:

javascript
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

javascript
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

javascript
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:

javascript
// ❌ 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:

  1. User-based rate limiting (custom implementation)
  2. API key based limits (for authenticated users)
  3. Request higher limits for shared environments

Monitoring Rate Limits

Client-Side Monitoring

Track your rate limit usage:

javascript
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:

  1. Optimize your usage - Cache results, batch requests
  2. Contact support - For enterprise license limits
  3. Self-host - Deploy your own instance with custom limits
  4. Use multiple endpoints - Distribute load across endpoint categories

Next Steps