Skip to main content
The @RateLimit annotation provides a declarative way to add rate limiting to your Spring Boot application endpoints.
1
Add the annotation to your controller method
2
Annotate any controller method or class with @RateLimit to enforce rate limiting:
3
import io.github.v4runsharma.ratelimiter.annotation.RateLimit;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;

@RestController
public class ApiController {

    @RateLimit(limit = 10, duration = 1, timeUnit = TimeUnit.MINUTES)
    @GetMapping("/api/data")
    public String getData() {
        return "Hello World";
    }
}
4
This limits the endpoint to 10 requests per minute.
5
Apply rate limiting at the class level
6
You can also apply rate limiting to all methods in a controller:
7
@RateLimit(limit = 100, duration = 1, timeUnit = TimeUnit.HOURS)
@RestController
public class ApiController {

    @GetMapping("/api/users")
    public List<User> getUsers() {
        return userService.findAll();
    }

    @GetMapping("/api/posts")
    public List<Post> getPosts() {
        return postService.findAll();
    }
}
8
Method-level annotations override class-level annotations.
9
Configure the scope
10
Use the scope parameter to specify how rate limits are applied:
11
// Global rate limit (shared across all users)
@RateLimit(limit = 100, duration = 1, scope = "global", timeUnit = TimeUnit.MINUTES)
@GetMapping("/api/public")
public String getPublicData() {
    return "Public data";
}

// Per-user rate limit
@RateLimit(limit = 10, duration = 1, scope = "user", timeUnit = TimeUnit.MINUTES)
@GetMapping("/api/profile")
public User getProfile() {
    return userService.getCurrentUser();
}

// Per-IP rate limit
@RateLimit(limit = 5, duration = 1, scope = "ip", timeUnit = TimeUnit.MINUTES)
@GetMapping("/api/login")
public String login() {
    return "Login successful";
}
12
Use custom keys for fine-grained control
13
The key parameter lets you differentiate rate limits without writing a custom resolver:
14
@RateLimit(limit = 5, duration = 1, key = "search", timeUnit = TimeUnit.SECONDS)
@GetMapping("/api/search")
public List<Result> search(@RequestParam String query) {
    return searchService.search(query);
}

@RateLimit(limit = 10, duration = 1, key = "export", timeUnit = TimeUnit.MINUTES)
@GetMapping("/api/export")
public byte[] export() {
    return exportService.generateReport();
}
15
Name your rate limits
16
Use the name parameter for better observability in metrics and logs:
17
@RateLimit(
    name = "api-search",
    limit = 20,
    duration = 1,
    timeUnit = TimeUnit.MINUTES
)
@GetMapping("/api/search")
public List<Result> search(@RequestParam String query) {
    return searchService.search(query);
}
18
Disable rate limiting conditionally
19
Use the enabled parameter to turn off rate limiting without removing the annotation:
20
@RateLimit(
    limit = 10,
    duration = 1,
    enabled = false,
    timeUnit = TimeUnit.MINUTES
)
@GetMapping("/api/debug")
public String debug() {
    return "Debug info";
}
21
This is useful for testing or feature flags.

Annotation reference

The @RateLimit annotation supports the following parameters:
ParameterTypeRequiredDefaultDescription
limitintYes-Maximum number of requests allowed within the window
durationlongYes-Window size in the specified time unit
timeUnitTimeUnitNoSECONDSTime unit for the duration (SECONDS, MINUTES, HOURS, etc.)
scopeStringNo""Scope hint for the rate limit (e.g., “user”, “ip”, “global”)
keyStringNo""Static key suffix to differentiate limits
nameStringNo""Logical name for metrics and monitoring
enabledbooleanNotrueWhether rate limiting is enabled
keyResolverClassNoRateLimitKeyResolver.classCustom key resolver implementation

How it works

When a method annotated with @RateLimit is called:
  1. The AOP interceptor captures the invocation (see RateLimitAspect.java:24-44)
  2. A key is resolved based on the scope and configuration
  3. The rate limiter checks if the request exceeds the limit
  4. If allowed, the method proceeds normally
  5. If blocked, a RateLimitExceededException is thrown, which returns HTTP 429
The annotation can be placed on both methods and classes, with method-level annotations taking precedence.