Skip to main content
API keys are the foundation of Unkey. They provide a simple, secure way to authenticate requests to your API while giving you fine-grained control over access, usage, and permissions.

Overview

Unkey handles the complete API key lifecycle — from creation to verification to revocation — so you can focus on building your API instead of managing authentication infrastructure.

Create keys

Generate API keys programmatically via API or dashboard with custom configuration

Verify instantly

Verify keys in ~50ms globally with automatic validation of limits, expiration, and permissions

Track usage

Monitor verification patterns, track remaining credits, and analyze usage per key

Revoke immediately

Disable or delete keys with global propagation in under 60 seconds

Key Creation

Create API keys through the dashboard or API with complete control over their configuration.
import { Unkey } from "@unkey/api";

const unkey = new Unkey({ rootKey: process.env.UNKEY_ROOT_KEY });

try {
  const { meta, data } = await unkey.keys.create({
    apiId: "api_...",
    name: "Production Key - Acme Corp",
    externalId: "user_123",  // Your user ID
    meta: {
      plan: "pro",
      company: "Acme Corp",
      email: "[email protected]"
    },
    expires: Date.now() + 365 * 24 * 60 * 60 * 1000,  // 1 year
    remaining: 100000,  // Usage limit
    refill: {
      interval: "monthly",
      amount: 100000
    },
    ratelimits: [{
      name: "requests",
      limit: 100,
      duration: 60000  // 100 requests per minute
    }]
  });

  console.log(data.key);  // sk_...
} catch (err) {
  console.error(err);
}

Key Configuration Options

FieldTypeDescription
apiIdstringThe API this key belongs to (required)
namestringHuman-readable name for the key
externalIdstringYour internal user/customer ID for linking
metaobjectCustom JSON metadata (plan tier, feature flags, etc.)
Set an expiration timestamp (Unix milliseconds) to automatically invalidate keys after a certain time.
expires: Date.now() + 7 * 24 * 60 * 60 * 1000  // 7 days
Keys return code: "EXPIRED" after expiration. Perfect for trials, temporary access, or forced rotation.
Limit total requests a key can make. Each verification decrements the counter.
remaining: 1000  // Key stops working after 1000 uses
When exhausted, verification returns code: "USAGE_EXCEEDED".
Automatically restore credits on a schedule:
refill: {
  interval: "daily",    // or "monthly"
  amount: 1000,         // Credits to restore
  refillDay: 1          // For monthly: day of month (1-31)
}
Perfect for subscription models: “50,000 requests/month”.
Limit request frequency per key:
ratelimits: [
  {
    name: "requests",
    limit: 100,
    duration: 60000  // 100 per minute
  },
  {
    name: "heavy",
    limit: 10,
    duration: 60000  // 10 heavy operations per minute
  }
]
Multiple limits can be applied to different operation types.
Attach permissions for fine-grained access control:
roles: ["editor"],
permissions: ["documents.read", "documents.write"]
Checked during verification to authorize specific actions.

Key Verification

Verify keys on every request to authenticate users and enforce limits.
import { Unkey } from "@unkey/api";

const unkey = new Unkey({ rootKey: process.env.UNKEY_ROOT_KEY });

export async function handler(req: Request) {
  const key = req.headers.get("Authorization")?.replace("Bearer ", "");

  if (!key) {
    return new Response("Missing API key", { status: 401 });
  }

  try {
    const { meta, data } = await unkey.keys.verifyKey({ key });

    if (!data.valid) {
      return new Response(
        `Unauthorized: ${data.code}`,
        { status: 401 }
      );
    }

    // Access key metadata
    console.log(data.keyId);      // key_...
    console.log(data.meta);        // Custom metadata
    console.log(data.credits);     // Remaining credits
    console.log(data.identity);    // Identity info if linked

    // Continue with API logic...
    return new Response("Success!");
  } catch (err) {
    console.error(err);
    return new Response("Service unavailable", { status: 503 });
  }
}

