---
title: API keys
description: Create keys, limit what each one can spend and call, and rotate or revoke them without downtime.
---

API keys authenticate your requests and draw on your account balance. You can create as many as you like and give each one its own limits. All keys share the same balance.

## Create a key

:::steps
### Open API keys

Go to [API keys](/app/keys) in the dashboard and click **New key**.

### Name the key and pick a channel group

Name the key after where it will run, such as `checkout-prod` or `eval-runner`. Leave **Channel group** on **Auto — best available** unless you need every request from this key to run in one specific group. See [Channel groups](/docs/models/channel-groups).

### Set limits (optional)

Open **Limits & restrictions** to cap spend, rate, concurrency, models or lifetime. Every option is described [below](#key-options), and you can change all of them later.

### Copy the key

Click **Create**. The full key appears once, in a banner at the top of the page. Copy it, or click **Use in tools…** to set up Claude Code, Codex CLI and other clients with it. After you click **Done**, only the first characters of the key are shown, for example `lr-a1b2c3…`.
:::

> [!WARNING] Keys are shown once
> Lovely Router stores only a hash of each key. If you lose a key, revoke it and create a new one.

## Key options

| Option | Default | What it does |
| --- | --- | --- |
| **Name** | — | Label shown in the key list and in [Logs](/app/logs). Up to 60 characters. |
| **Channel group** | Auto | Runs every request from this key in one channel group. **Auto** picks the best available group per request. Set at creation only. |
| **Spend limit (USD)** | No limit | Most the key may ever spend, in whole dollars, across model requests, tool runs and browser runs. It is a lifetime cap, not a monthly budget. |
| **Expires on** | Never | The key stops working after the end of this day, in your browser's time zone. |
| **Requests per minute** | Unlimited | How many model requests the key may start per minute. |
| **Max concurrent requests** | Unlimited | How many model requests the key may have in progress at once. A long stream counts until it ends. |
| **Only allow specific models** | All models | Allow-list of models the key may call. Aliases such as `claude-sonnet-5-5` count as the model they resolve to. |

The key list shows each key's group, limits, total **Spent** and **Last used** time.

## What happens when a limit is hit

A refused request is not billed and does not appear in Logs. The body uses the format of the API you called.

| Situation | Status | Anthropic type | OpenAI type | Message |
| --- | --- | --- | --- | --- |
| Key expired | `401` | `authentication_error` | `authentication_error` | `This API key has expired` |
| Key revoked | `401` | `authentication_error` | `authentication_error` | `Invalid or revoked API key` |
| Model not on the allow-list | `403` | `permission_error` | `permission_denied` | `This API key is not allowed to use 'gpt-6.1-sol'.` |
| Spend limit reached | `402` | `billing_error` | `insufficient_quota` | `This API key has reached its spend limit.` |
| Requests per minute or concurrency reached | `429` | `rate_limit_error` | `rate_limit_exceeded` | `This API key has reached its rate or concurrency limit.` |

Gemini-format requests get the same status codes in Gemini's error format. A `429` from a key limit carries a `Retry-After` header with the seconds to wait. Here is the spend-limit error in both formats. Every other body and the retry advice are in [Errors](/docs/api/errors).

```json title="Anthropic"
{
  "type": "error",
  "error": {
    "type": "billing_error",
    "message": "This API key has reached its spend limit."
  }
}
```

```json title="OpenAI"
{
  "error": {
    "message": "This API key has reached its spend limit.",
    "type": "insufficient_quota",
    "code": "insufficient_quota"
  }
}
```

### Which limits apply to tools and browser runs

| Limit | Model requests | Data tools and browser agent |
| --- | --- | --- |
| Expiry and revocation | Yes | Yes |
| Spend limit | Yes | Yes |
| Requests per minute | Yes | No |
| Max concurrent requests | Yes | No |
| Model allow-list | Yes | No |

The spend limit is checked before each request starts. A model request already in progress always finishes, so the key's total can end slightly above the limit. A tool or browser run reserves its maximum cost up front, so it is refused with `402` if that maximum would take the key past its limit.

## Edit a key

Click **Edit** on a key to change its name, spend limit, expiry date, rate and concurrency limits, or model allow-list. Changes apply to the next request. Clearing a field removes that limit.

To move a key to a different channel group, create a new key in that group and [rotate](#rotate-a-key) to it.

## Revoke a key

Click **Revoke**, then **Confirm revoke**. The key stops working on the next request, and revoking can't be undone. A revoked key stays in the list, greyed out, with its spend history, and its requests stay in [Logs](/app/logs).

## Rotate a key

Replace a key without downtime:

1. Create a new key with the same group and limits.
2. Deploy the new key to your app or secrets manager.
3. Wait until the old key's **Last used** time stops changing.
4. Revoke the old key.

Rotate on a schedule, after someone with access leaves, and immediately if a key may have leaked.

## Use a key in your tools

Click **Use in tools** on any key for ready-made setups for Claude Code, Codex CLI, Gemini CLI, Cherry Studio, DeepChat and AionUI, with one-click import where the app supports it. See [Use with your tools](/docs/integrations).

## Best practices

- **One key per app and environment.** Separate keys for production, staging, CI and your laptop keep spend visible per key, and let you revoke one without touching the others.
- **Cap keys that run unattended.** Agents, batch jobs and CI pipelines can loop. A spend limit bounds the damage, and a requests-per-minute limit slows a runaway loop.
- **Allow only the models a key needs.** A key for a summarisation job doesn't need the most expensive model.
- **Expire temporary keys.** Give demos, contractors and evaluations a key with an expiry date instead of remembering to revoke it.
- **Keep keys server-side.** Never ship a key in front-end or mobile code. See [Keep keys safe](/docs/authentication#keep-keys-safe).
