Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Rate Limiting and Throttling API Design

By Kokil Thapa | Last reviewed: September 2026

Every public API eventually faces the same problem: one client sends too many requests and everyone else slows down. Rate Limiting and Throttling API Design is how you stop that before it becomes an outage, a surprise cloud bill, or a partner integration that hammers your database at 3 a.m. On production Laravel applications I maintain, rate limits sit beside authentication and input validation—not as an optional hardening step. This guide walks through the algorithms, HTTP contract, Laravel 13 patterns, and gateway options you can ship this week. For deeper Laravel-specific wiring, see the companion piece on rate limiting and API throttling in Laravel.

What Is the Difference Between Rate Limiting and Throttling in API Design?

People use the terms interchangeably, but they solve slightly different problems. Rate limiting rejects excess traffic once a quota is exhausted. Throttling slows traffic down—queuing, delaying, or shedding load—so some requests still get through instead of failing immediately.

In practice, most REST APIs implement rate limiting with a hard ceiling. Throttling appears more often at the edge (API gateways, CDN WAFs) or inside job queues where delaying a webhook retry is acceptable. Your design should state which behaviour clients will see.

API Rate Limit Enforcement LayersClientSDK / partnerAPI GatewayEdge limitsApp LayerLaravel 13RedisCounters429 Too Many RequestsRetry-After header tells client when to retryX-RateLimit-Limit / Remaining / ResetDocument limits in OpenAPI before launchEnforce at gateway AND application for defence in depth
Rate Limiting and Throttling API Design works best as layered enforcement: gateway, application, and shared Redis counters.

A solid REST API design baseline treats limits as part of the contract. Partners plan retries around your headers. Internal teams avoid surprise denials during batch jobs. That contract belongs in your OpenAPI spec and your SDK error handling.

ApproachBehaviour on exceedBest forClient experience
Hard rate limitHTTP 429 immediatelyPublic REST APIs, auth endpointsPredictable; requires retry logic
Soft throttle (queue)Request waits in queueInternal microservices, webhooksHigher latency, fewer hard failures
Adaptive throttleLimit drops under loadShared SaaS during traffic spikesProtects platform; harder to document
Cost-based limitHeavy endpoints cost more tokensSearch, AI, report generationFair usage by resource weight

Which Rate Limiting Algorithms Should You Use for APIs?

Four algorithms cover nearly every production API. Pick based on burst tolerance, memory cost, and how precisely you need the window boundary to behave.

Fixed window counter

Count requests per calendar minute or hour. Implementation is trivial: one Redis key per client per window. The flaw is the "double burst" at window edges—a client can send 100 requests at 00:59 and 100 more at 01:00.

Sliding window log

Store timestamps of each request and drop entries older than the window. Accurate but memory-heavy at high volume. Good for low-traffic admin APIs or per-IP login protection.

Sliding window counter

Blend the current and previous fixed windows with a weighted count. You get near sliding-window accuracy with O(1) Redis operations. This is what many gateways use internally.

Token bucket

Tokens refill at a steady rate. Each request consumes one token. Clients can burst up to bucket capacity, then settle to the refill rate. Ideal for partner APIs where occasional spikes are fine but sustained abuse is not.

Token Bucket Rate LimitingToken BucketCapacity: 100 tokensCurrent: 73 tokensRefill10 tokens / secRequestCosts 1 tokenAllowToken > 0Burst up to bucket size, then steady refill rateEmpty bucket returns HTTP 429 with Retry-After
Token bucket is the default choice in Rate Limiting and Throttling API Design when partners need short bursts without sustained overload.

For a deeper algorithm walkthrough, read token bucket and sliding window rate limiting. On a legal-tech portal I built, login endpoints used a tight sliding window while document download APIs used token buckets with higher burst capacity.

How Do You Return HTTP 429 and Rate Limit Headers Correctly?

When a client exceeds its quota, return 429 Too Many Requests. The status code is defined in RFC 6585. Include headers that tell integrators exactly what happened and when they can retry.

Standard response headers:

  • Retry-After — seconds (integer) or HTTP-date until the client should retry
  • X-RateLimit-Limit — maximum requests allowed in the window
  • X-RateLimit-Remaining — requests left in the current window
  • X-RateLimit-Reset — Unix timestamp when the window resets

Some teams adopt the draft RateLimit header fields (`RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`). Pick one convention and use it consistently across all endpoints.

HTTP/1.1 429 Too Many Requests
Content-Type: application/problem+json
Retry-After: 42
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1726123456

