# Testnøgler og sandbox (dawa_test_)

> Brug dawa_test_-nøgler til udvikling og CI uden at bruge af kvoten, og hold test og produktion adskilt.
>
> https://danadresse.dk/guides/testnoegler-sandbox · verified 2026-11-02
En testnøgle starter med `dawa_test_` og er beregnet til udvikling, automatiske tests og demoer. Den giver de samme adressedata som en live-nøgle, men den bliver aldrig faktureret og bruger ikke af din kontos månedskvote. Til gengæld har den nogle begrænsninger, som denne guide gennemgår.

## Opret en testnøgle

Gå til [Nøgler](/dashboard/keys), klik **Opret nøgle**, og vælg *test*. Nøglen vises én gang. Gem den i din password manager eller i den `.env`-fil, du bruger lokalt.

Testnøgler tæller ikke med i loftet over antal nøgler. Grænsen på 2 nøgler på Hobby-planen gælder kun live-nøgler, så du kan have en testnøgle pr. udvikler eller pr. CI-pipeline.

## Forskelle mellem test og live

| | Live (`dawa_live_…`) | Test (`dawa_test_…`) |
|---|---|---|
| Data | Alle endpoints | Kun adresse-endpoints (se nedenfor) |
| Måling | Pr. konto (Hobby) eller pr. nøgle (betalte planer) | Pr. klient-IP, med Hobby-planens grænser |
| Minutgrænse | Din plans grænse | 360 kald i minuttet pr. IP |
| Fakturering | Efter din plan | Aldrig |
| Lækkontrol | Ja, markeres ved brug fra mange IP'er | Nej |
| Domænelås (browsernøgle) | Ja, hvis du sætter domæner | Ja, hvis du sætter domæner |

Målingen pr. IP betyder, at to udviklere bag hver sin internetforbindelse ikke deler grænse, mens to CI-jobs bag samme NAT-adresse gør. Headerne `X-RateLimit-Limit`, `X-RateLimit-Remaining` og `X-RateLimit-Minute-Remaining` viser IP'ens tal, ikke kontoens. Læs mere i [Grænser, kvoter og 429](/guides/graenser-og-429).

## Hvad en testnøgle må kalde

Testnøgler dækker adresseopslag: alle DAWA-kompatible stier som `/autocomplete`, `/adresser`, `/adgangsadresser`, `/datavask/adresser`, `/postnumre`, `/kommuner` og `/replikering/…`.

Tre grupper under `/api/v1` henter data live fra offentlige registre med en delt nøgle og er derfor lukket for testnøgler:

- `/api/v1/enrich/…` (BBR, DAGI, ejendom, klima og de andre berigelser)
- `/api/v1/datavask/parse`
- `/api/v1/stats/national`

Kalder du dem med en testnøgle, får du status 403:

```json id=sandbox-scope-body run=no
{
  "detail": {
    "error": {
      "code": "sandbox_scope",
      "message": "The sandbox/demo key is limited to basic address lookups. Use your own API key for enrich/datavask/stats."
    }
  },
  "error": {
    "code": "sandbox_scope",
    "message": "The sandbox/demo key is limited to basic address lookups. Use your own API key for enrich/datavask/stats."
  }
}
```

Løsningen er at bruge din egen live-nøgle til de kald. Berigelserne virker på vores egne demosider, fordi siderne sender et kortlivet token, der kun gælder fra dem. Det token kan du ikke bruge fra din egen kode. Den offentlige demonøgle på [/widget](/widget) er også en testnøgle og har de samme grænser.

Har du brug for at teste berigelser i CI, så brug en separat live-nøgle med lavt forbrug, og hold øje med den under [Forbrug](/dashboard/usage).

## Hvornår du skal bruge hvilken

| Opgave | Nøgle |
|---|---|
| Lokal udvikling af adressefelt eller import | Test |
| Enhedstests og integrationstests i CI | Test |
| Demo-side eller prototype | Test, låst til domænet |
| Produktion (webshop, CRM, backend) | Live |
| Kald til `/api/v1/enrich/…`, `/api/v1/datavask/parse`, `/api/v1/stats/national` | Live |
| Load-test | Ingen af delene uden aftale. Skriv til os via [support](/support) |

