Skip to main content
The official Next.js SDK for Unkey. Use this within your route handlers as a simple, type-safe way to verify API keys.

github.com/unkeyed/sdks/tree/main/nextjs

Install

 npm install @unkey/nextjs
Protecting API routes is as simple as wrapping them with the withUnkey handler:
import { NextRequestWithUnkeyContext, withUnkey } from "@unkey/nextjs";

export const POST = withUnkey(
  async (req: NextRequestWithUnkeyContext) => {
    // The key has already been verified at this point
    // Access verification details via req.unkey.data
    if (!req.unkey.data.valid) {
      return new Response("Unauthorized", { status: 401 });
    }

    // Your API logic here
    return Response.json({
      message: "Hello!",
      keyId: req.unkey.data.keyId,
      // If you set an externalId when creating the key:
      externalId: req.unkey.data.identity?.externalId,
    });
  },
  { rootKey: process.env.UNKEY_ROOT_KEY! }
);

What’s in req.unkey?

The req.unkey.data object contains the verification result:
FieldTypeDescription
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[]?Roles 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)
Access these via req.unkey.data.valid, req.unkey.data.keyId, etc.
If you want to customize how withUnkey processes incoming requests, you can do so as follows:

getKey

By default, withUnkey will look for a bearer token located in the authorization header. If you want to customize this, you can do so by passing a getter in the configuration object:
export const GET = withUnkey(
  async (req) => {
    // ...
  },
  {
    rootKey: process.env.UNKEY_ROOT_KEY!,
    getKey: (req) => new URL(req.url).searchParams.get("key"),
  }
);

onError

You can specify custom error handling. By default errors will be logged to the console, and withUnkey will return a NextResponse with status 500.
export const GET = withUnkey(
  async (req) => {
    // ...
  },
  {
    rootKey: process.env.UNKEY_ROOT_KEY!,
    onError: async (req, res) => {
      await analytics.trackEvent(`Error ${res.code}: ${res.message}`);
      return new NextResponse("Unkey error", { status: 500 });
    },
  }
);

handleInvalidKey

Specify what to do if Unkey reports that your key is invalid.
export const GET = withUnkey(
  async (req) => {
    // ...
  },
  {
    rootKey: process.env.UNKEY_ROOT_KEY!,
    handleInvalidKey: (req, res) => {
      return new Response("Unauthorized", { status: 401 });
    },
  }
);
Last modified on February 6, 2026