Girman Logo
Let's talk
tech

API Rate Limiting with Redis and PostgreSQL

In this post, learn how to implement scalable rate limiting by combining Redis—ideal for real-time request tracking using TTL-based structures—with PostgreSQL.

S
Author
Shivansh
June 11, 2025
5 min read
API Rate Limiting with Redis and PostgreSQL

A rate limit is a mechanism that restricts the number of requests a client can make to a server within a specific time frame, preventing abuse and ensuring fair resource usage.


Top 3 Benefits of Using a Rate Limiter in an App

  1. Protect Resources: Prevents resource exhaustion and ensures fair usage.
  2. Mitigate Abuse: Safeguards against malicious attacks and spam.
  3. Improve Performance: Optimises resource utilisation and enhances overall system performance.


Lets implements fixed-window rate limiting using Redis for real-time tracking and PostgreSQL for configuration management. By applying fixed-time windows, we limit the number of requests that a user can make within specific intervals, enabling precise control over usage patterns and protecting against abuse.


System Components

  1. PostgreSQL: Stores rate limit configurations per user and API endpoint.
  2. Redis: Tracks API usage in real-time with a TTL-based structure.
  3. Annotations: Assigns aliases to API endpoints, allowing flexible configuration by mapping multiple endpoints to the same alias.

This combination provides both efficient tracking and easy configurability for scalable, fine-grained rate limiting.


PostgreSQL for Rate Limit Configurations

PostgreSQL stores rate limit settings per user and API alias, simplifying configuration management. We define the following tables:

users: Stores user information (id, email, etc).

  1. api_identifier: Assigns unique aliases to API endpoints and tracks endpoint details.
  2. api_rate_limit_config: Links specific rate limits to each user and API alias.
  3. user_id: Links to users.id.
  4. api_alias: Alias assigned to the endpoint via annotations.
  5. time_window_in_sec: Duration of the time window.
  6. limit: Maximum allowed requests within the time window.


CREATE TABLE users (
   id SERIAL PRIMARY KEY,
   email VARCHAR(255) NOT NULL
);

CREATE TABLE api_identifier (
   id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
   name VARCHAR(255) NOT NULL,  -- A unique alias for each API (used in annotations)
   endpoint_path VARCHAR(255) NOT NULL,  -- The actual path of the API endpoint
   http_method VARCHAR(10) NOT NULL,  -- HTTP method (GET, POST, PUT, DELETE)
   description TEXT                    -- Optional description of the API endpoint
);

CREATE TABLE api_rate_limit_config (
   id SERIAL PRIMARY KEY,
   api_id BIGINT REFERENCES api_identifier(id) NOT NULL,
   time_window_in_sec INT NOT NULL,
   limit INT NOT NULL,
   UNIQ ( api_id, time_window_in_sec )
);


Redis for Real-Time Rate Tracking

Redis tracks requests in real time using a TTL-based key structure. Keys are defined as user_id:api_id:time_window, allowing efficient usage tracking per endpoint per user.

  1. Tracks the number of requests made in the current time window.
  2. Each key has a TTL set to time_window_in_sec


Redis Commands:

  1. Check if key exists: EXISTS user_id:api_id:time_window
  2. Increment usage count: INCR user_id:api_id:time_window
  3. Set TTL for time-based reset: EXPIRE user_id:api_id:time_window time_window_in_sec


Annotations for Endpoint Aliasing

Annotations are often used in languages like Java and Python to simplify functionality and make code cleaner and more modular. They allow you to define behaviours that are then applied or "triggered" at runtime.


Annotations provide a flexible mechanism to alias API endpoints, enabling administrators to group functionally similar endpoints under a single alias. This approach simplifies rate limit configuration and management, as changes to the rate limit for an alias apply across all endpoints sharing it.

Here’s how to create and use annotations for rate limiting in Java and Python.


  1. Define Annotation: Use a custom annotation, e.g., @ApiIdentifier("GET_USER_INFORMATION"), to specify an alias for each endpoint or method in the controller.


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ApiIdentifier {
    String value(); // to store the identifier like "GET_USER_INFORMATION"
}


from functools import wraps