Verification Response

When you verify a key, Unkey returns comprehensive information:
FieldTypeDescription
validbooleanWhether the key passed all checks
codestringStatus code: VALID, NOT_FOUND, EXPIRED, DISABLED, RATE_LIMITED, USAGE_EXCEEDED, INSUFFICIENT_PERMISSIONS
keyIdstringThe key’s unique identifier
namestringHuman-readable name of the key
metaobjectCustom metadata attached to the key
expiresnumberUnix timestamp (ms) when the key expires
creditsnumberRemaining uses (if usage limits set)
enabledbooleanWhether the key is enabled
rolesstring[]Roles attached to the key
permissionsstring[]Permissions attached to the key
identityobjectIdentity info if externalId was set
ratelimitsobject[]Rate limit states for each configured limit
The credits field shows remaining credits after this verification. A value of 999 means the key can be used 999 more times.

Verification Status Codes

{
  "valid": true,
  "code": "VALID",
  "keyId": "key_...",
  "credits": 9999
}

Metadata Management

Store arbitrary JSON data with each key for context and configuration.

Common Use Cases

User context

{
  "userId": "user_123",
  "email": "[email protected]",
  "company": "Acme Corp"
}

Plan information

{
  "plan": "enterprise",
  "tier": "unlimited",
  "billingId": "cus_..."
}

Feature flags

{
  "features": [
    "beta-access",
    "advanced-analytics"
  ]
}

Resource scoping

{
  "projectId": "proj_456",
  "teamId": "team_789",
  "environment": "production"
}

Update Metadata

try {
  const { meta, data } = await unkey.keys.update({
    keyId: "key_...",
    meta: {
      plan: "enterprise",  // Upgraded from pro
      features: ["analytics", "sso", "audit-logs"]
    }
  });
} catch (err) {
  console.error(err);
}

Key Revocation

Revoke access immediately when keys are compromised or users churn.
// Permanently remove the key
try {
  const { meta, data } = await unkey.keys.delete({
    keyId: "key_...",
  });
} catch (err) {
  console.error(err);
}
Use when the key should never work again. Cannot be undone.
Deletion is permanent and cannot be reversed. The key cannot be recovered. If you might need to restore access, use disable instead.

Propagation Time

  • Delete: Up to 60 seconds for global invalidation
  • Disable: Up to 60 seconds for global propagation
  • Re-enable: Immediate

Advanced Features

Custom cost per request

Charge different amounts per operation type:
// Complex query costs 10 credits
await unkey.keys.verifyKey({
  key: "sk_...",
  cost: 10
});

Permission checking

Verify permissions during authentication:
await unkey.keys.verifyKey({
  key: "sk_...",
  permissions: "documents.write"
});

Key rerolling

Generate a new key while preserving configuration:
await unkey.keys.reroll({
  keyId: "key_..."
});

Update credits

Manually adjust credit balance:
await unkey.keys.updateCredits({
  keyId: "key_...",
  operation: "increment",
  value: 5000
});

Best Practices

API keys should only be transmitted over secure connections. Never send keys in URLs or query parameters.
Never commit keys to version control. Use environment variables or secret management services.
Regularly rotate keys for security. Use the reroll feature to generate new keys while maintaining configuration.
Give keys only the permissions they need. Create separate keys for different services or operations.
Watch for unusual activity — sudden spikes, failed verifications, or geographic anomalies.
Always check the code field to provide helpful error messages to users:
  • EXPIRED: “Your API key has expired. Please generate a new one.”
  • USAGE_EXCEEDED: “You’ve reached your request limit. Upgrade for more.”
  • RATE_LIMITED: “Too many requests. Please slow down.”

Next Steps

Rate Limiting

Protect your API from abuse with flexible rate limiting

Identities

Group multiple keys under users or organizations

Analytics

Track usage patterns and verification trends

API Reference

Complete API documentation for all endpoints

Build docs developers (and LLMs) love