Skip to main content

fastapi-keygate

CI

API-key authentication, per-key rate limiting and per-key usage tracking for FastAPI — in a few lines, with zero setup (SQLite by default).

pip install fastapi-keygate
from fastapi import Depends, FastAPI
from keygate import ApiKey, KeyGate

app = FastAPI()
gate = KeyGate(app, admin_secret="change-me")  # SQLite, zero setup

@app.get("/hello")
def hello(key: ApiKey = Depends(gate.require)):
    return {"hello": key.owner_id}

if not gate.list_keys():  # demo only: mint a first key
    print("Your API key:", gate.create_key("demo-tenant", per_minute=5).key)
uvicorn app:app
curl -H "X-API-Key: kg_..." localhost:8000/hello   # 200, then 429 after 5/minute

(The package is fastapi-keygate on PyPI; you import keygate.)

Try it in 2 minutes

pip install fastapi-keygate uvicorn
curl -O https://raw.githubusercontent.com/codeSudee/fastapi-keygate/main/examples/app.py
uvicorn app:app

The terminal prints Your API key: kg_.... In a second terminal (paste your key):

curl -i localhost:8000/hello                              # 401: no key
curl -i -H "X-API-Key: kg_..." localhost:8000/hello       # 200, run it 6 times -> 429
keygate usage 1                                           # see what was counted
keygate create alice --per-minute 100                     # hand out another key

What you get

  • Auth — Depends(gate.require) returns the caller's ApiKey (id, owner_id, limits). Missing/invalid/revoked → 401. Keys are read from X-API-Key or Authorization: Bearer, never from query strings (those leak into logs).
  • Per-key rate limits — requests per minute and/or hour, stored on each key. Over limit → 429 with Retry-After; successful responses carry X-RateLimit-* headers.
  • Usage tracking — every authenticated request is logged (timestamp, method, route, status).
  • Key management — from Python or an opt-in admin API.

Keys look like kg_<prefix>_<secret>. Only a SHA-256 hash is stored; the plaintext is shown once at creation.

Managing keys

k = gate.create_key("acme", name="prod", per_minute=60, per_hour=1000)
k.key                      # plaintext, only available here
gate.list_keys("acme")     # [ApiKey, ...]
gate.revoke(k.id)
gate.usage(k.id, last_hours=24)
# UsageStats(total=…, by_status={200: …, 429: …}, by_endpoint={"GET /hello": …}, …)

Give keys scopes and guard routes with them (missing scope → 403; the "*" scope matches everything):

k = gate.create_key("acme", scopes=["read", "write"])

@app.delete("/items/{id}")
def delete(id: int, key: ApiKey = Depends(gate.require_scopes("write"))): ...

Rotate a key without downtime: new = gate.rotate(k.id, grace=timedelta(hours=24)) returns a replacement with the same owner, limits and scopes, and the old key keeps working until the grace period ends (POST /keygate/keys/{id}/rotate?grace_hours=24 in the admin API). Rate-limit counters are per key, so during the grace period the two keys each get their own allowance.

Keys never expire unless you set expires_at= (naive UTC datetime) or expires_in=timedelta(days=30); expired keys get 401. Omitted limits use KeyGate(default_per_minute=60, default_per_hour=None); pass None for unlimited.

With admin_secret set, these endpoints are mounted (header X-Admin-Secret). Without it, nothing is exposed.

Method Path
POST /keygate/keys {"owner_id": "acme", "per_minute": 30, "expires_at": "2027-01-01T00:00:00", "scopes": ["read"]} → returns the key once
GET /keygate/keys?owner_id=acme list
DELETE /keygate/keys/{id} revoke
POST /keygate/keys/{id}/rotate?grace_hours=24 replacement key, old one expires after the grace period
GET /keygate/keys/{id}/usage?last_hours=24 stats
DELETE /keygate/usage?older_than_days=90 delete old usage rows

Async apps

Using an async SQLAlchemy driver? AsyncKeyGate has the same features, with await:

pip install "fastapi-keygate[async]"        # + aiosqlite; use [asyncpg] for Postgres
from keygate import ApiKey, AsyncKeyGate

gate = AsyncKeyGate(app, db_url="sqlite+aiosqlite:///keygate.db", admin_secret="change-me")

@app.get("/hello")
async def hello(key: ApiKey = Depends(gate.require)):
    return {"hello": key.owner_id}

k = await gate.create_key("acme", per_minute=60)   # every management method is a coroutine

Tables are created on first use (or await gate.init()). For a shared rate-limit store use limits_uri="async+redis://host:6379" (uses the redis extra). Usage rows are written immediately (no batching in the async gate).

Command line