Brug aldrig en testnøgle til produktionstrafik. Alle dine brugere deler serverens IP, så de deler også én IP-grænse på gratisniveau, og minutgrænsen rammer hurtigt.

## Hold miljøerne adskilt

Den simpleste regel: produktion får en live-nøgle fra produktionsmiljøets hemmeligheder, alle andre miljøer får en testnøgle. Lad koden tjekke det ved opstart, så en forkert nøgle bliver opdaget med det samme i stedet for i en kundes checkout.

Det her script viser, hvilken slags nøgle du har sat, og laver ét kald:

```python id=key-mode-check run=live expect="Hellerup"
import os
import requests

key = os.environ["DANADRESSE_API_KEY"].strip()
if key.startswith("dawa_test_"):
    print("Testnøgle: målt pr. IP, aldrig faktureret")
elif key.startswith("dawa_live_"):
    print("Live-nøgle: målt på kontoen")
else:
    raise SystemExit("Ukendt nøgleformat. Den skal starte med dawa_test_ eller dawa_live_")

r = requests.get(
    "https://api.danadresse.dk/postnumre/2900",
    headers={"X-Api-Key": key},
    timeout=10,
)
r.raise_for_status()
print(r.json()["nr"], r.json()["navn"])
print("Tilbage denne måned:", r.headers.get("X-RateLimit-Remaining"))
```

En vagt ved opstart i Python, der nægter at starte produktion med en testnøgle og omvendt:

```python id=startup-guard-python run=syntax
import os
import sys

ENV = os.environ.get("APP_ENV", "development")
KEY = os.environ.get("DANADRESSE_API_KEY", "").strip()

if not KEY:
    sys.exit("DANADRESSE_API_KEY er ikke sat")
if ENV == "production" and not KEY.startswith("dawa_live_"):
    sys.exit("Produktion skal bruge en dawa_live_-nøgle")
if ENV != "production" and KEY.startswith("dawa_live_"):
    sys.exit(f"{ENV} må ikke bruge produktionsnøglen. Brug en dawa_test_-nøgle")
```

Samme vagt i Node:

```js id=startup-guard-node run=syntax
const env = process.env.NODE_ENV ?? "development";
const key = (process.env.DANADRESSE_API_KEY ?? "").trim();

if (!key) throw new Error("DANADRESSE_API_KEY er ikke sat");
if (env === "production" && !key.startsWith("dawa_live_")) {
  throw new Error("Produktion skal bruge en dawa_live_-nøgle");
}
if (env !== "production" && key.startsWith("dawa_live_")) {
  throw new Error(`${env} må ikke bruge produktionsnøglen. Brug en dawa_test_-nøgle`);
}
export const DANADRESSE_API_KEY = key;
```

I CI lægger du testnøglen som en hemmelighed og sender den videre som miljøvariabel. Eksempel for GitHub Actions:

```yaml id=github-actions run=no
jobs:
  test:
    runs-on: ubuntu-latest
    env:
      APP_ENV: test
      DANADRESSE_API_KEY: ${{ secrets.DANADRESSE_TEST_KEY }}
    steps:
      - uses: actions/checkout@v4
      - run: pip install -r requirements.txt
      - run: pytest
```

## Testnøgle i en browser

En testnøgle kan bruges i frontend-kode på en demo-side. Sæt *Tilladte domæner* på nøglen under [Nøgler](/dashboard/keys), ligesom med en live-nøgle. Så afvises kald fra andre sider med `403 origin_not_allowed`. Se [Browsernøgler og tilladte domæner](/guides/browser-noegler-domaener).

## Næste skridt

- [Header eller api_key i URL'en](/guides/api-noegle-header-eller-query)
- [Grænser, kvoter og 429](/guides/graenser-og-429)
- [Fejl 403: adgang nægtet](/guides/fejl-403), hvis du får `sandbox_scope` eller `origin_not_allowed`.
