Skip to main content
Protect a Cloudflare Worker with API keys. The Worker’s fetch handler calls keys.verifyKey on each request. Store the root key as a Worker secret, never in wrangler.toml.
You need a root key with the permissions listed on this page. Create one in the dashboard under Settings > Root Keys. See Permission reference for every permission.
1

Create a root key and a keyspace

In the dashboard, go to Settings > Root Keys and create a root key with api.*.create_api, api.*.create_key, and api.*.verify_key. Export it as UNKEY_ROOT_KEY in your shell for the curl commands. You’ll store it as a Worker secret in step 2. Never send it to a browser or mobile app.Then create a keyspace, which holds your keys. Use Keyspaces (APIs) in the dashboard, or apis.createApi:
create a keyspace
Keep the data.apiId from the response. You need it to create keys. See Root keys for more on root keys.
2

Install the SDK and store the root key

@unkey/api (version ) runs on Workers with no polyfills.
Your code reads it as env.UNKEY_ROOT_KEY. If your Worker also creates keys, add the keyspace ID from step 1 as an UNKEY_API_ID variable in wrangler.toml. It isn’t secret.
3

Create a key for a user

Create a key when a user signs up or asks for one. externalId links the key to the user, so verification tells you who’s calling. meta comes back on every verification. You get data.key only once: show it to the user and store only data.keyId.Workers have no process.env, so read the secret and keyspace ID from the handler’s env argument.
Every field is described in Creating keys.
4

Verify the key in the fetch handler

env is only available inside the handler, so create the client there. The code below creates it once and reuses it.
src/index.ts
Prefer a router? The Hono guide runs unchanged on Workers. Read the root key from c.env.UNKEY_ROOT_KEY instead of process.env.
5

Handle the outcome codes

keys.verifyKey returns HTTP 200 for every outcome. Check data.valid, then data.code for the reason, and return the matching status from your API:Remaining credits are in data.credits, and each checked rate limit is in data.ratelimits with remaining and reset. The call only fails with an HTTP error (which the SDKs throw) when the call itself is wrong, such as a bad root key or a malformed body. See Verifying keys for every field.
6

Next steps

Verifying keys

Every request field, the order of checks, and every response field.

Credits and refill

Meter usage per key and refill balances on a schedule.

Cookbook

Copy-ready recipes for rate limits, billing, and subscription tiers.
Last modified on September 29, 2026