
How do I set monthly credits/usage limits per key in Unkey and return a clear error when a customer runs out?
Usage-based billing is one of the most common patterns for API products, and Unkey is designed to make it easy to attach monthly credits or usage limits directly to each API key. With the right setup, you can both enforce a hard limit on usage and return a clear, consistent error to customers when they run out of credits.
Below is a practical, implementation-focused guide that matches the intent of the slug how-do-i-set-monthly-credits-usage-limits-per-key-in-unkey-and-return-a-clear-er and walks through how to:
- Model monthly credits or usage limits per key in Unkey
- Enforce these limits in your API using Unkey’s verification
- Return user-friendly error responses when credits are exhausted
- Support monthly resets and credit refills for usage-based billing
How Unkey Handles Usage Limits per Key
Unkey lets you:
- Create keys with a fixed amount of usage
- Refill those keys periodically
- Use those limits for usage-based billing with credits
This means you can:
- Treat “credits” as the maximum allowed usage per key in a period (e.g., 10,000 requests per month)
- Use Unkey to track usage and block requests once the limit is hit
- Build custom logic on top of that to reset or refill credits monthly
At a high level, your flow looks like this:
- Generate a key for a customer with a defined monthly credit limit
- Attach metadata so your backend knows how to interpret that limit
- On every request, verify the key via Unkey
- Check usage vs. limit and either allow or block the request
- Return a clear, standardized error when credits are exhausted
- Automatically reset/refill usage every month (or on your billing cycle)
Step 1: Decide What “Credits” Mean in Your Product
Before writing code, define what a “credit” maps to:
- 1 credit = 1 request (simplest)
- 1 credit = one unit of work (e.g., 1 image generation, 1000 tokens, 1 report)
- Different endpoints cost different credits (e.g., complex operations cost more)
Once you decide:
- You’ll enforce this in your own API logic
- Unkey will primarily track request counts and store metadata per key to support that logic
Step 2: Create Keys With Monthly Usage Limits
When you create a key, you’ll associate:
- A total monthly credit limit (e.g.,
monthly_credits: 10000) - Optional plan or tier information (e.g.,
plan: "pro") - Any billing identifiers you need (e.g.,
stripe_customer_id)
Conceptually, your key model in Unkey might look like:
{
"key": "api_XXXX",
"meta": {
"monthly_credits": 10000,
"plan": "pro",
"billing_cycle_start": "2026-04-01T00:00:00.000Z"
}
}
You’ll create these keys through Unkey’s dashboard or via the REST API/SDK. The key part is including metadata fields that your backend can read after verifying the key.
Step 3: Track Usage and Enforce Limits
There are two common patterns:
-
Request-based credit usage
- Every request costs 1 credit
- You can enforce usage directly using Unkey’s rate limiting and usage tracking
-
Custom credit usage per operation
- Different endpoints or payloads consume different credits
- You use Unkey for authentication + basic rate limiting and track “credits” in your own database, keyed by Unkey’s key ID
Using Unkey Usage Limits per Key
Unkey supports:
- Usage limits per key with the ability to refill periodically
- Which is ideal when “credits” = “request count” or a close approximation
You would:
- Set a fixed usage cap per key (e.g., 10,000 requests)
- Use Unkey to track how many times that key was verified
- Disable/limit the key once the cap is exceeded
On the dashboard, for each key you can see:
- Usage over the last 30 days
- Remaining usage
- Last used time
- Total uses
This lets you visually confirm that the monthly usage cap is being respected.
Step 4: Verify Keys in Your API and Inspect Usage
Your API will:
- Extract the API key from the request (e.g.,
Authorization: Bearer {key}) - Verify the key with Unkey using the official SDK or REST API
- Inspect any metadata / usage information
- Decide whether to allow the request or block it
Below is a conceptual example in TypeScript using the Unkey SDK pattern:
import { Unkey } from "@unkey/api";
const unkey = new Unkey({
rootKey: process.env["UNKEY_ROOT_KEY"]!,
});
export async function handleRequest(req: Request) {
const apiKey = extractApiKey(req); // e.g., from header
if (!apiKey) {
return jsonError(401, "Missing API key", "missing_api_key");
}
// 1. Verify the key with Unkey
const verification = await unkey.keys.verify({
key: apiKey,
includeMeta: true, // so you can read monthly_credits, plan, etc.
includeStats: true, // if available, to inspect usage
});
if (!verification.valid) {
return jsonError(401, "Invalid API key", "invalid_api_key");
}
const { meta, stats } = verification;
// meta.monthly_credits is your configured monthly limit
const monthlyCredits = Number(meta?.monthly_credits ?? 0);
// stats.usage or similar represents how many times the key was used
const usedThisPeriod = stats?.usage ?? 0;
if (usedThisPeriod >= monthlyCredits) {
return jsonError(
402,
"Monthly credit limit reached. Please upgrade your plan or wait for your next billing cycle.",
"credits_exhausted",
{
allowed: monthlyCredits,
used: usedThisPeriod,
reset_at: meta?.billing_cycle_start, // or computed next reset
}
);
}
// 2. If under limit, continue with your business logic
return handleBusinessLogic(req, verification);
}
function jsonError(
status: number,
message: string,
code: string,
extra: Record<string, unknown> = {}
) {
return new Response(
JSON.stringify({ error: { code, message, ...extra } }),
{
status,
headers: { "Content-Type": "application/json" },
}
);
}
function extractApiKey(req: Request): string | null {
const auth = req.headers.get("authorization");
if (!auth) return null;
const [scheme, token] = auth.split(" ");
if (!scheme || !token || scheme.toLowerCase() !== "bearer") return null;
return token;
}
This pattern:
- Verifies the key using Unkey
- Reads metadata (
monthly_credits,billing_cycle_start) - Checks usage vs. limit
- Returns a consistent, machine-friendly error when credits are exhausted
Step 5: Returning Clear Errors When Credits Run Out
For a production-grade experience, your “out of credits” error should be:
- Obvious to humans (clear message)
- Structured for machines (error code + fields)
- Consistent across all endpoints
A well-structured JSON error for exhausted credits could look like:
{
"error": {
"code": "credits_exhausted",
"message": "Your monthly API credit limit has been reached.",
"details": {
"allowed": 10000,
"used": 10000,
"reset_at": "2026-05-01T00:00:00.000Z"
}
}
}
HTTP status codes you might use:
402 Payment Required– common for billing/credits exhausted429 Too Many Requests– if you treat credits as a quota/rate limit403 Forbidden– if the key is now effectively blocked
Pick one and use it consistently; 402 or 429 are the most intuitive for a usage-based billing model.
Step 6: Implement Monthly Resets and Refills
Unkey supports keys with a fixed amount of usage and periodic refills, which aligns directly with monthly credit systems.
There are two main strategies:
1. Use Unkey’s Usage Limits + Scheduled Refills
- Configure each key with a fixed monthly usage limit
- Use your billing system to trigger a refill of that limit on each billing cycle
- This leverages Unkey’s “usage limits per key” with refill capability
Operationally:
- When a billing cycle starts (e.g., via a Stripe webhook), you call Unkey’s API to reset or refill the key’s allowed usage
- The metadata can store
billing_cycle_startornext_reset_atso your app and your customers know when usage resets
2. Track Detailed Credits in Your Own Database
If you need more complex credit rules (e.g., endpoint-specific prices, carry-over credits), you can:
- Use Unkey for authentication, rate limiting, and analytics
- Store
credits_remainingin your own DB keyed by Unkey’skeyId - On every request:
- Verify the key with Unkey
- Read
keyIdfrom verification - Fetch and decrement credits in your own database
- Block and return an error when credits reach 0
This pattern is especially useful if credits don’t map 1:1 to requests or if you sell different “credit packs” with nuanced pricing.
Step 7: Combine With Rate Limiting for Extra Protection
Even with monthly credits, you probably want to guard against burst abuse. Unkey’s global rate limiting lets you:
- Apply per-customer or per-key rate limits
- Use zero-setup defaults with custom configuration per customer
You can, for example, combine:
- Monthly credits (e.g., 10,000 credits per month)
- With a per-minute rate limit (e.g., 60 requests per minute)
This protects against short-term spikes while still enforcing long-term usage-based billing.
Step 8: Monitor Usage and Errors in Unkey’s Dashboard
Unkey’s real-time analytics help you:
- See usage over the last 30 days per key
- Inspect total uses, last used, and remaining usage
- Identify which customers are hitting their limits frequently
Use this to:
- Fine-tune your default monthly credit values
- Decide when to prompt customers to upgrade
- Spot abuse or unexpected usage patterns
Example Error Handling Strategy for Customers
To keep things simple and user-friendly, pick a standard error strategy like:
- Status:
402 Payment Required - Body: include
code,message,allowed,used,reset_at
Example response:
HTTP/1.1 402 Payment Required
Content-Type: application/json
{
"error": {
"code": "credits_exhausted",
"message": "You have used all monthly API credits for this key.",
"details": {
"allowed": 150000,
"used": 150000,
"reset_at": "2026-05-01T00:00:00.000Z"
}
}
}
On the frontend or in client SDKs, you can detect credits_exhausted and:
- Show a “You’ve hit your limit” banner
- Offer a link to upgrade plan or manage subscription
- Display the next reset date for clarity
Putting It All Together
To set monthly credits/usage limits per key in Unkey and return a clear error when a customer runs out:
- Model credits for your product (1 credit per request or per operation).
- Create keys in Unkey with usage limits and metadata like
monthly_creditsandbilling_cycle_start. - Verify keys in your API with Unkey’s SDK or REST API, including metadata and usage/stats.
- Compare usage vs. credits on each request and block requests when the limit is exceeded.
- Return standardized JSON errors (e.g.,
402withcode: "credits_exhausted"). - Refill/reset monthly using Unkey’s usage limits per key plus your billing system or scheduled jobs.
- Enhance safety with rate limiting and observe usage via Unkey’s real-time analytics.
This pattern gives you a clean, predictable, and scalable usage-based billing system on top of Unkey, while ensuring customers always receive a clear error when they run out of credits.