def api_identifier(identifier):
    def decorator(view_func):
        @wraps(view_func)
        def wrapped_view(*args, **kwargs):
            # Print the identifier for demonstration
            print(f"API Identifier: {identifier}")
            return view_func(*args, **kwargs)
        wrapped_view.api_identifier = identifier  # Attach the identifier to the view
        return wrapped_view
    return decorator


  1. Apply Annotation: Assign a unique alias to each endpoint. This alias is then used as the api_identifier.name in both PostgreSQL and Redis.


@RestController
public class UserController {

    @ApiIdentifier("GET_USER_INFORMATION")
    @GetMapping("/api/user/info")
    public String getUserInfo() {
        return "User information data";
    }
}



from django.http import JsonResponse
from django.views import View

# Function-based view example
@api_identifier("GET_USER_INFORMATION")
def get_user_info(request):
    return JsonResponse({"message": "User information data"})


  1. Configuration Retrieval: On each request, look up the api_id in api_rate_limit_config to fetch the appropriate rate limit.


@Component
public class ApiIdentifierInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            Method method = ((HandlerMethod) handler).getMethod();
            ApiIdentifier apiIdentifier = method.getAnnotation(ApiIdentifier.class);
            if (apiIdentifier != null) {
                String identifier = apiIdentifier.value();
                System.out.println("API Identifier: " + identifier);
                // Use identifier for logging, rate limiting, etc.
            }
        }
        return true;
    }
}


from django.utils.deprecation import MiddlewareMixin

class ApiIdentifierMiddleware(MiddlewareMixin):

    def process_view(self, request, view_func, view_args, view_kwargs):
        # Check if the view has an `api_identifier` attribute
        identifier = getattr(view_func, 'api_identifier', None)
        if identifier:
            print(f"API Identifier: {identifier}")
            # Here, you could implement rate limiting, logging, etc., based on identifier.
        return None


  1. Redis Validation: Check Redis using the alias-based key structure. Increment the request count or deny access if the rate limit is reached.


Response Header

To inform clients of their usage and reset times, include the following headers in the response:

  1. X-RateLimit-Limit: Shows the maximum number of requests allowed within the time window.
  2. X-RateLimit-Remaining: Indicates the remaining requests in the current window.
  3. X-RateLimit-Reset: Provides the timestamp when the rate limit will reset, in Unix epoch seconds.


Summary

This setup combines Redis’s efficiency in real-time usage tracking with PostgreSQL’s reliability for storing configuration. By using annotations for aliasing, you can manage multiple

endpoints under shared limits, creating a powerful, flexible rate-limiting solution that scales easily:

  1. Redis: Efficiently tracks request counts and resets automatically per time_window_in_sec.
  2. PostgreSQL: Holds rate limit settings per user and API alias, making configurations easily manageable.
  3. Annotations: Streamline rate-limiting by mapping each API endpoint to an alias, allowing consistent configurations across the system.


Read More:

How to Design an Effective Subscription Management System

Case Study: Revolutionizing Scent Marketing’s CRM with Frappe

tech
Published June 11, 2025

Schedule a free 30-minute consultation to explore ERPNext

Lets ChatMail Us
S
Author
Shivansh
Girman-logo
frappe-partner

Girman Tech is a Frappe Certified Partner in Bangalore, trusted for delivering tailored ERPNext solutions to businesses of all sizes. As an official Frappe and ERPNext Partner in Bangalore, we help companies to streamline operations and grow with open-source ERPNext solutions.

From seamless implementation to customization and ongoing support, our team ensures businesses unlock the full potential of open-source ERP. Based in Bangalore, we serve clients across India and globally with reliable, scalable, and future-ready ERP solutions.

Recognized By

footer_startupindia

BUSINESS

mail

contact@girmantech.com

phone

(+91) 93801 94282

Accounting ERP Software in Bangalore

Accounting ERP Software in Karnataka

HR Contact

mail

careers@girmantech.com

phone

(+91) 7558354540

CONTACT

9380194282

girish@girmantech.com

manish@girmantech.com

ADDRESS

Girman Technologies Pvt Ltd

BRIGADE NORTHRIDGE, PHASE-1, Yelahanka, Bangalore, Karnataka, India 560064

GIRMAN TECH

FOLLOW US