Documentation Index Fetch the complete documentation index at: https://unkey.com/docs/llms.txt
Use this file to discover all available pages before exploring further.
What you’ll build
An Express server with a protected /secret route that requires a valid API key. Requests without a valid key get rejected with a 401.
Time to complete: ~5 minutes
Prerequisites
Want to skip ahead? Clone the complete example and run it locally.
Create your Express app mkdir unkey-express && cd unkey-express
npm init -y
npm install express @unkey/api dotenv
Add your root key Get a root key from Settings → Root Keys and create a .env file: UNKEY_ROOT_KEY = "unkey_..."
Never commit your .env file. Add it to .gitignore.
Create your server Create index.js with a protected route: const express = require ( "express" );
const { Unkey } = require ( "@unkey/api" );
require ( "dotenv" ). config ();
const app = express ();
const port = process . env . PORT || 3000 ;
const unkey = new Unkey ({ rootKey : process . env . UNKEY_ROOT_KEY });
// Public route
app . get ( "/" , ( req , res ) => {
res . json ({ message : "Welcome! Try /secret with an API key." });
});
// Protected route
app . get ( "/secret" , async ( req , res ) => {
// 1. Extract the key from the Authorization header
const authHeader = req . headers . authorization ;
const key = authHeader ?. replace ( "Bearer " , "" );
if ( ! key ) {
return res . status ( 401 ). json ({ error : "Missing API key" });
}
// 2. Verify with Unkey
try {
const { data } = await unkey . keys . verifyKey ({ key });
if ( ! data . valid ) {
// Key is invalid, expired, rate limited, etc.
return res . status ( 401 ). json ({
error : "Invalid API key" ,
code : data . code ,
});
}
// 3. Key is valid, return protected data
res . json ({
message : "Welcome to the secret route!" ,
keyId : data . keyId ,
// Include any metadata you attached to the key
identity : data . identity ,
});
} catch ( err ) {
console . error ( err );
return res . status ( 500 ). json ({ error : "Could not verify key" });
}
});
app . listen ( port , () => {
console . log ( `Server running at http://localhost: ${ port } ` );
});
Test it First, create a test key in your Unkey dashboard , then: curl http://localhost:3000/secret \
-H "Authorization: Bearer YOUR_API_KEY"
You should see: {
" message " : "Welcome to the secret route!" ,
" keyId " : "key_..." ,
" identity " : null
}
Now try without a key: curl http://localhost:3000/secret
You’ll get: {
" error " : "Missing API key"
}
What’s in data?
After successful verification, data contains:
Field Type Description validbooleanWhether the key passed all checks codestringStatus code (VALID, NOT_FOUND, RATE_LIMITED, etc.) keyIdstringThe key’s unique identifier namestring?Human-readable name of the key metaobject?Custom metadata associated with the key expiresnumber?Unix timestamp (in milliseconds) when the key will expire. (if set) creditsnumber?Remaining uses (if usage limits set) enabledbooleanWhether the key is enabled rolesstring[]?Permissions attached to the key permissionsstring[]?Permissions attached to the key identityobject?Identity info if externalId was set when creating the key ratelimitsobject[]?Rate limit states (if rate limiting configured)
Using as middleware
For cleaner code, extract verification into middleware:
const { Unkey } = require ( "@unkey/api" );
const unkey = new Unkey ({ rootKey : process . env . UNKEY_ROOT_KEY });
async function unkeyAuth ( req , res , next ) {
const key = req . headers . authorization ?. replace ( "Bearer " , "" );
if ( ! key ) {
return res . status ( 401 ). json ({ error : "Missing API key" });
}
try {
const { data } = await unkey . keys . verifyKey ({ key });
if ( ! data . valid ) {
return res . status ( 401 ). json ({ error : "Invalid API key" });
}
// Attach key info to request for use in route handlers
req . unkey = data ;
next ();
} catch ( err ) {
console . error ( err );
return res . status ( 500 ). json ({ error : "Could not verify key" });
}
}
module . exports = { unkeyAuth };
Then use it on any route:
const { unkeyAuth } = require ( "./middleware/auth" );
app . get ( "/secret" , unkeyAuth , ( req , res ) => {
// req.unkey contains the verification result
res . json ({ message : "Secret data" , keyId : req . unkey . keyId });
});
app . get ( "/another-secret" , unkeyAuth , ( req , res ) => {
res . json ({ data : "More protected content" });
});
Next steps
Add rate limiting Limit requests per key
Set usage limits Cap total requests per key
Add permissions Fine-grained access control
SDK Reference Full TypeScript SDK docs
Troubleshooting
Getting 401 even with a valid key?
Ensure the key hasn’t expired or been revoked - Verify the Authorization
header format: Bearer YOUR_KEY (note the space) - Check that your root key
has the verify_key permission
Check that UNKEY_ROOT_KEY is set correctly in your .env - Make sure
you’re calling require("dotenv").config() before using env vars - Check the
Unkey dashboard for any service issues
The code above uses CommonJS. For TypeScript, install types and use imports: npm install -D typescript @types/express @types/node
import express from "express" ;
import { Unkey } from "@unkey/api" ;