{
  "type": "https://api.example.com/errors/rate-limit",
  "title": "Rate limit exceeded",
  "status": 429,
  "detail": "You exceeded 120 requests per minute.",
  "retry_after": 42
}

Use JSON formatter tools to validate error payloads during development. Problem Details (RFC 9457) give SDK authors a stable parsing surface. Never return 503 for rate limiting—that tells clients your server is down, not that they should back off.

Rate Limit Algorithm ComparisonFixed WindowSimple, edge burstLow memoryAccuracy: lowSliding WindowSmooth boundaryWeighted counterAccuracy: highToken BucketAllows burstsSteady refillAccuracy: highProduction recommendationPublic APIs: token bucket or sliding window counterAuth/login: tight sliding window per IP + user
Algorithm choice is a core decision in Rate Limiting and Throttling API Design—match the algorithm to burst tolerance and accuracy needs.

How Do You Implement Rate Limiting in Laravel 13?

Laravel ships built-in rate limiting via RateLimiter and route middleware. On PHP 8.3+ with Laravel 13, configuration lives in bootstrap/app.php or a dedicated service provider. Back the limiter with Redis 8.10 in production so limits survive horizontal scaling.

Define named limiters

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

RateLimiter::for('api', function (Request $request) {
    return Limit::perMinute(120)
        ->by($request->user()?->id ?: $request->ip())
        ->response(function (Request $request, array $headers) {
            return response()->json([
                'title' => 'Rate limit exceeded',
                'status' => 429,
            ], 429, $headers);
        });
});

RateLimiter::for('exports', function (Request $request) {
    return [
        Limit::perMinute(5)->by($request->user()->id),
        Limit::perDay(50)->by($request->user()->id),
    ];
});

Attach middleware to routes

Route::middleware(['auth:sanctum', 'throttle:api'])
    ->prefix('v1')
    ->group(function () {
        Route::get('/orders', [OrderController::class, 'index']);
        Route::post('/exports', [ExportController::class, 'store'])
            ->middleware('throttle:exports');
    });

Key the limiter by something stable: authenticated user ID, API key ID, or OAuth client ID. IP-only keys break behind carrier-grade NAT and punish mobile users in Nepal and elsewhere. For custom key strategies, see Laravel rate limiting with custom keys.

Set CACHE_STORE=redis in production. File or database cache backends do not share state across app servers. I've debugged "limits that only work on one node" more than once—that is almost always a cache driver issue.

  1. Define limiters in a service provider boot method.
  2. Apply throttle middleware on route groups.
  3. Return structured 429 JSON with standard headers.
  4. Store counters in Redis, not local memory.
  5. Log rate-limit hits with client ID for abuse review.
  6. Document every limiter name in OpenAPI.

More patterns appear in Laravel API best practices and the guide on building RESTful APIs with Laravel.

Where Should Rate Limits Live: Gateway, App, or Both?

Defence in depth wins. An API gateway (Kong, Traefik, AWS API Gateway) drops obvious abuse before PHP-FPM ever spins up. Application-level limits enforce business rules the gateway cannot know—per-tenant quotas, expensive endpoint weights, or role-based tiers.

On shared EC2 infrastructure where I run Deployer 7 releases, edge limits protect the whole fleet during traffic spikes. App limits catch authenticated abuse that shares one NAT IP. Read Kong as an API gateway and Traefik as an API gateway for edge deployment options.

429 Retry Decision FlowReceive HTTP 429Read Retry-AfterWait, then retryExponential backoffStop after N triesSDKs must honour Retry-After before applying backoffUse jitter to prevent thundering herd on window resetIdempotent GET/HEAD retries are safe; POST needs idempotency keys
Good Rate Limiting and Throttling API Design includes clear retry rules—clients should read Retry-After, add jitter, and respect idempotency on writes.

Choosing limit keys and tiers

Free-tier SaaS APIs often allow 60 requests per minute. Paid tiers get 600 or 6,000. Store tier metadata on the user or API key record. Resolve the limit in middleware before the controller runs.

Cost-weighted limits matter when some endpoints are expensive. A product search might cost 1 token. A PDF report export might cost 20. This pattern prevents one integration from monopolising workers during month-end reporting.

Webhooks and async work

Outbound webhooks need their own outbound rate limits. Payment gateways and SMS providers will ban your IP if you retry aggressively. Queue webhook delivery with backoff and cap concurrent sends per destination. See webhook design and security and webhook design patterns for reliability.

How Do You Design Rate Limits Partners Can Actually Integrate With?

Limits nobody knows about become support tickets. Document them in OpenAPI, publish a status page note when limits change, and ship an SDK that parses 429 responses automatically.

OpenAPI documentation

