Documentation Index
Fetch the complete documentation index at: https://mintlify.com/better-auth/better-auth/llms.txt
Use this file to discover all available pages before exploring further.
The API Key plugin lets you create and manage API keys for your application. It supports rate limiting, custom expiration, permissions, metadata, organization-owned keys, and optional Redis-backed storage.
Installation
Install the package
npm install @better-auth/api-key
Add the plugin to your auth config
import { betterAuth } from "better-auth"
import { apiKey } from "@better-auth/api-key"
export const auth = betterAuth({
plugins: [
apiKey()
]
})
Add the client plugin
import { createAuthClient } from "better-auth/client"
import { apiKeyClient } from "@better-auth/api-key/client"
export const authClient = createAuthClient({
plugins: [
apiKeyClient()
]
})
Usage
Create an API key
const { data: apiKey } = await authClient.apiKey.create({
name: "My Project Key",
expiresIn: 60 * 60 * 24 * 30, // 30 days in seconds (optional)
prefix: "proj_", // optional custom prefix
metadata: { environment: "production" }, // optional
})
// The key value is only returned once on creation
console.log(apiKey.key) // "proj_abc123..."
The raw key value is only returned at creation time. Store it securely — it cannot be retrieved again.
Creating a key server-side (for a specific user):
const apiKey = await auth.api.createApiKey({
body: {
name: "CI/CD Key",
userId: "user-id",
permissions: {
files: ["read", "write"],
users: ["read"],
},
},
})
Verify an API key
Verification is always server-side. Pass the key from the incoming request:
const result = await auth.api.verifyApiKey({
body: {
key: request.headers.get("x-api-key"),
// Optional: check required permissions
permissions: {
files: ["read"],
},
},
})
if (result.valid) {
// result.key contains the API key details (except the raw key value)
console.log(result.key.name, result.key.referenceId)
} else {
console.error(result.error?.message)
}
By default, Better Auth looks for the API key in the x-api-key request header.
Get an API key
const { data: key } = await authClient.apiKey.get({
id: "api-key-id",
})
// Returns everything except the raw key value
List API keys
const { data } = await authClient.apiKey.list({
query: {
limit: 10,
offset: 0,
sortBy: "createdAt",
sortDirection: "desc",
},
})
// { apiKeys, total, limit, offset }
// List organization-owned keys:
const { data: orgKeys } = await authClient.apiKey.list({
query: { organizationId: "org-id" },
})
Update an API key
await authClient.apiKey.update({
keyId: "api-key-id",
name: "Renamed Key",
// Server-only fields require using auth.api.updateApiKey:
// enabled, remaining, refillAmount, refillInterval,
// metadata, expiresIn, rateLimitEnabled, rateLimitTimeWindow,
// rateLimitMax, permissions
})
Delete an API key
await authClient.apiKey.delete({
keyId: "api-key-id",
})
Permissions
API keys support fine-grained, resource-based permissions.
Set default permissions
apiKey({
permissions: {
defaultPermissions: {
files: ["read"],
users: ["read"],
},
// Or dynamically:
// defaultPermissions: async (referenceId, ctx) => ({
// files: ["read"],
// }),
},
})
Create a key with permissions
const key = await auth.api.createApiKey({
body: {
name: "Read-only Key",
userId: "user-id",
permissions: {
files: ["read"],
projects: ["read"],
},
},
})
Verify permissions
const result = await auth.api.verifyApiKey({
body: {
key: "api-key-value",
permissions: { files: ["write"] }, // will be false if key only has ["read"]
},
})
Using API keys for authentication
Enable session creation from API keys to allow API key holders to authenticate as the associated user:
apiKey({
enableSessionForAPIKeys: true,
})
With this enabled, if a valid API key is found in the x-api-key header, Better Auth will mock a user session for the request.
Configuration
import { apiKey } from "@better-auth/api-key"
apiKey({
// Unique ID for this configuration (required with multiple configs)
configId: "default",
// Who owns the key: "user" | "organization" (default: "user")
references: "user",
// Header name to read API key from (default: "x-api-key")
apiKeyHeaders: "x-api-key",
// Length of generated keys (default: 64)
defaultKeyLength: 64,
// Default prefix for all keys
defaultPrefix: "sk_",
// Require a name on key creation
requireName: false,
// Enable storing metadata on keys
enableMetadata: true,
// Key expiration config
keyExpiration: {
defaultExpiresIn: null, // null = never expires
disableCustomExpiresTime: false,
minExpiresIn: 1, // minimum days
maxExpiresIn: 365, // maximum days
},
// Rate limiting
rateLimit: {
enabled: true,
timeWindow: 60 * 1000, // 1 minute window
maxRequests: 100, // max requests per window
},
// Enable session creation from API keys
enableSessionForAPIKeys: false,
})
Secondary storage (Redis)
For high-performance API key lookups, store keys in Redis instead of your primary database:
import { betterAuth } from "better-auth"
import { apiKey } from "@better-auth/api-key"
export const auth = betterAuth({
secondaryStorage: {
get: async (key) => await redis.get(key),
set: async (key, value, ttl) => {
if (ttl) await redis.set(key, value, { EX: ttl })
else await redis.set(key, value)
},
delete: async (key) => await redis.del(key),
},
plugins: [
apiKey({
storage: "secondary-storage",
}),
],
})
Multiple configurations
You can run multiple API key configurations side-by-side (e.g., public and private keys) by passing an array directly to apiKey(). Each configuration must have a unique configId:
import { apiKey } from "@better-auth/api-key"
betterAuth({
plugins: [
apiKey([
{ configId: "public", defaultPrefix: "pk_" },
{ configId: "secret", defaultPrefix: "sk_" },
]),
],
})
Specify configId when creating or verifying keys to use a specific configuration.
Schema
The API Key plugin creates an apikey table:
| Field | Type | Description |
|---|
id | string | Primary key |
configId | string | Configuration ID this key belongs to |
name | string | Optional human-readable name |
start | string | First few characters of the key (for display) |
prefix | string | Key prefix (stored in plaintext) |
key | string | Hashed API key |
referenceId | string | Owner ID (user ID or organization ID) |
enabled | boolean | Whether the key is active |
expiresAt | Date | Expiration date |
rateLimitEnabled | boolean | Whether rate limiting is active |
rateLimitTimeWindow | number | Rate limit time window in ms |
rateLimitMax | number | Max requests per window |
requestCount | number | Requests made in current window |
remaining | number | Remaining requests (if using remaining count) |
refillAmount | number | Amount to refill remaining count |
refillInterval | number | Refill interval in ms |
permissions | string | JSON-serialized permissions |
metadata | string | JSON-serialized metadata |
createdAt | Date | Creation timestamp |
updatedAt | Date | Last update timestamp |
API keys are hashed before storage by default. Disabling hashing (disableKeyHashing: true) is strongly discouraged as it exposes raw keys if your database is breached.