# Authentication
Every call uses a short-lived token. The SDK exchanges your API key for a one-hour token and renews it for you. With raw HTTP, you do the exchange with one request.
## API keys [#api-keys]
An API key acts as your workspace and spends its credits. Each key has:
- a **Name**, to tell keys apart;
- an **Access** level, which decides what the key may do (for example, the fullest level is "Full, including schema changes");
- an optional **Expires** date;
- optional **Browser origins**, only for keys used from a web page. Browser keys can only read.
Only workspace admins manage keys. Your plan caps active keys; the keys page shows your count, and [Limits](/docs/developers/limits) shows how to read it in code.
### Create a key [#create-a-key]
### Open SDK & API [#open-sdk--api]
Go to . If the page asks you to verify it is you, do so.
### Fill in the key [#fill-in-the-key]
Select **Create key**, then set **Name**, **Access** and **Expires**.
### Copy it now [#copy-it-now]
Select **Create key** in the form. **Your new API key** is shown once: copy it to somewhere safe, such as your secret manager.
## Use a key from Python [#use-a-key-from-python]
The SDK looks for the key in this order: the `api_key` argument, then the `KLARETY_API_KEY` environment variable.
```python title="connect.py"
import os
from klarety import AuthError, Nexus
try:
nexus = Nexus.connect() # reads KLARETY_API_KEY
except AuthError as refused:
raise SystemExit(f"Could not sign in: {refused}")
with nexus:
print(nexus.account().roles)
```
Passing the key explicitly works the same way:
```python
nexus = Nexus.connect(api_key=os.environ["MY_KLARETY_KEY"])
```
`connect()` raises `AuthError` straight away when no key is found (`pass an api key or set KLARETY_API_KEY`) or when the key is rejected (`api key rejected`). A rejected key is wrong, revoked or expired.
### How the token is renewed [#how-the-token-is-renewed]
`connect()` keeps the token in memory. About five minutes before the hour is up, the next call exchanges the key again, so a long-running process (a worker, a notebook, a server) keeps working. The key never leaves your process except in that exchange.
## Use a key from the command line [#use-a-key-from-the-command-line]
The CLI stores your key in the operating system's keychain, never in a file:
```bash
klarety login "paste-your-key-here"
klarety sql "SELECT 1 AS ok"
```
`klarety logout` removes the key from the keychain. See [CLI](/docs/developers/cli) for every command.
## Use a key from raw HTTP [#use-a-key-from-raw-http]
Without the SDK, exchange the key for a token first.
```bash
curl -s https://api.klarety.ai/v1/token \
-H "Content-Type: application/json" \
-d "{\"api_key\": \"$KLARETY_API_KEY\"}"
```
The answer:
```json
{
"access_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 3600
}
```
Send the token on every other request:
```bash
curl -s https://api.klarety.ai/v1/account \
-H "Authorization: Bearer $TOKEN"
```
`expires_in` is in seconds; exchange again before it runs out. A missing, wrong or expired token always gets the same `401` body, so callers never learn which check failed. See [Errors](/docs/developers/errors).
## Rotate a key [#rotate-a-key]
Rotate a key on a schedule, when someone with access leaves, or if it may have leaked. In this order, rotation never interrupts your service:
### Create the new key [#create-the-new-key]
In , create a key with the same **Access** as the old one.
### Deploy it [#deploy-it]
Replace the old key wherever it was used (secret manager, server environment) and restart or redeploy. Confirm with `nexus.account()` that it signs in.
### Revoke the old key [#revoke-the-old-key]
Select **Revoke** on the old key, then **Revoke key**. Anything still using it stops working right away, and this cannot be undone.
At your plan's key limit? Revoke an unused key first.
Revoke it first, then create a replacement. A few minutes of downtime cost less than a key that works for someone else.
## What can go wrong [#what-can-go-wrong]
| Problem | Cause | Fix |
| --- | --- | --- |
| `AuthError` at `connect()` | Key missing, mistyped, revoked or expired. | Check the variable, or create a new key. |
| `RefusedError` on a call | The key's **Access** does not allow this call. | Use a key with the access you need. |
| No **Create key** button | You are not a workspace admin. | Ask an admin to create the key. |
| `401` from raw HTTP after an hour | The token expired. | Call `POST /v1/token` again. |
## Next steps [#next-steps]
- [SDK quickstart](/docs/developers) to run a first query.
- [Errors](/docs/developers/errors) for every status code.
- [Limits and rate limits](/docs/developers/limits) to read what your plan allows.