Move from DAWA by changing the base URL

Move an existing DAWA integration to Danadresse with a new base URL and a key, check it with a smoke-test script, and know the known differences.

4 min Level: Beginner ✓ Code tested

DAWA closed on 1 October 2026 at 10:00, and dawa.aws.dk and api.dataforsyningen.dk now answer with errors. Danadresse has the same paths, parameters and JSON fields, so for most integrations the migration is two changes: a new domain and an API key.

This guide shows how to make the domain configurable, how to check the switch with a small script, and where our API differs from DAWA today.

1. Change the domain and add a key

BeforeAfter
https://dawa.aws.dkhttps://api.danadresse.dk
https://api.dataforsyningen.dkhttps://api.danadresse.dk
no keyheader X-Api-Key: <your key>

The path after the domain does not change. https://api.dataforsyningen.dk/adresser?postnr=8000 becomes https://api.danadresse.dk/adresser?postnr=8000. The DAWA endpoints live at the root, without /api/v1.

Create a key under Keys. Every call needs a key; without one you get 401 with the code missing_api_key. If you cannot send a header (for example from a spreadsheet or a <script src>), the key can go in the URL as ?api_key=. See Header or api_key in the URL for when that is acceptable.

If many files contain DAWA URLs, migrate-cli can find and replace them for you.

2. Make the base URL configurable

Do not hard-code the domain again. Read it from configuration with https://api.danadresse.dk as the default. Then you can point at a test proxy, a mock in your tests, or another provider without touching the code.

Python:

Python#config-python
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 get_address(address_id: str) -> dict:
    r = session.get(f"{BASE_URL}/adresser/{address_id}", timeout=10)
    r.raise_for_status()
    return r.json()

Node 18 or newer:

JavaScript#config-node
const BASE_URL = (process.env.DANADRESSE_BASE_URL ?? "https://api.danadresse.dk").replace(/\/$/, "");
const API_KEY = process.env.DANADRESSE_API_KEY;

export async function getAddress(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#config-php
<?php
$baseUrl = rtrim(getenv('DANADRESSE_BASE_URL') ?: 'https://api.danadresse.dk', '/');
$apiKey = getenv('DANADRESSE_API_KEY');

function getAddress(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);
}

In .NET, put the values in appsettings.json and the key in user secrets or an environment variable:

JSON#config-dotnet
{
  "Danadresse": {
    "BaseUrl": "https://api.danadresse.dk"
  }
}
C##config-csharp
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. Run a smoke test

A smoke test calls the endpoints your code uses and checks the status and one field you read. Run it after the switch and again in CI. This script makes five calls:

Python#smoke-test
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"]

# (path, parameters, one field your code reads)
CHECKS = [
    ("/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"),
]

failed = 0
for path, params, field in CHECKS:
    r = session.get(BASE_URL + path, params=params, timeout=10)
    ok = r.status_code == 200 and field in r.text
    failed += 0 if ok else 1
    print(f"{'OK  ' if ok else 'FAIL'} {r.status_code} {path}")

print("All checks passed" if failed == 0 else f"{failed} checks failed")
raise SystemExit(1 if failed else 0)

Expected output:

Text#smoke-output
OK   200 /autocomplete
OK   200 /adresser/0a3f50ae-b4ee-32b8-e044-0003ba298018
OK   200 /adgangsadresser/reverse
OK   200 /postnumre/8000
OK   200 /datavask/adresser
All checks passed

Add the parameters you use yourself (struktur, srid, format, filters). A parameter we do not know is ignored without an error, so also check that the response has the shape you expect – for example that a filter actually narrowed the result.

4. What differs from DAWA

AreaDAWADanadresse
KeyNoneRequired. 401 missing_api_key without one, 401 invalid_api_key for a wrong or revoked key
LimitsNone fixedMonthly quota and a per-minute limit. See Limits, quotas and 429
Error format{"type","title","details"}Same fields plus code, e.g. "code": "missing_api_key", on every error including 500. See The error format
href fieldsPointed at DAWAPoint at https://api.danadresse.dk
CodesZero-padded ("0101", "0800")The same. Input may be un-padded: kommunekode=101 works
srid4326 and 25832The same. Other values give 400 QueryParameterFormatError
/replikering/udtraekStreamed JSON arrayThe same. Add &ndjson for one JSON line per object

5. Known gaps

Most of DAWA's contract is covered: the endpoints (including /historik/*, naboer, /bebyggelser, /stednavne2 and /vejnavnpostnummerrelationer), both replication forms, format=csv|geojson|geojsonz and JSONP. These values are empty today because we have no source for them. The current list is on Compatibility.

  • Fields on adgangsadresser: the keys are all there, but vejpunkt has no coordinates ([]), bebyggelser is an empty list, brofast is null, and in adgangspunkt the fields id, kilde, tekstretning and ændret are null.
  • bbox and visueltcenter: empty lists on several area and street endpoints, for example /regioner, /vejstykker and /navngivneveje.

Address lookup, autocomplete, datavask and reverse geocoding are not affected. If you rely on one of these values, tell us through support with the endpoint and field you need.

Next steps

Related guides

Markdown source Spotted an error? Tell us API-reference (Swagger)

Try it with a free key

2,000 calls/month on the free plan. No credit card.

Create account →