← Back to Developer Blog
💻 DeveloperMarch 8, 20268 min read

API Rate Limiting: Stop Getting Abused

Your API is getting hammered. Here's how to implement rate limiting that actually works without blocking real users.

By Raspib Technology Team

API Rate Limiting: Stop Getting Abused

Your API is getting hammered by bots. Or one client is making 10,000 requests per minute.

Here's how to fix it.

Why Rate Limiting Matters

Real story: Client's API went down. One user's script had a bug, made 50,000 requests in 10 minutes. Crashed the server. Affected all users.

Rate limiting would've stopped it at request 100.

Basic Rate Limiting (Laravel)

Route::middleware('throttle:60,1')->group(function () {
    Route::get('/api/users', [UserController::class, 'index']);
});

60 requests per minute. Simple.

Problem: All users share the same limit. One bad actor affects everyone.

Better: Per-User Rate Limiting

Route::middleware('throttle:100,1,user')->group(function () {
    Route::get('/api/users', [UserController::class, 'index']);
});

Each authenticated user gets 100 requests/minute.

Much better. Bad actors only affect themselves.

Advanced: Different Limits for Different Endpoints

// Read endpoints: Higher limit
Route::middleware('throttle:200,1')->group(function () {
    Route::get('/api/products', [ProductController::class, 'index']);
    Route::get('/api/categories', [CategoryController::class, 'index']);
});

// Write endpoints: Lower limit
Route::middleware('throttle:30,1')->group(function () {
    Route::post('/api/orders', [OrderController::class, 'store']);
    Route::post('/api/payments', [PaymentController::class, 'process']);
});

Reading is cheap. Writing is expensive. Limit accordingly.

Node.js Rate Limiting

Using Express:

const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
  windowMs: 60 * 1000, // 1 minute
  max: 100, // 100 requests per minute
  message: 'Too many requests, try again later',
  standardHeaders: true,
  legacyHeaders: false,
});

app.use('/api/', limiter);

Redis-Based Rate Limiting

For multiple servers, use Redis:

// Laravel with Redis
Route::middleware('throttle:100,1,redis')->group(function () {
    Route::get('/api/data', [DataController::class, 'index']);
});
// Node.js with Redis
const RedisStore = require('rate-limit-redis');
const redis = require('redis');

const client = redis.createClient();

const limiter = rateLimit({
  store: new RedisStore({
    client: client,
  }),
  windowMs: 60 * 1000,
  max: 100,
});

Why Redis: Multiple servers share the same rate limit counter. User can't bypass by hitting different servers.

Strategies That Work

1. Tiered Limits

// Free tier
Route::middleware('throttle:50,1')->group(function () {
    // Limited endpoints
});

// Paid tier
Route::middleware('throttle:500,1')->group(function () {
    // Higher limits
});

// Enterprise tier
Route::middleware('throttle:5000,1')->group(function () {
    // Highest limits
});

2. Burst Allowance

Allow short bursts, but limit sustained traffic:

// Allow 10 requests immediately, then 1 per second
Route::middleware('throttle:10,1,per_second')->group(function () {
    Route::post('/api/search', [SearchController::class, 'search']);
});

3. IP + User Combined

// Limit by IP for unauthenticated
Route::middleware('throttle:20,1')->group(function () {
    Route::post('/api/register', [AuthController::class, 'register']);
});

// Limit by user for authenticated
Route::middleware(['auth', 'throttle:100,1,user'])->group(function () {
    Route::get('/api/dashboard', [DashboardController::class, 'index']);
});

Custom Rate Limiting Logic

Sometimes you need custom rules:

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;

RateLimiter::for('api', function (Request $request) {
    // Premium users get higher limits
    if ($request->user()?->isPremium()) {
        return Limit::perMinute(1000);
    }
    
    // Regular users
    if ($request->user()) {
        return Limit::perMinute(100);
    }
    
    // Unauthenticated: Very limited
    return Limit::perMinute(10);
});

Use it:

Route::middleware('throttle:api')->group(function () {
    // Your routes
});

Handling Rate Limit Responses

Return Useful Headers

// Laravel does this automatically
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1678901234
Retry-After: 60

Clients know when they can retry.

Custom Error Response

// app/Exceptions/Handler.php
use Illuminate\Http\Exceptions\ThrottleRequestsException;

public function render($request, Throwable $exception)
{
    if ($exception instanceof ThrottleRequestsException) {
        return response()->json([
            'error' => 'Rate limit exceeded',
            'retry_after' => $exception->getHeaders()['Retry-After'] ?? 60,
        ], 429);
    }
    
    return parent::render($request, $exception);
}

Real Project Example

Client: Payment processing API
Problem: Getting 50,000+ requests/hour, server struggling
Solution: Implemented tiered rate limiting

// Public endpoints: Very limited
Route::middleware('throttle:10,1')->group(function () {
    Route::post('/api/webhook', [WebhookController::class, 'handle']);
});

// Authenticated: Normal limits
Route::middleware(['auth', 'throttle:100,1'])->group(function () {
    Route::post('/api/payments', [PaymentController::class, 'process']);
});

// Verified partners: High limits
Route::middleware(['auth', 'verified', 'throttle:1000,1'])->group(function () {
    Route::post('/api/bulk-payments', [PaymentController::class, 'bulk']);
});

Results:

  • Server load: 85% → 45%
  • Legitimate requests: No impact
  • Bot traffic: Blocked 95%
  • Costs: Reduced by 40%

Monitoring Rate Limits

Track who's hitting limits:

RateLimiter::for('api', function (Request $request) {
    $limit = Limit::perMinute(100);
    
    // Log when users hit limits
    if (RateLimiter::tooManyAttempts($request->user()->id, 100)) {
        Log::warning('User hit rate limit', [
            'user_id' => $request->user()->id,
            'ip' => $request->ip(),
        ]);
    }
    
    return $limit;
});

Helps identify:

  • Buggy client code
  • Potential abuse
  • Need for higher limits

When to Adjust Limits

Increase limits if:

  • Legitimate users hitting limits
  • Complaints about "too many requests"
  • Business needs higher throughput

Decrease limits if:

  • Server struggling
  • High bot traffic
  • Abuse detected

Bottom Line

Rate limiting is essential for any public API.

Start with simple limits. Adjust based on real usage. Monitor and iterate.

Better to start strict and loosen up than start loose and get abused.


Building APIs?

We build secure, scalable APIs for Nigerian businesses.

📞 WhatsApp: +234 708 711 0468
📧 info@raspibtech.com
📍 Lagos Island

Related:

Need Help with Your Project?

Let's discuss how Raspib Technology can help transform your business

Related Articles

API Rate Limiting Guide - Protect Your API