# Skift fra DAWA ved at ændre base-URL'en

> Flyt en eksisterende DAWA-integration til Danadresse med ny base-URL og en nøgle, test den med et røgtest-script og kend de kendte forskelle.
>
> https://danadresse.dk/guides/migrer-fra-dawa · verified 2026-11-02
DAWA lukkede 1. oktober 2026 kl. 10.00, og `dawa.aws.dk` og `api.dataforsyningen.dk` svarer nu med fejl. Danadresse har de samme stier, parametre og JSON-felter, så for de fleste integrationer er migreringen to ændringer: et nyt domæne og en API-nøgle.

Guiden viser, hvordan du gør domænet konfigurerbart, hvordan du tester skiftet med et lille script, og hvor vores API afviger fra DAWA i dag.

## 1. Skift domænet og tilføj en nøgle

| Før | Efter |
|---|---|
| `https://dawa.aws.dk` | `https://api.danadresse.dk` |
| `https://api.dataforsyningen.dk` | `https://api.danadresse.dk` |
| ingen nøgle | header `X-Api-Key: <din nøgle>` |

Stien efter domænet ændres ikke. `https://api.dataforsyningen.dk/adresser?postnr=8000` bliver til `https://api.danadresse.dk/adresser?postnr=8000`. DAWA-endpoints ligger i roden, uden `/api/v1`.

Opret en nøgle under [Nøgler](/dashboard/keys). Alle kald kræver en nøgle; uden nøgle får du `401` med koden `missing_api_key`. Kan du ikke sende en header (fx fra et regneark eller et `<script src>`), kan nøglen sendes som `?api_key=` i URL'en. Se [Header eller api_key i URL'en](/guides/api-noegle-header-eller-query) for, hvornår det er i orden.

Har du mange filer med DAWA-adresser, kan [migrate-cli](/guides/find-dawa-kald-med-migrate-cli) finde og erstatte dem for dig.

## 2. Gør base-URL'en konfigurerbar

Hardkod ikke domænet igen. Læs det fra konfigurationen med `https://api.danadresse.dk` som standard. Så kan du pege på en test-proxy, en mock i dine tests eller skifte leverandør uden at røre koden.

Python:

```python id=config-python run=syntax
import os

import requests

BASE_URL = os.environ.get("DANADRESSE_BASE_URL", "https://api.danadresse.dk").rstrip("/")

session = requests.Session()
session.headers["X-Api-Key"] = os.environ["DANADRESSE_API_KEY"]


def hent_adresse(adresse_id: str) -> dict:
    r = session.get(f"{BASE_URL}/adresser/{adresse_id}", timeout=10)
    r.raise_for_status()
    return r.json()
```

Node 18 eller nyere:

```js id=config-node run=syntax
const BASE_URL = (process.env.DANADRESSE_BASE_URL ?? "https://api.danadresse.dk").replace(/\/$/, "");
const API_KEY = process.env.DANADRESSE_API_KEY;

export async function hentAdresse(id) {
  const res = await fetch(`${BASE_URL}/adresser/${id}`, {
    headers: { "X-Api-Key": API_KEY },
  });
  if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
  return res.json();
}
```

PHP:

```php id=config-php run=syntax
<?php
$baseUrl = rtrim(getenv('DANADRESSE_BASE_URL') ?: 'https://api.danadresse.dk', '/');
$apiKey = getenv('DANADRESSE_API_KEY');

function hentAdresse(string $baseUrl, string $apiKey, string $id): array
{
    $ch = curl_init("$baseUrl/adresser/" . rawurlencode($id));
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => ["X-Api-Key: $apiKey"],
        CURLOPT_TIMEOUT => 10,
    ]);
    $body = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    curl_close($ch);
    if ($status !== 200) {
        throw new RuntimeException("HTTP $status: $body");
    }
    return json_decode($body, true);
}
```

I .NET lægger du værdierne i `appsettings.json` og nøglen i user secrets eller en miljøvariabel:

```json id=config-dotnet run=no
{
  "Danadresse": {
    "BaseUrl": "https://api.danadresse.dk"
  }
}
```

```csharp id=config-csharp run=no
builder.Services.AddHttpClient("danadresse", client =>
{
    client.BaseAddress = new Uri(builder.Configuration["Danadresse:BaseUrl"]!);
    client.DefaultRequestHeaders.Add("X-Api-Key",
        Environment.GetEnvironmentVariable("DANADRESSE_API_KEY"));
});
```

## 3. Kør en røgtest

En røgtest kalder de endpoints, din kode bruger, og tjekker status og et felt, du læser. Kør den efter skiftet og igen i din CI. Scriptet her laver fem kald:

