Skip to main content

Rate Limit Overview

ContentStats.io implements rate limiting to ensure fair API usage and system stability.

Current Limits

PlanRequests per MinuteConcurrent Videos
Free6010
Pro300100
EnterpriseCustomUnlimited

Rate Limit Headers

Every API response includes rate limit headers:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1640995200
HeaderDescription
X-RateLimit-LimitMaximum requests allowed per window
X-RateLimit-RemainingRequests remaining in current window
X-RateLimit-ResetUnix timestamp when limit resets

Checking Rate Limits

const response = await fetch('https://contentstats.io/api/v1/videos', {
  headers: { 'X-API-Key': apiKey }
});

console.log('Limit:', response.headers.get('X-RateLimit-Limit'));
console.log('Remaining:', response.headers.get('X-RateLimit-Remaining'));
console.log('Resets at:', new Date(response.headers.get('X-RateLimit-Reset') * 1000));

Handling Rate Limits

Exponential Backoff

async function fetchWithBackoff(url, options, maxRetries = 5) {
  for (let i = 0; i < maxRetries; i++) {
    const response = await fetch(url, options);
    
    if (response.status !== 429) {
      return response;
    }
    
    const resetTime = parseInt(response.headers.get('X-RateLimit-Reset'));
    const waitMs = Math.max(0, (resetTime * 1000) - Date.now());
    
    console.log(`Rate limited. Waiting ${waitMs}ms`);
    await new Promise(resolve => setTimeout(resolve, waitMs));
  }
  
  throw new Error('Max retries exceeded');
}

Request Queuing

class RateLimiter {
  constructor(requestsPerMinute) {
    this.limit = requestsPerMinute;
    this.queue = [];
    this.processing = false;
  }
  
  async request(fn) {
    return new Promise((resolve, reject) => {
      this.queue.push({ fn, resolve, reject });
      this.process();
    });
  }
  
  async process() {
    if (this.processing || this.queue.length === 0) return;
    
    this.processing = true;
    const { fn, resolve, reject } = this.queue.shift();
    
    try {
      const result = await fn();
      resolve(result);
    } catch (error) {
      reject(error);
    }
    
    this.processing = false;
    
    const delay = 60000 / this.limit; // ms between requests
    setTimeout(() => this.process(), delay);
  }
}

// Usage
const limiter = new RateLimiter(60);

for (const video of videos) {
  await limiter.request(() => trackVideo(video.url));
}

Best Practices

Group operations to reduce API calls:
// ❌ Bad: 100 individual requests
for (const video of videos) {
  await getVideo(video.id);
}

// ✅ Better: Use list endpoint
const videos = await fetch('https://contentstats.io/api/v1/videos?limit=100');
Cache data to avoid redundant requests:
const cache = new Map();
const TTL = 60 * 60 * 1000; // 1 hour

async function getCachedVideo(id) {
  const cached = cache.get(id);
  
  if (cached && Date.now() - cached.timestamp < TTL) {
    return cached.data;
  }
  
  const data = await fetchVideo(id);
  cache.set(id, { data, timestamp: Date.now() });
  
  return data;
}
Track rate limit usage:
let requestCount = 0;

async function monitoredFetch(url, options) {
  const response = await fetch(url, options);
  
  requestCount++;
  const remaining = parseInt(response.headers.get('X-RateLimit-Remaining'));
  
  if (remaining < 10) {
    console.warn(`Low rate limit: ${remaining} requests remaining`);
  }
  
  return response;
}

Increasing Limits

Need higher limits? Contact sales for Enterprise plans with:
  • Custom rate limits
  • Dedicated infrastructure
  • Priority support
  • SLA guarantees

Next Steps