> For the complete documentation index, see [llms.txt](https://quantumphp.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://quantumphp.gitbook.io/docs/packages/ratelimit/contracts.md).

# Contracts

## Route contract

Rate limiting starts at the route definition or route group definition.

```php
Router::group('/api', function () {
    Router::get('/posts', 'PostController', 'index');
    Router::get('/comments', 'CommentController', 'index');
})->rateLimit(100, 60);
```

### Parameters

* `$limit` - maximum allowed hits in the current window
* `$interval` - window length in seconds

Both values need to be greater than `0`. The route API validates them as soon as you call `rateLimit(...)`.

### Where `rateLimit(...)` applies

`RouteBuilder::rateLimit(...)` supports three usage patterns:

* directly on a route definition
* on the current group while the group is being built
* immediately after a group definition, which applies the settings to the routes that group just created

Calling `rateLimit(...)` without an active route or group raises `RouteException::rateLimitOutsideRoute()`.

## Runtime key contract

`RateLimiter` groups requests by a normalized key shaped like:

```
<METHOD>:<route-pattern>:<ip>
```

Normalization rules that affect real usage:

* HTTP methods are uppercased
* empty route patterns become `/`
* missing or empty IPs become `0.0.0.0`

Because the route pattern is part of the key, counts follow the declared route pattern rather than the final controller name.

## Limiter contract

`RateLimiter` is a thin wrapper around one adapter instance.

```php
$limiter = RateLimiterFactory::get();

$allowed = $limiter->hit('GET', '/api/posts', '127.0.0.1', 60, 60);
$retryAfter = $limiter->retryAfter('GET', '/api/posts', '127.0.0.1');
$limiter->reset('GET', '/api/posts', '127.0.0.1');
```

### Method behavior

#### `hit(...)`

* increments the current counter
* returns `true` while the counter is within the limit
* returns `false` once the bucket moves past the limit

#### `retryAfter(...)`

* returns remaining seconds in the current window when the adapter can determine it
* returns `0` when no state exists or expiry is not available

#### `reset(...)`

* with no count or a non-positive count, clears the bucket
* with a positive count, seeds the bucket with that count and starts a new adapter-level TTL window

## Factory contract

`RateLimiterFactory::get(?string $adapter = null)` resolves one `RateLimiter` per adapter name and reuses that instance for later calls.

That means repeated calls for the same adapter share the same underlying adapter object for the life of the current DI-managed factory instance.

If `config('rate_limit')` is not loaded yet, the factory imports `config/rate_limit.php` on first use.

## Middleware contract

`RateLimitMiddleware` reads the matched route's rate-limit settings and then:

* passes the request through with `X-RateLimit-Limit`, or
* returns a `429` JSON response immediately

When the adapter returns `0` from `retryAfter()`, the middleware uses the route interval for the `Retry-After` header.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://quantumphp.gitbook.io/docs/packages/ratelimit/contracts.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