Describe global limits in the API info block. Annotate expensive routes individually. Tools like Scribe for Laravel generate docs from your actual middleware config—see design a REST API with OpenAPI and Swagger.

SDK retry logic

Your public SDK should read Retry-After, sleep, and retry idempotent requests. For POST requests, require idempotency keys so a retried payment does not double-charge. Full SDK guidance lives in SDK design for your public API.

Monitoring and alerting

Track rate-limit hit rate per client, per endpoint, and globally. A sudden spike on one API key may mean a runaway cron job—not an attack. Wire metrics into Prometheus and Grafana as described in API monitoring with Prometheus and Grafana.

On the Quick And Easy Nepalese Grocery Laravel eCommerce platform, checkout and inventory APIs used separate limiters. Checkout stayed tight to prevent card-testing abuse. Catalog reads got a higher burst because mobile clients prefetch product lists.

Security overlap

Rate limiting is not a substitute for authentication. It complements it. Login and password-reset endpoints deserve the strictest limits—often per IP and per username combined. Brute-force protection overlaps with rate limiting to stop brute-force attacks and the broader API security checklist.

Abuse prevention goes further: block known bad ASNs, require API keys for all non-public routes, and rotate leaked keys quickly. The practical abuse guide at API rate limiting and abuse prevention covers patterns I use on production systems.

Versioning and deprecation

When you ship API v2, do not silently tighten v1 limits to force migration—that breaks trust. Announce limit changes in changelog emails and sunset headers. API versioning strategies and deprecation best practices pair naturally with quota policy.

Redis implementation sketch

For custom limiters outside Laravel's facade, atomic INCR with TTL in Redis avoids race conditions. The Redis INCR command documentation shows the primitive most libraries wrap.

/*
  Pseudocode: sliding window with Redis INCR + EXPIRE
  Key: ratelimit:{client_id}:{window_start_minute}
*/
count = redis.incr(key)
if count == 1:
    redis.expire(key, 60)
if count > limit:
    return 429 with Retry-After = ttl(key)

For multi-region deployments, prefer a central Redis cluster or accept eventually consistent limits per region. Strong global consistency is rarely worth the latency cost for rate limiting.

Testing limits before launch

Load-test with realistic client concurrency. Verify 429 headers, confirm Redis keys expire, and ensure a limit breach does not leak stack traces. Include rate-limit scenarios in your CI smoke suite where feasible. Our testing and optimization service often catches missing Retry-After headers during pre-launch audits.

If you are building a new public API from scratch, pairing limit design with API development early saves painful retrofitting. Limits touch routing, caching, billing tiers, and support playbooks.

Key Takeaways

  • Rate limiting rejects excess requests with HTTP 429; throttling slows or queues traffic—document which behaviour your API uses.
  • Use token bucket or sliding window counters backed by Redis for accurate, scalable limits across multiple app servers.
  • Return Retry-After plus X-RateLimit-* headers on every 429 so SDKs and partners retry correctly.
  • Enforce limits at both the API gateway and application layer, keyed by user or API key—not IP alone.
  • Document quotas in OpenAPI, monitor hit rates per client, and require idempotency keys on retried write operations.
  • Weight expensive endpoints higher so one report export cannot consume the same budget as a thousand lightweight reads.

People Also Ask

What HTTP status code should APIs return when rate limited?

Return 429 Too Many Requests with a Retry-After header. Do not use 403 (that implies authorization failure) or 503 (that implies server overload). A JSON error body with a stable error type URL helps SDK parsing.

What is a reasonable API rate limit for free and paid tiers?

Free tiers commonly start at 60–100 requests per minute. Paid tiers scale to 1,000+ depending on infrastructure cost. Match limits to actual server capacity and endpoint cost. Publish exact numbers rather than vague "fair use" policies.

Should rate limiting use IP address or API key?

Prefer API key or authenticated user ID for identified clients. Use IP as a fallback for anonymous endpoints like login and password reset. Combine both on auth routes to block distributed credential stuffing.

How does rate limiting differ from DDoS protection?

Rate limiting enforces per-client quotas under normal traffic shapes. DDoS protection handles volumetric attacks at the network or CDN edge. You need both, but they operate at different layers and thresholds.

Ship Rate Limits Before You Need Them

Rate Limiting and Throttling API Design is cheapest to get right at launch and expensive to bolt on after an partner integration or scraper takes your API offline. Pick an algorithm, wire Redis, return proper 429 headers, document the contract, and test under load. Your future on-call self will thank you.