No code needed to manage keys (--db or KEYGATE_DB points at your database, default sqlite:///keygate.db):

keygate create acme --per-minute 60 --scope read --expires-days 30   # prints the key once
keygate list [--owner acme] [--all]
keygate rotate 1 --grace-hours 24
keygate revoke 1
keygate usage 1 --last-hours 24
keygate prune --older-than-days 90

Configuration

KeyGate(
    app,
    db_url="postgresql+psycopg://user:pw@host/db",  # any SQLAlchemy URL
    limits_uri="redis://localhost:6379",             # shared rate-limit store
    admin_secret=...,
)

Things to know

  • Multiple workers: the default rate-limit store is in memory, per process. With N Uvicorn/Gunicorn workers a 60/min limit becomes up to 60×N. Use limits_uri="redis://..." (pip install fastapi-keygate[redis]) in that case.
  • Why not slowapi? Rate limiting uses limits, the engine slowapi wraps. slowapi is built around per-endpoint decorators that need a request argument; keygate's limits belong to the key, so they're enforced inside the auth dependency. You can still use slowapi alongside it.
  • Usage table growth: every authenticated request adds a row. Call gate.prune_usage(timedelta(days=90)) periodically (or DELETE /keygate/usage?older_than_days=90) to delete old rows.
  • Busy apps: KeyGate(app, usage_batch_size=100) buffers usage rows in memory and writes them in one commit. They are flushed when the buffer fills, on gate.flush_usage(), before gate.usage() / gate.prune_usage(), and at exit. Rows still buffered are lost if the process crashes.
  • Schema: tables are created on startup (create_all); there are no migrations yet.
  • Not included (yet): Alembic migrations.
  • Timestamps are naive UTC.

Development

pip install -e ".[dev]" && pytest

MIT licensed.

Web dashboard

A built-in UI for viewing keys, usage graphs, live rate-limit status, and editing, rotating or revoking keys.

from keygate.dashboard import mount_dashboard

gate = KeyGate(app, admin_secret="change-me")
mount_dashboard(app, gate)  # open /keygate/dashboard and sign in with the admin secret

The fastest way to see your own: pip install -U fastapi-keygate uvicorn, then keygate quickstart. It starts an empty dashboard with one protected route, /hello, and prints the link and an admin secret. Generate a key on the API keys page and the dialog gives you a ready-to-run curl command. To put keys in front of a paid API such as OpenAI, see examples/openai_proxy.py; your OpenAI key stays in an environment variable and is never a Keygate key.

The API lives under /keygate/dashboard/api and reads straight from the gate's own key and usage tables. Try it with sample traffic: python examples/dashboard_demo.py (secret: demo-secret), or click around the live demo (sample data, nothing saved).

The UI source is in dashboard/ (React, TypeScript, Tailwind, Framer Motion). Build it into the package with cd dashboard && npm install && npm run build, or run npm run dev for hot reload against a local API on :8000.

Plans (optional)

Cap what each owner can do. Plans are off by default:

from keygate import DEFAULT_PLANS, KeyGate

gate = KeyGate(app, admin_secret="change-me", plans=DEFAULT_PLANS)  # free / pro / team
gate.set_plan("acme", "pro")

Each owner_id gets a plan that limits active keys, per-key rate limits and scopes. Going over returns 402 with a plan_limit payload. Billing is not included: assign plans from your own code (for example from a payment webhook) with set_plan(owner_id, plan, billing_status=...).

Metadata

Release files for fastapi-keygate 0.5.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 fastapi-keygate 0.5.1
File Size Uploaded Provenance
fastapi_keygate-0.5.1.tar.gz 544.0 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for fastapi-keygate 0.5.1
File Interpreter ABI Platform
fastapi_keygate-0.5.1-py3-none-any.whl Python 3 none any Details

Total release size: 1.0 MB

Release files / fastapi_keygate-0.5.1.tar.gz

Download URL fastapi_keygate-0.5.1.tar.gz
Size 544.0 kB
Tags Source
SHA-256 checksum
How to use checksums
cc24f077082e3ac4f52674c5c457a4624c2b6bc42e6bbf6618a81a2a615f1edd
BLAKE2b-256 checksum
How to use checksums
987151a062ea6f3de3672e66ba773bb7ff03d59950be0113aa011924652490aa
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 21, 2026.

Transparency log

Release files / fastapi_keygate-0.5.1-py3-none-any.whl

Download URL fastapi_keygate-0.5.1-py3-none-any.whl
Size 462.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
91a6b1ce0ec2671ee610ac316e7cc3a5078c94e15d6114d6a364c2b4a19f18cd
BLAKE2b-256 checksum
How to use checksums
1902d1b75f2872f3cd700c9651e4bf11d284f41ad7c17739e938bb1a9a72303f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 21, 2026.

Transparency log

Security for fastapi-keygate 0.5.1

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for fastapi_keygate-0.5.1.tar.gz

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 Oct 21, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

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