Skip to content

Endpoint-Based Rate Limiting — Per-Route API Traffic Control

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Endpoint. We cover key concepts, practical examples, and best practices to help you master this topic.

Endpoint-based Rate Limiting applies distinct rate limits to different API routes, allowing critical or expensive endpoints to have stricter limits while read-only or cheap endpoints can handle more traffic.

What You'll Learn

  • What endpoint-based rate limiting is and when to use it
  • How to configure different limits for different API routes
  • How to implement endpoint-based limiting with Redis

Why It Matters

Not all endpoints have the same cost or risk profile. An authentication endpoint should have strict limits (5 requests per minute) to prevent brute-force attacks, while a public data endpoint might allow 1000 requests per minute. Endpoint-based limiting gives you this granular control.

Real-World Use

DodaTech's API applies endpoint-specific rate limits: /auth/login allows 10 req/min to prevent credential stuffing, /api/search allows 100 req/min per user, and /api/public/status allows 1000 req/min. This protects sensitive operations while keeping public data accessible.

flowchart LR
    A["Request"] --> B{"Which endpoint?"}
    B -->|"/auth/login"| C["Rate: 10 req/min"]
    B -->|"/api/search"| D["Rate: 100 req/min"]
    B -->|"/api/public/*"| E["Rate: 1000 req/min"]
    C --> F{"Under limit?"}
    D --> F
    E --> F
    F -->|Yes| G["Process Request"]
    F -->|No| H["429 Too Many Requests"]
    style B fill:#dbeafe,stroke:#2563eb
    style H fill:#fecaca,stroke:#dc2626

Configuration Structure

Define endpoint limits in a configuration file:

ENDPOINT_LIMITS = {
    "auth": {
        "paths": ["/api/auth/login", "/api/auth/register"],
        "limit": 10,
        "window": 60,  # 1 minute
        "key_by": "ip"
    },
    "search": {
        "paths": ["/api/search", "/api/suggest"],
        "limit": 100,
        "window": 60,
        "key_by": "user_id"
    },
    "public": {
        "paths": ["/api/public/*"],
        "limit": 1000,
        "window": 60,
        "key_by": "ip"
    },
    "admin": {
        "paths": ["/api/admin/*"],
        "limit": 500,
        "window": 60,
        "key_by": "user_id"
    }
}

Implementation with Redis

import time
import re
import redis
from flask import Flask, request, jsonify, g

app = Flask(__name__)
r = redis.Redis(host='localhost', port=6379, db=0)

def get_endpoint_config(path):
    for name, config in ENDPOINT_LIMITS.items():
        for pattern in config["paths"]:
            regex = "^" + re.escape(pattern).replace(r'\*', '.*') + "$"
            if re.match(regex, path):
                return config
    return None

@app.before_request
def rate_limit_by_endpoint():
    config = get_endpoint_config(request.path)
    if not config:
        return None

    if config["key_by"] == "ip":
        key = f"rl:endpoint:{request.path}:{request.remote_addr}"
    elif config["key_by"] == "user_id":
        user_id = g.get('user_id', 'anonymous')
        key = f"rl:endpoint:{request.path}:{user_id}"

    current = r.get(key)
    if current and int(current) >= config["limit"]:
        return jsonify({
            "error": "rate_limit_exceeded",
            "message": f"Endpoint limit of {config['limit']} per {config['window']}s exceeded",
            "retry_after": r.ttl(key)
        }), 429

    pipe = r.pipeline()
    pipe.incr(key, 1)
    pipe.expire(key, config["window"])
    pipe.execute()

@app.route('/api/auth/login', methods=['POST'])
def login():
    return jsonify({"message": "Login endpoint"})

Pattern Matching

Support both exact paths and wildcards:

from fnmatch import fnmatch

def match_endpoint(path, patterns):
    for pattern in patterns:
        if fnmatch(path, pattern):
            return True
    return False

# Test pattern matching
test_paths = ["/api/public/status", "/api/public/health", "/api/auth/login"]
patterns = ["/api/public/*", "/api/auth/*"]

for path in test_paths:
    matched = match_endpoint(path, patterns)
    print(f"{path}: {'Matched' if matched else 'No match'}")

Expected output:

/api/public/status: Matched
/api/public/health: Matched
/api/auth/login: Matched

Common Mistakes

1. Using Path Prefix Only Without Exact Patterns

Prefix matching may capture unintended routes. Use explicit patterns and test that each route maps to the correct limit.

2. Not Ordering Patterns Correctly

If a path matches multiple patterns, the first match wins. Order patterns from most specific to most general.

3. Ignoring HTTP Method in Endpoint Identity

A GET /api/data and POST /api/data may need different limits. Include the HTTP method in the rate limit key.

4. Forgetting Wildcard Prefixes

Sub-routes like /api/v2/search may not match /api/search*. Define explicit patterns for all route versions.

5. Applying Limits to Health Check Endpoints

Health check / monitoring endpoints should typically be excluded from rate limiting to avoid false alerts.

Practice Questions

  1. Why would an auth endpoint need stricter limits than a search endpoint?
  2. How do you handle path patterns with wildcards?
  3. What happens if a request matches multiple endpoint patterns?
  4. Should health check endpoints be rate limited?
  5. How do you include HTTP method in endpoint identification?

Answers

  1. Auth endpoints are targets for brute-force attacks. 2. Use fnmatch or regex pattern matching. 3. The first matching pattern in the configuration is applied. 4. Generally no, to avoid false monitoring alerts. 5. Include request.method in the rate limit key.

Challenge

Build an endpoint-based rate limiter that reads a YAML configuration file, supports exact paths and glob patterns, applies per-endpoint limits with HTTP method awareness, and returns appropriate headers showing the specific endpoint limit.

FAQ

What is endpoint-based rate limiting?

Rate limiting that applies different limits to different API routes based on route patterns.

Which endpoints should have the strictest limits?

Authentication endpoints, password reset, payment processing, and any mutation endpoint.

Should public endpoints have rate limits?

Yes, but typically much higher limits than auth or write endpoints.

How do wildcard patterns work in endpoint limits?

Glob or regex patterns match multiple paths under a common prefix.

What is the default limit for unmapped endpoints?

Define a default limit for any path not matching a specific endpoint configuration.

Mini Project

Create a Flask middleware that loads endpoint rate limits from a YAML file, supports exact and wildcard path patterns, applies limits per-method, and includes a debug endpoint showing the current rate limit configuration for any given path.

What's Next

  • Learn about API-key-based rate limiting for third-party access control
  • Explore quota-based limits for daily and monthly usage tracking
  • Continue to tiered rate limiting for free/pro/enterprise plans

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro