Skip to content

Quick Start

Weighted costs

class BulkImportView(APIView):
    throttle_classes = [
        rate_limit(rate="1000/h", cost=lambda request: len(request.data.get("rows", [1]))),
    ]

A request importing 50 rows consumes 50 units from the hourly budget — proportional to actual cost, not counted as "one request" the same as a single-row import.

Plan tiers

class ReportView(APIView):
    throttle_classes = [
        rate_limit(
            rate={"free": "10/h", "pro": "100/h", "enterprise": "unlimited"},
            key="user",
        )
    ]

Wait — "unlimited" isn't a valid rate string; for an effectively unlimited tier, use a very high number instead (there's no dedicated "unlimited" sentinel, keeping the rate format uniform):

rate={"free": "10/h", "pro": "100/h", "enterprise": "1000000/h"}

By default, the tier is read from request.user.plan. Override with tier_resolver= for any other lookup (a related Subscription model, a JWT claim, ...):

rate_limit(
    rate={"free": "10/h", "pro": "100/h"},
    tier_resolver=lambda request: request.auth.get("plan", "free") if request.auth else "free",
)

Composed keys

from drf_ratelimit_plus import combine

rate_limit(rate="1000/h", key=combine("tenant", "user"))

Produces keys like tenant:acme|user:42 — each user gets an independent budget, but nested within their tenant's namespace (useful if you also want a separate, higher-level per-tenant limit using a different throttle).

Multiple throttles on one view

class ArticleViewSet(RateLimitHeadersMixin, viewsets.ModelViewSet):
    throttle_classes = [
        rate_limit(rate="1000/h", key="tenant", scope="tenant-wide"),
        rate_limit(rate="100/h", key="user"),
    ]

Both are checked; DRF denies the request if either throttle denies it. RateLimitHeadersMixin reports the tightest (lowest remaining) of the two in the response headers, since that's the one closest to being hit.

Custom key functions

def by_organization_and_endpoint(request, view=None):
    org_id = request.user.organization_id
    endpoint = view.__class__.__name__ if view else request.path
    return f"org:{org_id}:{endpoint}"

rate_limit(rate="500/h", key=by_organization_and_endpoint)

Checking a result programmatically

from drf_ratelimit_plus.algorithms import token_bucket_check
from drf_ratelimit_plus.rates import parse_rate
from drf_ratelimit_plus.client import get_redis_client

result = token_bucket_check(get_redis_client(), "my-key", parse_rate("10/m"), burst=10)
print(result.allowed, result.remaining, result.reset_seconds)

Useful for building a custom admin dashboard showing current rate-limit state, or for a pre-flight check before an expensive client-side operation.

Next steps

  • Advanced Usage — writing custom key functions, combining with authentication, testing.
  • Performance — what each algorithm costs.