# philiprehberger_rate_limiter
[](https://github.com/philiprehberger/ex-rate-limiter/actions/workflows/ci.yml)
[](https://hex.pm/packages/philiprehberger_rate_limiter)
[](https://github.com/philiprehberger/ex-rate-limiter/commits/main)
In-process rate limiter with token bucket and sliding window algorithms
## Requirements
- Elixir >= 1.14
- OTP >= 25
## Installation
Add to your `mix.exs` dependencies:
```elixir
def deps do
[
{:philiprehberger_rate_limiter, "~> 0.1.0"}
]
end
```
Then run:
```bash
mix deps.get
```
## Usage
Add the rate limiter to your supervision tree:
```elixir
children = [
{Philiprehberger.RateLimiter, name: :my_limiter}
]
Supervisor.start_link(children, strategy: :one_for_one)
```
Then check rate limits anywhere in your application:
```elixir
case Philiprehberger.RateLimiter.check(:my_limiter, "user:123",
algorithm: :token_bucket,
capacity: 100,
refill_rate: 10
) do
{:ok, info} ->
# Request allowed, info.remaining tokens left
process_request()
{:error, info} ->
# Rate limited, retry in info.retry_after milliseconds
{:error, :rate_limited}
end
```
### Token Bucket
Best for APIs that allow short bursts above the average rate. Tokens refill continuously at a fixed rate.
```elixir
{:ok, %{remaining: 99, limit: 100, reset_in: 1000}} =
Philiprehberger.RateLimiter.check(:my_limiter, "user:123",
algorithm: :token_bucket,
capacity: 100,
refill_rate: 10,
cost: 1
)
```
### Sliding Window
Best for strict request counting per time window. Resets when the window expires.
```elixir
{:ok, %{remaining: 59, limit: 60, reset_in: 58000}} =
Philiprehberger.RateLimiter.check(:my_limiter, "ip:1.2.3.4",
algorithm: :sliding_window,
limit: 60,
window: 60_000
)
```
### Peeking Without Consuming
Check the current status without consuming a token:
```elixir
{:ok, %{remaining: 45, limit: 100}} =
Philiprehberger.RateLimiter.peek(:my_limiter, "user:123",
algorithm: :token_bucket,
capacity: 100,
refill_rate: 10
)
```
### Resetting Limits
```elixir
# Reset a single key
:ok = Philiprehberger.RateLimiter.reset(:my_limiter, "user:123")
# Reset all keys
:ok = Philiprehberger.RateLimiter.reset_all(:my_limiter)
```
## API
| Function | Description |
|----------|-------------|
| `start_link(opts)` | Start the rate limiter process. Requires `:name` option |
| `check(limiter, key, opts)` | Check and consume from the rate limit. Returns `{:ok, info}` or `{:error, info}` |
| `peek(limiter, key, opts)` | Check status without consuming. Returns `{:ok, info}` |
| `status(limiter, key, opts)` | Get status as a plain map |
| `reset(limiter, key)` | Reset rate limit for a specific key |
| `reset_all(limiter)` | Reset all rate limits |
### Options
| Option | Algorithm | Description |
|--------|-----------|-------------|
| `:algorithm` | Both | `:token_bucket` or `:sliding_window` (required) |
| `:capacity` | Token bucket | Maximum number of tokens (required) |
| `:refill_rate` | Token bucket | Tokens added per second (required) |
| `:cost` | Token bucket | Tokens consumed per check (default: 1) |
| `:limit` | Sliding window | Maximum requests per window (required) |
| `:window` | Sliding window | Window size in milliseconds (required) |
## Development
```bash
mix deps.get
mix compile --warnings-as-errors
mix format --check-formatted
mix test
```
## Support
If you find this project useful:
⭐ [Star the repo](https://github.com/philiprehberger/ex-rate-limiter)
🐛 [Report issues](https://github.com/philiprehberger/ex-rate-limiter/issues?q=is%3Aissue+is%3Aopen+label%3Abug)
💡 [Suggest features](https://github.com/philiprehberger/ex-rate-limiter/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement)
❤️ [Sponsor development](https://github.com/sponsors/philiprehberger)
🌐 [All Open Source Projects](https://philiprehberger.com/open-source-packages)
💻 [GitHub Profile](https://github.com/philiprehberger)
🔗 [LinkedIn Profile](https://www.linkedin.com/in/philiprehberger)
## License
[MIT](LICENSE)