Skip to main content
The unkey.py SDK provides full access to Unkey’s API for managing keys, verifying requests, and rate limiting.

Installation

pip install unkey.py
Requirements: Python 3.9+

Quick Start

Initialize the client

import os
from unkey import Unkey

unkey = Unkey(root_key="unkey_...")

# Or use environment variable
unkey = Unkey(root_key=os.environ["UNKEY_ROOT_KEY"])
Never expose your root key in client-side code or commit it to version control.

Verify an API Key

Check if a user’s API key is valid:
from typing import Optional
from unkey import Unkey, ApiError

unkey = Unkey(root_key=os.environ["UNKEY_ROOT_KEY"])

def verify_request(api_key: str) -> Optional[dict]:
    """Verify an API key and return the result or None if invalid."""
    try:
        result = unkey.keys.verify_key(key=api_key)
        
        if not result.valid:
            print(f"Key invalid: {result.code}")
            return None
            
        return {
            "valid": True,
            "key_id": result.key_id,
            "meta": result.meta,
            "remaining": result.credits,
        }
        
    except ApiError as e:
        print(f"Unkey error: {e.message}")
        return None

Verification response fields

FieldTypeDescription
validboolWhether the key passed all checks
codestrStatus code (VALID, NOT_FOUND, RATE_LIMITED, etc.)
key_idstrThe key’s unique identifier
namestr?Human-readable name of the key
metadict?Custom metadata associated with the key
expiresint?Unix timestamp (in milliseconds) when the key will expire (if set)
creditsint?Remaining uses (if usage limits set)
enabledboolWhether the key is enabled
roleslist[str]?Roles attached to the key
permissionslist[str]?Permissions attached to the key
identitydict?Identity info if external_id was set when creating the key
ratelimitslist[dict]?Rate limit states (if rate limiting configured)

FastAPI example

from fastapi import FastAPI, Header, HTTPException
from unkey import Unkey
import os

app = FastAPI()
unkey = Unkey(root_key=os.environ["UNKEY_ROOT_KEY"])

@app.get("/api/protected")
async def protected_route(x_api_key: str = Header(...)):
    result = unkey.keys.verify_key(key=x_api_key)
    
    if not result.valid:
        raise HTTPException(
            status_code=401 if result.code == "NOT_FOUND" else 403,
            detail=f"Unauthorized: {result.code}"
        )
    
    return {
        "message": "Access granted",
        "key_id": result.key_id,
        "remaining_credits": result.credits
    }

Flask example

from flask import Flask, request, jsonify
from unkey import Unkey
import os

app = Flask(__name__)
unkey = Unkey(root_key=os.environ["UNKEY_ROOT_KEY"])

@app.route("/api/protected")
def protected_route():
    api_key = request.headers.get("X-API-Key")
    
    if not api_key:
        return jsonify({"error": "Missing API key"}), 401
    
    result = unkey.keys.verify_key(key=api_key)
    
    if not result.valid:
        return jsonify({"error": result.code}), 401
    
    return jsonify({
        "message": "Access granted",
        "key_id": result.key_id
    })

Create API Keys

Issue new keys for your users:
from datetime import datetime, timedelta

result = unkey.keys.create_key(
    api_id="api_...",
    
    # Optional but recommended
    prefix="sk_live",
    external_id="user_123",          # Link to your user
    name="Production key",
    
    # Optional: Expiration
    expires=(datetime.now() + timedelta(days=30)).timestamp() * 1000,
    
    # Optional: Usage limits
    remaining=1000,
    refill={
        "amount": 1000,
        "interval": "monthly",
    },
    
    # Optional: Rate limits
    ratelimit={
        "limit": 100,
        "duration": 60000,  # 100 per minute
    },
    
    # Optional: Custom metadata
    meta={
        "plan": "pro",
        "created_by": "admin",
    },
)

# Send result.key to your user (only time you'll see it!)
print(f"New key: {result.key}")
print(f"Key ID: {result.key_id}")
The full API key is only returned once at creation. Unkey stores only a hash.

Update Keys

Modify an existing key:
unkey.keys.update_key(
    key_id="key_...",
    
    name="Updated name",
    meta={"plan": "enterprise"},
    enabled=True,
    
    # Update rate limit
    ratelimit={
        "limit": 1000,
        "duration": 60000,
    },
)

Delete Keys

Permanently revoke a key:
unkey.keys.delete_key(key_id="key_...")
Or disable temporarily:
unkey.keys.update_key(
    key_id="key_...",
    enabled=False,
)

Async Support

All methods have async variants with _async suffix:
import asyncio
import os
from unkey import Unkey

async def main():
    async with Unkey(root_key=os.environ["UNKEY_ROOT_KEY"]) as unkey:
        # Async verification
        result = await unkey.keys.verify_key_async(key="sk_live_...")
        
        if result.valid:
            print("Key is valid!")
            
        # Async key creation
        new_key = await unkey.keys.create_key_async(
            api_id="api_...",
            prefix="sk_live",
        )
        print(f"Created: {new_key.key}")

asyncio.run(main())

Async FastAPI

from fastapi import FastAPI, Header, HTTPException
from unkey import Unkey
import os

app = FastAPI()

@app.get("/api/protected")
async def protected_route(x_api_key: str = Header(...)):
    async with Unkey(root_key=os.environ["UNKEY_ROOT_KEY"]) as unkey:
        result = await unkey.keys.verify_key_async(key=x_api_key)
        
        if not result.valid:
            raise HTTPException(status_code=401, detail=result.code)
        
        return {"message": "Access granted", "key_id": result.key_id}

Error Handling

import os
from unkey import Unkey, ApiError

unkey = Unkey(root_key=os.environ["UNKEY_ROOT_KEY"])

try:
    result = unkey.keys.create_key(api_id="api_...")
    print(f"Created: {result.key}")
    
except ApiError as e:
    print(f"API Error: {e.status_code} - {e.message}")
    # e.status_code - HTTP status
    # e.message - Human readable error
    # e.raw_response - Full response for debugging

Rate Limiting

Rate limiting is included in key verification, but you can also use the standalone rate limit API:
result = unkey.ratelimits.limit(
    namespace="my-app",
    identifier="user_123",
    limit=100,
    duration=60000,  # 60 seconds
)

if not result.success:
    print(f"Rate limited. Reset at: {result.reset}")
else:
    print(f"Allowed. {result.remaining} requests left")

Full Reference

GitHub

Complete auto-generated API reference
Last modified on February 6, 2026