Metadata-Version: 2.5
Name: asgi-ratelimiter
Version: 0.3.1
Summary: Modular ASGI rate limiting for FastAPI and Starlette
Project-URL: Homepage, https://github.com/luisgcss/asgi-ratelimiter
Project-URL: Repository, https://github.com/luisgcss/asgi-ratelimiter
Project-URL: Issues, https://github.com/luisgcss/asgi-ratelimiter/issues
Project-URL: Changelog, https://github.com/luisgcss/asgi-ratelimiter/blob/master/CHANGELOG.md
Author: luisgcss
License-Expression: AGPL-3.0-or-later
License-File: LICENSE
Keywords: api,asgi,async,fastapi,middleware,rate-limiter,rate-limiting,starlette,throttling
Classifier: Development Status :: 2 - Pre-Alpha
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.14
Requires-Dist: loguru>=0.7
Provides-Extra: fastapi
Requires-Dist: fastapi>=0.140; extra == 'fastapi'
Provides-Extra: redis
Requires-Dist: redis>=5.0; extra == 'redis'
Provides-Extra: sqlite
Provides-Extra: starlette
Requires-Dist: starlette>=0.40; extra == 'starlette'
Description-Content-Type: text/markdown

# asgi-ratelimiter

[![PyPI](https://img.shields.io/pypi/v/asgi-ratelimiter.svg)](https://pypi.org/project/asgi-ratelimiter/)
[![Python](https://img.shields.io/pypi/pyversions/asgi-ratelimiter.svg)](https://pypi.org/project/asgi-ratelimiter/)
[![License](https://img.shields.io/pypi/l/asgi-ratelimiter.svg)](LICENSE)
[![CI](https://github.com/luisgcss/asgi-ratelimiter/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/luisgcss/asgi-ratelimiter/actions/workflows/ci.yml)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)

Fixed-window rate limiting for [ASGI](https://asgi.readthedocs.io/) apps.
Use **`Depends`** with [FastAPI](https://fastapi.tiangolo.com/) or **middleware** with [Starlette](https://www.starlette.io/).

SQLite by default · Redis optional · Typed · Python 3.14+

## Install

```bash
pip install "asgi-ratelimiter[fastapi]"           # FastAPI + SQLite
pip install "asgi-ratelimiter[fastapi,redis]"     # FastAPI + Redis
pip install "asgi-ratelimiter[starlette,redis]"   # Starlette + Redis
```

Or with [uv](https://docs.astral.sh/uv/): `uv add "asgi-ratelimiter[fastapi]"`.

| Extra | Adds |
|-------|------|
| `fastapi` | FastAPI integration |
| `starlette` | Starlette middleware |
| `redis` | Redis backend |
| `sqlite` | Marker only (stdlib) |

## Quick start

Define a rate:

```python
from asgi_ratelimiter import Duration, Rate

rate = Rate(limit=5, interval=Duration.MINUTE)  # 5 / min
rate = Rate(limit=100, interval=Duration.HOUR * 2)  # 100 / 2h
```

### FastAPI

```python
from fastapi import Depends, FastAPI
from asgi_ratelimiter import Duration, Rate
from asgi_ratelimiter.fastapi import RateLimiter

app = FastAPI(
	dependencies=[Depends(RateLimiter(rate=Rate(limit=5, interval=Duration.MINUTE)))]
)


@app.get("/ping")
async def ping():
	return {"ok": True}
```

Over limit → **429** + `Retry-After`.

### Starlette

```python
from starlette.applications import Starlette
from starlette.responses import PlainTextResponse
from starlette.routing import Route
from asgi_ratelimiter import Duration, Rate
from asgi_ratelimiter.starlette import RateLimitMiddleware


async def homepage(request):
	return PlainTextResponse("ok")


app = Starlette(routes=[Route("/", homepage)])
app.add_middleware(
	RateLimitMiddleware,
	rate=Rate(limit=5, interval=Duration.MINUTE),
)
```

### Redis

```python
from redis.asyncio import Redis
from asgi_ratelimiter import Duration, Rate
from asgi_ratelimiter.backends.redis import RedisBackend
from asgi_ratelimiter.fastapi import RateLimiter

redis = Redis.from_url("redis://localhost:6379/0")
limiter = RateLimiter(
	rate=Rate(limit=5, interval=Duration.MINUTE),
	backend=RedisBackend(redis=redis),
)
```

Same `backend=` pattern works with Starlette middleware.

## Logging

```python
from asgi_ratelimiter import configure_logging, set_level

configure_logging(level="DEBUG")
set_level("WARNING")
```

Off until you call `configure_logging` / `set_level` ([loguru](https://github.com/Delgan/loguru)).

## Examples

| File | Stack |
|------|--------|
| [`examples/fastapi/fastapi_sqlite.py`](examples/fastapi/fastapi_sqlite.py) | FastAPI + SQLite |
| [`examples/fastapi/fastapi_redis.py`](examples/fastapi/fastapi_redis.py) | FastAPI + Redis |
| [`examples/starlette/starlette_sqlite.py`](examples/starlette/starlette_sqlite.py) | Starlette + SQLite |
| [`examples/starlette/starlette_redis.py`](examples/starlette/starlette_redis.py) | Starlette + Redis |

```bash
uv sync --group dev
uv run --with "uvicorn[standard]" python examples/fastapi/fastapi_sqlite.py
```

## Development

```bash
uv sync --group dev
uv run pytest
uv run ruff check .
uv run ruff format .
```

See [CONTRIBUTING.md](CONTRIBUTING.md) and [AGENTS.md](AGENTS.md).

## License

[AGPL-3.0-or-later](LICENSE) · [Changelog](CHANGELOG.md) · [Issues](https://github.com/luisgcss/asgi-ratelimiter/issues)