```python id=smoke-test run=live expect="OK   200 /autocomplete"
import os

import requests

BASE_URL = os.environ.get("DANADRESSE_BASE_URL", "https://api.danadresse.dk").rstrip("/")
session = requests.Session()
session.headers["X-Api-Key"] = os.environ["DANADRESSE_API_KEY"]

# (sti, parametre, et felt som din kode læser)
TJEK = [
    ("/autocomplete", {"q": "Rådhuspladsen 1", "per_side": 1}, "forslagstekst"),
    ("/adresser/0a3f50ae-b4ee-32b8-e044-0003ba298018", {}, "adressebetegnelse"),
    ("/adgangsadresser/reverse", {"x": 12.5683, "y": 55.6761}, "adressebetegnelse"),
    ("/postnumre/8000", {}, "navn"),
    ("/datavask/adresser", {"betegnelse": "Strandvejen 100, 2900 Hellerup"}, "kategori"),
]

fejl = 0
for sti, params, felt in TJEK:
    r = session.get(BASE_URL + sti, params=params, timeout=10)
    ok = r.status_code == 200 and felt in r.text
    fejl += 0 if ok else 1
    print(f"{'OK  ' if ok else 'FEJL'} {r.status_code} {sti}")

print("Alle tjek bestået" if fejl == 0 else f"{fejl} tjek fejlede")
raise SystemExit(1 if fejl else 0)
```

Forventet output:

```text id=smoke-output run=no
OK   200 /autocomplete
OK   200 /adresser/0a3f50ae-b4ee-32b8-e044-0003ba298018
OK   200 /adgangsadresser/reverse
OK   200 /postnumre/8000
OK   200 /datavask/adresser
Alle tjek bestået
```

Tilføj de parametre, du selv bruger (`struktur`, `srid`, `format`, filtre). En parameter, vi ikke kender, ignoreres uden fejl, så tjek også, at svaret har den form, du forventer – fx at et filter faktisk har indsnævret resultatet.

## 4. Hvad er anderledes end DAWA

| Område | DAWA | Danadresse |
|---|---|---|
| Nøgle | Ingen | Påkrævet. `401 missing_api_key` uden, `401 invalid_api_key` ved forkert eller tilbagekaldt nøgle |
| Grænser | Ingen faste | Månedskvote og grænse pr. minut. Se [Grænser, kvoter og 429](/guides/graenser-og-429) |
| Fejlformat | `{"type","title","details"}` | Samme felter plus `code`, fx `"code": "missing_api_key"`, på alle fejl, også `500`. Se [Fejlformatet](/guides/fejlformat) |
| `href`-felter | Pegede på DAWA | Peger på `https://api.danadresse.dk` |
| Koder | Med foranstillede nuller (`"0101"`, `"0800"`) | Det samme. Input må være uden nuller: `kommunekode=101` virker |
| `srid` | `4326` og `25832` | Det samme. Andre værdier giver `400 QueryParameterFormatError` |
| `/replikering/udtraek` | Streamet JSON-array | Det samme. Tilføj `&ndjson` for én JSON-linje pr. objekt |

## 5. Kendte huller

Det meste af DAWA's kontrakt er dækket: endpoints (også `/historik/*`, `naboer`, `/bebyggelser`, `/stednavne2` og `/vejnavnpostnummerrelationer`), begge former for replikering, `format=csv|geojson|geojsonz` og JSONP. Disse værdier er tomme i dag, fordi vi ikke har en kilde til dem. Den aktuelle liste står på [Kompatibilitet](/kompatibilitet).

- **Felter på adgangsadresser:** nøglerne er der alle sammen, men `vejpunkt` har ingen koordinater (`[]`), `bebyggelser` er en tom liste, `brofast` er `null`, og i `adgangspunkt` er felterne `id`, `kilde`, `tekstretning` og `ændret` `null`.
- **`bbox` og `visueltcenter`:** tomme lister på flere område- og vej-endpoints, fx `/regioner`, `/vejstykker` og `/navngivneveje`.

Adresseopslag, autocomplete, datavask og reverse-geokodning er ikke påvirket. Har du brug for en af disse værdier, så skriv til os via [support](/support) med det endpoint og felt, du mangler.

## Næste skridt

- Brugte du DAWA's widget? [Fra dawa-autocomplete2](/guides/migrer-dawa-autocomplete2).
- Har du skrevet dit eget autocomplete? [Hjemmebygget DAWA-autocomplete](/guides/migrer-egen-dawa-autocomplete).
- Gemmer du adresse-id'er? Læs [status, darstatus og nedlagte adresser](/guides/status-og-darstatus).
