# Error 401: missing or invalid API key

> Find the cause of 401 missing_api_key and invalid_api_key with a checklist and a small diagnostic script, and fix the key or the way you send it.
>
> https://danadresse.dk/en/guides/fejl-401 · verified 2026-11-02
Status 401 means the API does not know who you are. Either no key came with the call (`missing_api_key`), or the key is not recognised (`invalid_api_key`). Every call needs a key, the DAWA-compatible ones included, so this is the error people meet most often right after switching from DAWA.

## What you see

On the DAWA paths (`/adresser`, `/autocomplete` and so on):

```json id=missing-dawa run=no
{
  "type": "UnauthorizedError",
  "title": "This API requires an X-Api-Key header. Get a free key in 30 seconds at https://danadresse.dk/dashboard/signup — then add 'X-Api-Key: <your key>' to every request.",
  "details": null,
  "code": "missing_api_key"
}
```

```json id=invalid-dawa run=no
{
  "type": "UnauthorizedError",
  "title": "The provided API key is invalid or revoked.",
  "details": null,
  "code": "invalid_api_key"
}
```

On `/api/v1` the same code is in `error.code`. See [The error format](/en/guides/fejlformat).

## Run the diagnostic script first

The script checks the usual mistakes in the key itself and then makes one call:

```python id=diagnose-401 run=live expect="200"
import os
import re

import requests

raw = os.environ.get("DANADRESSE_API_KEY")
if raw is None:
    raise SystemExit("DANADRESSE_API_KEY is not set in this environment")
key = raw.strip().strip('"').strip("'")
if key != raw:
    print("Warning: the key had whitespace, a line break or quotes around it")
if not re.fullmatch(r"dawa_(live|test)_[0-9a-f]{64}", key):
    print(f"Warning: the key does not have the expected format (length {len(key)})")

r = requests.get(
    "https://api.danadresse.dk/postnumre/8000",
    headers={"X-Api-Key": key},
    timeout=10,
)
try:
    code = r.json().get("code")
except ValueError:
    code = None
print(r.status_code, code or "OK")
```

If you get `200 OK`, the key is fine and the problem is in how your application sends it. Go through the checklist.

## missing_api_key: the key did not arrive

**The header has the wrong name.**

- Cause: The key was sent as `Authorization: Bearer …`, `Api-Key`, `apikey` or in the JSON body. The API only reads the `X-Api-Key` header and the `api_key` query parameter. Only the remote MCP server at `/mcp` also accepts `Authorization: Bearer <key>`.
- Fix: Use `X-Api-Key: <key>`. Upper and lower case in the header name do not matter.

**The environment variable is empty where the code runs.**

- Cause: The key is set in your terminal, but not in systemd, Docker, cron, CI or the hosting panel. The header goes out as `X-Api-Key: ` with no value, and an empty header counts as missing.
- Fix: Log the length of the key (never the key itself) at startup, and stop the program if it is 0.

**A proxy or framework strips the header.**

- Cause: Some reverse proxies, API gateways and serverless platforms only forward known headers.
- Fix: Test with curl directly against `https://api.danadresse.dk` from the same machine. If that works, allow `X-Api-Key` in the proxy, or send the key as `api_key` in the URL.

**The call goes to `http://`.**

- Cause: `http://api.danadresse.dk` answers with a redirect (301) to `https://`. curl without `-L` just shows an HTML page, and some HTTP clients drop headers or the body when they follow a redirect.
- Fix: Always use `https://`.

**The key sits in a widget setting that is not used.**

- Cause: DAWA's old widget reads the key from the script URL (`?key=…`), from `data-api-key` on the script tag, or from the `apiKey` and `params.api_key` options. Anywhere else, the widget sends no key.
- Fix: See [From dawa-autocomplete2](/en/guides/migrer-dawa-autocomplete2).

## invalid_api_key: the key was not recognised

**The key is mistyped or truncated.**

- Cause: Copied without the last characters, or with an extra one. A key is `dawa_live_` or `dawa_test_` followed by 64 characters from 0-9 and a-f.
- Fix: Run the diagnostic script. If the format is wrong, create a new key under [Keys](/dashboard/keys). The key is shown only once, so it cannot be retrieved again.

**Quotes or a line break in the key.**

- Cause: `.env` files with `KEY="…"` read by a tool that does not strip the quotes, or a key with a line break from a file. Whitespace around a key in the header is stripped. In `api_key=` in the URL it is not.
- Fix: Strip quotes and whitespace when you read the key.

**The key was deleted or rotated.**

- Cause: Someone deleted the key in the dashboard, or it was replaced and the old one is still in an environment.
- Fix: Check under [Keys](/dashboard/keys) which keys are active (the last four characters are shown), and update the configuration.

**Both header and URL hold a key.**

- Cause: The header wins. An old key in the header gives `invalid_api_key` even if the URL holds the new one.
- Fix: Send only one of them.

**A key from another product.**

- Cause: A Datafordeler key or a key from another service in the same variable.
- Fix: Use a key from [danadresse.dk/dashboard/keys](/dashboard/keys).

## 401 on the dashboard API

Endpoints such as `/api/v1/keys` and `/api/v1/webhooks` use a login token (JWT) in `Authorization: Bearer …`, not an API key. Without a token you get `error.code` `unauthorized`. With an expired or wrong token you get `invalid_token`. Get a new token with `POST /api/v1/auth/login`.

## Next steps

- [Header or api_key in the URL](/en/guides/api-noegle-header-eller-query)
- [Error 403: access denied](/en/guides/fejl-403), if the key is recognised but refused.
- [Test keys and the sandbox](/en/guides/testnoegler-sandbox)
