Skip to main content

django-tenant-apikeys

PyPI version Python versions Tests License: MIT Latest on Django Packages Documentation

django-tenant-apikeys

Multi-tenant API key authentication for Django

Issue a key per tenant, hash it before it ever touches the database, gate access with scopes instead of an all-or-nothing flag, restrict it by IP, rate-limit it, and rotate or revoke it later without touching your own code. First-class Django REST Framework and Django Ninja support.

If you've built API key auth for a SaaS product before, you've probably written this same code three or four times: generate a random token, hash it, store the hash, look it up on every request, and figure out how to show the raw key to the user exactly once — then bolt on a tenant relation, a scopes field, and an IP allowlist by hand. django-tenant-apikeys is that code, written once and tested against every rejection path, not just the happy one.

  • Bring your own tenant model. Subclass one abstract model, add a tenant foreign key, and request.tenant is resolved automatically on every authenticated request.
  • Nothing sensitive is stored. Keys are secrets-generated, kept only as a SHA-256 hash, checked with a constant-time comparison, and shown to you exactly once.
  • Scopes, not just on/off. "orders:read", a whole namespace with "orders:*", or everything with "*".
  • Per-key IP allowlists and rate limits. IPv4/IPv6, CIDR networks, and a configurable per-key request limit — both optional, both opt-in.
  • A real lifecycle. rotate() issues a replacement and revokes the original in one call; revoke()/reactivate() manage access directly.
  • Small, framework-agnostic core. The model doesn't know DRF or Ninja exist. Everything framework-specific is an optional adapter on top.

Install

pip install django-tenant-apikeys[drf]
# myapp/models.py
class OrganizationAPIKey(AbstractTenantAPIKey):
    tenant = models.ForeignKey(Organization, related_name="api_keys", on_delete=models.CASCADE)
instance, raw_key = OrganizationAPIKey.generate_key(
    name="CI deploy key",
    tenant=org,
    scopes=["deployments:write"],
)
print(raw_key)  # tak_live_3f9a2c1d.k7pQ2m1v... -- shown once, never stored
# myapp/views.py
class DeploymentsView(APIView):
    authentication_classes = [TenantAPIKeyAuthentication]
    permission_classes = [HasAPIKeyScope]
    required_scopes = ["deployments:write"]

    def post(self, request):
        request.tenant.deployments.create(...)  # attached automatically

Full walkthrough: Quickstart. A complete runnable project lives in examples/simple_saas/ — clone the repo, run python manage.py create_demo_key, and you have a real tenant-scoped key and a working curl command in about a minute.

Documentation

Full documentation, including configuration, IP restrictions, rate limiting, the Django Ninja integration, and the complete API reference, is at stackadnan.github.io/django-tenant-apikeys.

Installation · Quickstart · Configuration Getting started
Authentication · Multi-tenancy · Scopes · Key lifecycle Core concepts
Environments · IP restrictions · Rate limiting · Metadata Per-key policies
Django REST Framework · Django Ninja Framework integrations
Admin · Management commands · Security · Testing · Troubleshooting Operating it
API reference Reference

The wiki has a few longer standalone articles — a factual comparison against djangorestframework-api-key, shared-schema vs. schema-per-tenant multi-tenancy, and a general API-key security checklist — that go deeper than the reference docs on those specific topics.

Supported versions

  • Python 3.10, 3.11, 3.12, 3.13, 3.14
  • Django 4.2, 5.2 and 6.1, on the Python versions each supports (tested in CI; other 4.x/5.x/6.x releases are likely to work but aren't part of the test matrix)
  • Django REST Framework ≥ 3.14 and/or django-ninja ≥ 1.0, both optional

Development

git clone https://github.com/stackadnan/django-tenant-apikeys
cd django-tenant-apikeys
pip install -e ".[dev]"
pytest --cov=django_tenant_apikeys --cov-report=term-missing   # 100% coverage enforced
ruff check .
mypy django_tenant_apikeys

Those three commands are exactly what CI runs on every push and PR — see CONTRIBUTING.md for the full workflow, and Testing for running against PostgreSQL and testing your own authentication code.

License

MIT

Metadata

Release files for django-tenant-apikeys 0.4.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-tenant-apikeys 0.4.1
File Size Uploaded
django_tenant_apikeys-0.4.1.tar.gz 44.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for django-tenant-apikeys 0.4.1
File Interpreter ABI Platform
django_tenant_apikeys-0.4.1-py3-none-any.whl Python 3 none any Details

Total release size: 72.8 kB

Release files / django_tenant_apikeys-0.4.1.tar.gz

Download URL django_tenant_apikeys-0.4.1.tar.gz
Size 44.8 kB
Tags Source
SHA-256 checksum
How to use checksums
c4f245a90de330e3b444eab082b9c78b206e7e533c4b6d462c024b38677d363e
BLAKE2b-256 checksum
How to use checksums
693fcb5974fd557ac40ebb341a2635c95cb48a5956378a681985a1e81b981180
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release files / django_tenant_apikeys-0.4.1-py3-none-any.whl

Download URL django_tenant_apikeys-0.4.1-py3-none-any.whl
Size 27.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
86aec96001612e3b1d69826bbc1a979493426f3bf82d6af490bd09a72963c5aa
BLAKE2b-256 checksum
How to use checksums
494ea593b914867612b5d57711d129261d3d7c4b1ac06b4778aec3cadf9158b4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release history Release notifications | RSS feed

0.6.0

2 release files

0.5.0

2 release files

This release

0.4.1 This release

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page