Need help designing or auditing limits on a Laravel, Symfony, or custom PHP API? Contact us to review your current setup, or explore related reading on rate limiting strategies for APIs and REST API design best practices in 2026. You can also browse the Mijar Law Associates client portal work and other portfolio projects where production API limits protect real business workflows.

Frequently Asked Questions

Rate limiting rejects excess traffic once a quota is exhausted, typically with HTTP 429. Throttling slows traffic instead—queuing, delaying, or shedding load so some requests still complete. Most public REST APIs use hard rate limits. Throttling appears more at API gateways, CDN WAFs, or job queues where delayed webhook retries are acceptable. Document which behaviour your clients will see.

Return HTTP 429 Too Many Requests with a Retry-After header. Do not use 403 or 503.

Four algorithms cover most cases. Fixed window counters are simple but suffer double bursts at window edges. Sliding window logs are accurate but memory-heavy. Sliding window counters blend fixed windows for near-accurate O(1) Redis operations—common in gateways. Token bucket refills steadily and allows short bursts, making it the default for partner APIs where occasional spikes are fine but sustained abuse is not.

Laravel ships RateLimiter and throttle middleware. On PHP 8.3+ with Laravel 13, define named limiters in bootstrap/app.php or a service provider using Limit::perMinute() keyed by user ID or API key. Attach throttle:limiter-name middleware to route groups. Set CACHE_STORE=redis in production so counters share state across app servers. Return structured 429 JSON with standard headers from a custom response callback. Document every limiter name in OpenAPI.

Free tiers commonly start at 60–100 requests per minute. Paid tiers scale to 1,000 or higher depending on infrastructure cost.

Prefer authenticated user ID, API key ID, or OAuth client ID. IP-only keys break behind carrier-grade NAT and punish mobile users. Use IP as a fallback for anonymous endpoints like login and password reset, and combine both on auth routes.

Use both for defence in depth. An API gateway such as Kong, Traefik, or AWS API Gateway drops obvious abuse before PHP-FPM spins up. Application-level limits enforce business rules the gateway cannot know—per-tenant quotas, expensive endpoint weights, or role-based tiers. App limits also catch authenticated abuse that shares one NAT IP behind the gateway.

Include Retry-After as seconds or HTTP-date, X-RateLimit-Limit for the window maximum, X-RateLimit-Remaining for requests left, and X-RateLimit-Reset as a Unix timestamp. Some teams use draft RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset instead. Pick one convention and apply it consistently. Pair headers with a JSON Problem Details body so SDK authors have a stable parsing surface.

HTTP 503 tells clients your server is down or overloaded, triggering failover or alert logic. Rate limiting is a client quota issue, not server failure. Returning 503 misleads integrators into thinking the platform is unavailable rather than asking them to back off. HTTP 429 with Retry-After gives partners a clear, retryable signal they can handle in SDK logic.

Rate limiting enforces per-client quotas under normal traffic shapes—one integration sending too many requests. DDoS protection handles volumetric attacks at the network or CDN edge, often at much higher thresholds. You need both, but they operate at different layers. Rate limits protect application resources and fair usage; DDoS tools absorb or block attack traffic before it reaches your API.

Document limits in OpenAPI globally and per expensive route. Publish status page notes when limits change. Ship an SDK that reads Retry-After, sleeps, and retries idempotent requests automatically. Require idempotency keys on retried POST writes so a payment retry does not double-charge. Monitor rate-limit hit rates per client and endpoint—a sudden spike often means a runaway cron job, not an attack.

File or database cache backends do not share state across multiple app servers. In horizontal scaling, each node tracks its own counter, so limits effectively multiply by server count. Redis 8.10 gives atomic counters that survive horizontal scaling. I've debugged limits that only worked on one node—that is almost always a cache driver issue, not a middleware bug.

Cost-weighted limits assign different token costs to endpoints by resource weight. A product search might cost one token while a PDF report export costs twenty. This prevents one integration from monopolising workers during month-end reporting. Use this pattern when some endpoints are significantly more expensive than others and a flat per-request quota would be unfair or risky.

Outbound webhooks need their own rate limits separate from inbound API quotas. Payment gateways and SMS providers will ban your IP if you retry aggressively. Queue webhook delivery with exponential backoff and cap concurrent sends per destination. Treat outbound throttling as a reliability requirement, not an optional hardening step, especially when integrations retry failed deliveries automatically.

Fixed window counters reset at calendar boundaries—a client can send one hundred requests at 00:59 and another hundred at 01:00, effectively doubling the allowed rate in a two-minute span. Sliding window counters or token bucket algorithms avoid this edge behaviour. Choose fixed windows only when simplicity outweighs burst accuracy, such as low-traffic internal tools.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: