django-api-usage
Lightweight, privacy-first usage metering for Django APIs, with an optional deprecation lifecycle layer on top.
It answers two questions that decide whether an endpoint can be changed or removed:
- How much is this endpoint (or this whole Django app) used?
- Who is calling it, so they can be contacted before it changes?
The package is deliberately generic: the core is plain Django middleware (works with or without Django REST Framework), it never breaks a request because metering failed, and it stores no raw personal data by default.
Why not just drf-api-tracking?
drf-api-tracking stores one database row per request (including bodies), which
is heavy and privacy-sensitive. django-api-usage accumulates counters and
keeps consumer attribution hashed and opt-in. That makes it suitable both for
continuous usage analytics and for the concrete decision of deprecating an
endpoint.
Install
pip install django-api-usage
INSTALLED_APPS = [
# ...
"django_api_usage",
]
MIDDLEWARE = [
# ...
"django_api_usage.middleware.ApiUsageMiddleware",
]
Metering starts immediately. Counters are buffered in the cache and written to the database in batches:
python manage.py api_usage_flush # from cron, or a Celery beat task
python manage.py api_usage_report --days 30
python manage.py api_usage_report --days 90 --sunset-candidates
With Celery (and the celery extra) two tasks are provided:
django_api_usage.tasks.flush_api_usage and
django_api_usage.tasks.prune_api_usage.
Configuration
API_USAGE = {
"ENABLED": True,
"FAIL_OPEN": True,
"BUFFER_BACKEND": "cache", # "cache" (batched) or "db" (write per request)
"RETENTION_DAYS": 90,
"TRACK_CONSUMERS": False, # opt-in; stores hashed callers only
"CONSUMER_SALT": "a-rotatable-secret",
}
All resolvers are overridable, so no project-specific knowledge leaks into the package:
API_USAGE = {
"APP_LABEL_RESOLVER": "myproject.api.resolvers.app_label",
"SITE_RESOLVER": "myproject.api.resolvers.site_id",
"ROLE_SCOPES_RESOLVER": "myproject.api.resolvers.role_scopes",
}
Admin
The package ships a read-only admin for the three models, built with plain
Django — no dependency beyond Django itself (no django-object-actions or
similar):
- Endpoint stats changelist: a search box (by endpoint path, route name, app,
method or replacement), filters by date, profile and role, and the sum of
the
countcolumn for the rows matching the current filters. - Flush now: moves counters buffered in the cache into the database, so they
appear immediately when you run with
BUFFER_BACKEND = "cache". - Clear statistics: empties the aggregated counters behind a confirmation page (endpoints and their deprecation metadata are kept).
- Export selected rows to CSV on all three admins. Relations are flattened into one analysis-friendly table (a stat row carries its endpoint's path, app and method), so the file can be fed straight to pandas or a spreadsheet. Use the "select all" link to export every row matching the current filters.
Deprecation lifecycle
Endpoint carries deprecated, sunset_date, replacement and owner, so the
same data that measures usage also drives a deprecation plan. See the consuming
project's migration guide for the full workflow (measure → notify → enforce).
DRF shadow mode
django_api_usage.drf.HasAPIScope enforces scopes declared on a view:
class AllUserViewSet(viewsets.ReadOnlyModelViewSet):
permission_classes = [HasAPIScope]
required_scopes = ["user:read_all"]
With API_ENFORCEMENT = "report" (the default) nothing is blocked yet: a
X-Api-Would-Deny header is returned instead, so you can deploy a permission and
measure what it would have denied before enforcing it.
Status
Alpha. The core is implemented: metering middleware, models and migrations, management commands, the deprecation layer and a DRF shadow-mode permission. Tested on Python 3.10-3.12 and Django 4.2/5.2. MIT licensed.
Development
python -m django test tests --settings=tests.settings
ruff check .
black --check .
Metadata
Release files for django-api-usage 0.1.5
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| django_api_usage-0.1.5.tar.gz | 25.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_api_usage-0.1.5-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 50.6 kB
Release files / django_api_usage-0.1.5.tar.gz
| Download URL | django_api_usage-0.1.5.tar.gz |
|---|---|
| Size | 25.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
772c56389a9d46e2ec3c396fdee21de3753c92fb5051ad0c5d35bbc509d692de
|
|
BLAKE2b-256 checksum How to use checksums |
f555fa96e92b7d2dcf6adb0b2e5fd3246e950df00f003eca4b0397877e397d95
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.25
|
Release files / django_api_usage-0.1.5-py3-none-any.whl
| Download URL | django_api_usage-0.1.5-py3-none-any.whl |
|---|---|
| Size | 25.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
59b88e10c04c6609c6faefa9c2300af968c44670eb707835a3618a87918fa09f
|
|
BLAKE2b-256 checksum How to use checksums |
d2d8fcc46534ce2eea99c9f1608f97840d3a9ebac6679595e918c38457d55ac1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.25
|