A few months ago, a client called me because their API was down. Someone had written a script that was making 10,000 requests per minute to their search endpoint. The server was overwhelmed. Legitimate users could not get through.
They had no rate limiting. It took 10 minutes to add it. The problem never happened again.
Rate limiting is one of those things that seems optional until it is not.
What rate limiting does
Rate limiting says: "You can make X requests per Y time period. After that, you get a 429 Too Many Requests response until the window resets."
This protects your server from abuse, prevents one user from hogging resources, and makes brute-force attacks much harder.
Laravel's built-in rate limiting
Laravel has excellent rate limiting built in. You configure it in RouteServiceProvider (or directly in routes):
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});Then apply it to your routes:
Route::middleware(['auth:sanctum', 'throttle:api'])->group(function () {
Route::get('/products', [ProductController::class, 'index']);
});That is it. 60 requests per minute per user. After that, they get a 429 response.
Different limits for different users
Premium users should get higher limits. Free users get lower limits. This is easy to implement:
RateLimiter::for('api', function (Request $request) {
$user = $request->user();
if (!$user) {
// Unauthenticated requests: 20 per minute by IP
return Limit::perMinute(20)->by($request->ip());
}
if ($user->plan === 'premium') {
return Limit::perMinute(500)->by($user->id);
}
return Limit::perMinute(60)->by($user->id);
});Rate limit headers
When you use Laravel's throttle middleware, it automatically adds headers to every response:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
Retry-After: 30 (only on 429 responses)These headers let API clients know how many requests they have left and when they can try again. Good API clients use these to avoid hitting the limit.
Protecting specific endpoints more aggressively
Some endpoints need stricter limits. Login endpoints are a classic example — you want to prevent brute-force password attacks:
RateLimiter::for('login', function (Request $request) {
return [
Limit::perMinute(5)->by($request->input('email')),
Limit::perMinute(20)->by($request->ip()),
];
});
Route::post('/login', [AuthController::class, 'login'])
->middleware('throttle:login');This limits login attempts to 5 per minute per email address and 20 per minute per IP. An attacker trying to brute-force a password will be stopped very quickly.
Use Redis for high-traffic APIs
By default, rate limiting uses your cache driver. For high-traffic APIs, make sure your cache driver is Redis. It is much faster than the database for this kind of operation.
Rate limiting is a small investment with a big payoff. Add it to every API you build.




