Skip to main content
The AnimeThemes API implements rate limiting to ensure fair usage and maintain service quality for all users.

Rate Limit Overview

The API uses different rate limiting strategies depending on the endpoint and authentication status.

API Endpoints

REST API endpoints (/api/*) are rate limited based on:
  • Authenticated requests: 90 requests per minute per user
  • Unauthenticated requests: 90 requests per minute per IP address

GraphQL Endpoint

The GraphQL endpoint (/graphql) has its own rate limit:
  • Default: 90 queries per minute
  • Configurable via GRAPHQL_RATE_LIMIT environment variable

Video Streaming

Video endpoints have a separate configurable rate limit:
  • Default: 90 requests per minute per IP
  • Set to -1 to disable rate limiting
  • Configurable via VIDEO_RATE_LIMITER environment variable

Rate Limit Headers

The API includes rate limit information in response headers:
X-RateLimit-Limit: 90
X-RateLimit-Remaining: 89
Retry-After: 60
  • X-RateLimit-Limit - Maximum requests allowed in the time window
  • X-RateLimit-Remaining - Number of requests remaining in current window
  • Retry-After - Seconds until the rate limit resets (included in 429 responses)

Rate Limit Exceeded

When you exceed the rate limit, you’ll receive a 429 Too Many Requests response:
{
  "message": "Too many requests"
}
The response includes a Retry-After header indicating when you can retry the request.

Bypassing Rate Limits

Certain users can be granted special permissions to bypass rate limits:

API Rate Limit Bypass

Users with the bypass api rate limiter permission are exempt from REST API rate limits.

GraphQL Rate Limit Bypass

Users with the bypass graphql rate limiter permission are exempt from GraphQL rate limits.

Local Development

Requests from 127.0.0.1 without a forwarded IP header are not rate limited. This allows unlimited requests during local development. From app/Providers/RouteServiceProvider.php:38-42:
// (If request is from client and no forwarded ip) or 
// (the user logged in has permission to bypass API rate limiting)
if (($ip === '127.0.0.1' && ! $forwardedIp) || 
    ($user instanceof User && $user->can(SpecialPermission::BYPASS_API_RATE_LIMITER->value))) {
    return Limit::none();
}

Rate Limiting Implementation

The API uses Laravel’s rate limiting features with automatic Redis detection:
  • With Redis: Uses ThrottleRequestsWithRedis for distributed rate limiting
  • Without Redis: Uses ThrottleRequests with cache-based limiting
This ensures rate limiting works in both simple and distributed environments.

Best Practices

Monitor Rate Limit Headers

Always check the X-RateLimit-Remaining header to track your usage:
const response = await fetch('https://api.animethemes.moe/api/anime');
const remaining = response.headers.get('X-RateLimit-Remaining');

if (remaining < 10) {
  console.warn('Approaching rate limit');
}

Implement Retry Logic

Respect the Retry-After header when you receive a 429 response:
const response = await fetch('https://api.animethemes.moe/api/anime');

if (response.status === 429) {
  const retryAfter = response.headers.get('Retry-After');
  await sleep(retryAfter * 1000);
  // Retry the request
}

Use Efficient Queries

Reduce the number of requests by:
# Instead of multiple requests:
curl https://api.animethemes.moe/api/anime/1
curl https://api.animethemes.moe/api/anime/1/animethemes

# Use includes:
curl "https://api.animethemes.moe/api/anime/1?include=animethemes"

Using Field Selection

curl "https://api.animethemes.moe/api/anime?fields[anime]=id,name,year"

Requesting Larger Pages

# Instead of 6 requests with default page size (15):
curl "https://api.animethemes.moe/api/anime?page[number]=1"
curl "https://api.animethemes.moe/api/anime?page[number]=2"
# ... etc

# Use larger page size (up to 100):
curl "https://api.animethemes.moe/api/anime?page[size]=100"

Cache Responses

Implement client-side caching to reduce duplicate requests:
const cache = new Map();

async function fetchWithCache(url) {
  if (cache.has(url)) {
    return cache.get(url);
  }
  
  const response = await fetch(url);
  const data = await response.json();
  cache.set(url, data);
  
  return data;
}

Batch Operations

When you need multiple resources, use filtering instead of individual requests:
# Instead of:
curl https://api.animethemes.moe/api/anime/1
curl https://api.animethemes.moe/api/anime/2
curl https://api.animethemes.moe/api/anime/3

# Use:
curl "https://api.animethemes.moe/api/anime?filter[id]=1,2,3"

Handling Rate Limits in Your Application

Python Example

import time
import requests

def fetch_with_rate_limit(url):
    while True:
        response = requests.get(url)
        
        if response.status_code == 429:
            retry_after = int(response.headers.get('Retry-After', 60))
            print(f'Rate limited. Waiting {retry_after} seconds...')
            time.sleep(retry_after)
            continue
            
        return response.json()

data = fetch_with_rate_limit('https://api.animethemes.moe/api/anime')

JavaScript Example

async function fetchWithRateLimit(url) {
  while (true) {
    const response = await fetch(url);
    
    if (response.status === 429) {
      const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
      console.log(`Rate limited. Waiting ${retryAfter} seconds...`);
      await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
      continue;
    }
    
    return await response.json();
  }
}

const data = await fetchWithRateLimit('https://api.animethemes.moe/api/anime');

Rate Limit Queue

For applications making many requests, implement a queue system:
class RateLimitedQueue {
  constructor(requestsPerMinute = 90) {
    this.queue = [];
    this.requestsPerMinute = requestsPerMinute;
    this.interval = 60000 / requestsPerMinute; // ms between requests
    this.lastRequest = 0;
  }
  
  async add(url) {
    return new Promise((resolve, reject) => {
      this.queue.push({ url, resolve, reject });
      this.process();
    });
  }
  
  async process() {
    if (this.queue.length === 0 || this.processing) return;
    
    this.processing = true;
    
    while (this.queue.length > 0) {
      const now = Date.now();
      const timeSinceLastRequest = now - this.lastRequest;
      
      if (timeSinceLastRequest < this.interval) {
        await new Promise(resolve => 
          setTimeout(resolve, this.interval - timeSinceLastRequest)
        );
      }
      
      const { url, resolve, reject } = this.queue.shift();
      this.lastRequest = Date.now();
      
      try {
        const response = await fetch(url);
        resolve(await response.json());
      } catch (error) {
        reject(error);
      }
    }
    
    this.processing = false;
  }
}

// Usage
const queue = new RateLimitedQueue(90);
const data = await queue.add('https://api.animethemes.moe/api/anime');

Rate Limit Configuration

Server administrators can configure rate limits via environment variables:
# REST API rate limit (requests per minute)
# Set in RouteServiceProvider (default: 90)

# GraphQL rate limit (queries per minute)
GRAPHQL_RATE_LIMIT=90

# Video streaming rate limit (requests per minute)
# Set to -1 to disable
VIDEO_RATE_LIMITER=90

Build docs developers (and LLMs) love