Skip to main content

Async SDK for issuing and validating scoped API keys

Project description

apikeys-platform

Async Python SDK for issuing, scoping, and validating API keys — backed by SQLite or PostgreSQL.

Designed for developers who want to add API key management to their own product without building the infrastructure from scratch.

Installation

# SQLite (development / small deployments)
pip install "apikeys-platform[sqlite]"

# PostgreSQL (production)
pip install "apikeys-platform[postgresql]"

# Both drivers
pip install "apikeys-platform[all]"

Requires Python 3.11+.

Quickstart

create_tables and APIKeyClient both accept plain URLs — no need to specify the async driver suffix:

import asyncio
from apikeys import APIKeyClient, KeyMetadata, create_tables

async def main():
    # SQLite for dev:   "sqlite:///myapp.db"
    # PostgreSQL prod:  "postgresql://user:pass@host:5432/dbname"
    db_url = "sqlite:///myapp.db"
    await create_tables(db_url)   # creates tables on first run; safe to call on every restart
    client = APIKeyClient(db_url)

    # One-time setup
    org     = await client.create_organization("Acme Inc")
    product = await client.create_product(str(org.id), "My API")
    project = await client.create_project(str(org.id), "v1")
    await client.add_product_to_project(str(product.id), str(project.id))

    # Issue a key for one of your users
    result = await client.create_key(
        str(org.id),
        project_id=str(project.id),
        product_id=str(product.id),
        metadata=KeyMetadata(
            name="Alice's key",
            scopes=["read", "write"],
            rate_limit=1000,
            custom={"user_id": "u_alice", "plan": "pro"},
        ),
    )
    print(result.plaintext)   # return this once to your user

    # Validate an incoming request
    key = await client.validate_key(
        result.plaintext,
        product_id=str(product.id),
        required_scope="read",
    )
    print(key.metadata.custom["user_id"])   # → u_alice

asyncio.run(main())

Key concepts

Concept Description
Organization Top-level tenant — maps to one of your customers or your own company
Project Groups keys within an org (e.g. v1, v2, mobile)
Product A named API surface linked to projects; keys can be scoped to one product
KeyMetadata.custom Arbitrary JSON attached to a key — store user_id, plan, tenant_id, etc.

Keys are scoped from broad → narrow: org-wide → project-scoped → product-scoped.
validate_key() checks the key is active, matches the expected product, and holds the required scope.

Exceptions

from apikeys import InvalidKeyError, RevokedKeyError, InsufficientScopeError
Exception When raised
InvalidKeyError Key not found
RevokedKeyError Key exists but has been revoked
InsufficientScopeError Key is valid but missing the required scope

Full example

End-to-end script showing one-time setup, per-user key creation, request validation, and listing keys by user:

import asyncio
from apikeys import APIKeyClient, KeyMetadata
from apikeys.db.session import create_tables

async def main() -> None:
    DB_URL = "sqlite+aiosqlite:///acme_demo.db"
    await create_tables(DB_URL)
    client = APIKeyClient(DB_URL)

    # ── One-time setup ───────────────────────────────────────────────────────
    org     = await client.create_organization("Acme Corp")
    product = await client.create_product(str(org.id), "Acme App")
    project = await client.create_project(str(org.id), "Acme API v1")
    await client.add_product_to_project(str(product.id), str(project.id))

    ORG_ID     = str(org.id)
    PRODUCT_ID = str(product.id)
    PROJECT_ID = str(project.id)

    # ── Issue a key for a user ───────────────────────────────────────────────
    result = await client.create_key(
        ORG_ID,
        project_id=PROJECT_ID,
        product_id=PRODUCT_ID,
        metadata=KeyMetadata(
            name="Alice's production key",
            scopes=["read", "write"],
            rate_limit=1000,
            custom={"user_id": "u_alice", "plan": "pro"},
        ),
    )
    print(f"plaintext: {result.plaintext}")  # return this ONCE to the user

    # ── Validate an incoming request ─────────────────────────────────────────
    key = await client.validate_key(
        result.plaintext,
        product_id=PRODUCT_ID,
        required_scope="read",
    )
    print(f"user: {key.metadata.custom['user_id']}  plan: {key.metadata.custom['plan']}")

    # ── List a user's keys ───────────────────────────────────────────────────
    all_keys   = await client.list_project_keys(PROJECT_ID)
    alice_keys = [k for k in all_keys if k.metadata.custom.get("user_id") == "u_alice"]
    for k in alice_keys:
        print(f"  {k.metadata.name}  [{k.id}]  revoked={k.revoked_at is not None}")

asyncio.run(main())

Source: examples/acme_integration.py

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

apikeys_platform-0.1.1.tar.gz (9.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

apikeys_platform-0.1.1-py3-none-any.whl (9.2 kB view details)

Uploaded Python 3

File details

Details for the file apikeys_platform-0.1.1.tar.gz.

File metadata

  • Download URL: apikeys_platform-0.1.1.tar.gz
  • Upload date:
  • Size: 9.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.8

File hashes

Hashes for apikeys_platform-0.1.1.tar.gz
Algorithm Hash digest
SHA256 6dbc29b8bf5e4f3c9b619d2cf9417b2fe50d1ed047077e46a5387c5e168ff632
MD5 ea59acfb886262e1ee9336c6113342b6
BLAKE2b-256 c3a5edc777eda80bc790b964e9fc88499691be126c056d555aa91574e68c7b23

See more details on using hashes here.

File details

Details for the file apikeys_platform-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for apikeys_platform-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 9c2efd31128b0024f812a0be0b5ddbfec03bf0667bdbead930bb045c50aa8b84
MD5 0fa280a62d8e46543c5180363ef732df
BLAKE2b-256 cdf16cacd4903211b746fa9c12cbd0bd743b6c1878f7d2afe80a8cb58ee254f3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page