# Authentication

> The key header forms, which routes need a key, the scopes, the per-key limits and the authentication error codes.

Developers · Reference · https://wxalerts.org/docs/developers/authentication/

How to make, send and revoke a key is in
[Make, send and revoke an API key](/docs/developers/api-keys/).

## Key format and lifetime

| | |
| --- | --- |
| Format | `wx_live_` followed by 43 characters |
| Live keys per account | up to ten |
| Expiry | 30 days unless a longer term or Never is chosen when the key is made |
| Storage | only a hash is stored; the key is shown once |

## Sending a key

| Form | Example |
| --- | --- |
| `X-API-Key` header | `X-API-Key: wx_live_...` |
| `Authorization` header | `Authorization: Bearer wx_live_...` |

Use the same paths as the public API, with `/v1` in front. A key is never sent
in the URL. A request with `?key=` or `?api_key=` on `/v1` is refused with
`400 key_in_query`.

`placefile:read` is the one exception to the header rule: GR software cannot
send a header, so that key goes in the placefile URL as `?key=`. Keep that URL
private. Without a key the placefile is the public one, with restricted
reports filtered out. A key that is unknown, revoked or no longer held by a
meteorologist gets that same public file, not an error, so the layer in
your radar software keeps drawing.

## What needs a key

The public routes work without one, limited per address. A key on those same
paths (with or without `/v1`) is charged to the key instead. The endpoints the
site does not use are served **only** under `/v1` and always need one (the
[route index](/docs/api/rest/#every-route) has both lists): `/alerts/active`, `/alerts/active/geojson`,
`/stats/lightning/daily`, `/storms/active`, `/storms/{id}`,
`/storms/couplets/{id}/cone`, `/forecast/sounding`, `/visibility`,
`/visibility/point`, and everything under `/tropical` and `/winter`.

## Scopes

A key reads only what its scopes allow. New keys get every scope below except
`placefile:read` unless you choose otherwise.

| Scope | Paths under `/v1` |
|---|---|
| `alerts:read` | `/alerts/*`, `/stats/*` |
| `weather:read` | `/forecast`, `/forecast/sounding`, `/forecast/raw` (on keys enabled for it; see [Raw model data](/docs/api/raw/)), `/conditions`, `/stations/*`, `/weather`, `/sky/outlook`, `/visibility/*` |
| `outlooks:read` | `/outlooks/*`, `/tropical/*`, `/winter/*` |
| `storms:read` | `/storms/*`, `/lightning/*` |
| `reports:read` | `/lsr/reports` |
| `placefile:read` | The full spotter report placefile. Meteorologist accounts only. |

## Per-key limits

Each key may make about **300 requests a minute**, with short bursts allowed.
The endpoints that return large geometry keep their own tighter limits, charged
to the key. Every `/v1` response carries:

| Header | Meaning |
|---|---|
| `RateLimit-Limit` | The burst size for this key |
| `RateLimit-Remaining` | Requests left in the burst |
| `RateLimit-Reset` | On a 429, seconds until you may retry |
| `Retry-After` | On a 429, the same |

Responses keep their `ETag`, so send `If-None-Match` and take the `304`. The
data changes on the order of minutes. Polling faster than that buys nothing.
For alerts as they happen, use [MQTT](/docs/developers/mqtt/alerts/) instead of polling.

`/v1` responses are `Cache-Control: private`: your own client may cache them,
and no shared cache will.

The limits for clients without a key are on [Rate limits](/docs/developers/rate-limits/).

## Errors

| Status | `detail.code` | What to do |
|---|---|---|
| 400 | `key_in_query` | Move the key to a header, then revoke it |
| 401 | `api_key_required` | Send a key |
| 401 | `api_key_invalid` | The key is unknown, revoked or expired |
| 403 | `insufficient_scope` | Add the scope on the Account page, or use another key |
| 429 | `rate_limited` | Wait `Retry-After` seconds |

The full list of status codes is on [Errors](/docs/developers/errors/).
