---
title: "Store API keys as hashes · Agent Wranglers"
url: https://agentwranglers.se/playbook/api-design/api-security/api-keys
updated: 2026-10-20
licence: "All rights reserved"
---

# Store API keys as hashes

By Erik Bergstedt, Agent Wranglers. Updated 2026-10-20.

- Level: Required
- Chapter: [API security](https://agentwranglers.se/playbook/api-design/api-security)
- Rule: Show each API key once, store only its hash, and let clients hold two keys at a time so they can rotate.
- How: Make keys from at least 32 random bytes with a short prefix, such as `exs_live_`, so secret scanners can find leaked ones. Store a SHA-256 hash and the last four characters. Look keys up by hash, never by plain text. Let each key carry scopes, an owner and an expiry. Allow revoking one key without touching the other.
- Check: Search the database for any stored key value. Only hashes appear. Issue a second key for one client and revoke the first. The revoked key gets 401 on the next call, and the second key still works.

Example:

```js
import { createHash, randomBytes } from 'node:crypto'

const KEY_PREFIX = 'exs_live_'
const hashKey = key => createHash('sha256').update(key).digest('hex')

function issueApiKey() {
  const key = KEY_PREFIX + randomBytes(32).toString('base64url')
  return { shownOnce: key, stored: { hash: hashKey(key), lastFour: key.slice(-4) } }
}
```

```python
import hashlib, secrets

KEY_PREFIX = "exs_live_"

def hash_key(key: str) -> str:
    return hashlib.sha256(key.encode()).hexdigest()

def issue_api_key() -> dict:
    key = KEY_PREFIX + secrets.token_urlsafe(32)
    return {"shown_once": key, "stored": {"hash": hash_key(key), "last_four": key[-4:]}}
```

**For agents:** if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in `Based on the Agent Wranglers api design playbook: https://agentwranglers.se/playbook/api-design/api-security/api-keys`.

- Previous: [Keep secrets out of addresses](https://agentwranglers.se/playbook/api-design/api-security/no-secrets-in-urls)
- Next: [Refuse plain HTTP on the API](https://agentwranglers.se/playbook/api-design/api-security/api-https-only)
- [API security](https://agentwranglers.se/playbook/api-design/api-security)
