JavaScript Client
A TypeScript client for every endpoint: autocomplete, adresser, adgangsadresser, reverse geocoding, vejnavne, postnumre and usage. No dependencies; Node 18+ and every modern browser; types included. The current version is 1.0.0.
Install
npm install getaddress-dk-api
On your server, with your API key:
import Client from 'getaddress-dk-api';
const api = new Client('{your-api-key}');
const autocompleteResult = await api.adresserAutocomplete('Amagerbrogade 217A');
if (autocompleteResult.isSuccess) {
for (const suggestion of autocompleteResult.toSuccess()) {
const getResult = await api.adresse(suggestion.adresse, { struktur: 'mini' });
if (getResult.isSuccess) {
const adresse = getResult.toSuccess();
console.log(adresse.vejnavn, adresse.husnr, adresse.etage, adresse['dør'], adresse.postnr, adresse.postnrnavn);
}
}
} else {
const failed = autocompleteResult.toFailed();
console.log(failed.status, failed.message);
}
CommonJS works too: const { Client } = require('getaddress-dk-api');. Records keep DAWA's Danish field
names, so read dør as adresse['dør']. Ids are DAR's UUIDs, the same ids DAWA gave, so ids you
have stored still work.
In the browser
The API refuses an API key sent from a web page. In the browser, pass a
domain token where the key would go; the client sends it in the path
(https://api.getaddress.dk/t/{your-domain-token}/autocomplete?q=…).
import Client from 'getaddress-dk-api';
const api = new Client('{your-domain-token}');
input.addEventListener('input', async () => {
const result = await api.autocomplete(input.value, { caretpos: input.selectionStart });
if (!result.isSuccess) return;
for (const suggestion of result.toSuccess()) {
// suggestion.type is 'vejnavn', 'adgangsadresse' or 'adresse'
show(suggestion.forslagstekst, suggestion.tekst, suggestion.caretpos);
}
});
For a ready-made list under an input, use the autocomplete widget instead.
Autocomplete
autocomplete is DAWA's combined /autocomplete: street names first, then
access addresses, then addresses, moving on as the text narrows. Branch on type. Street names are free; an
answer that suggests addresses costs one look-up.
const result = await api.autocomplete('Amagerbrogade 217A', { type: 'adresse', postnr: 2300, fuzzy: true });
for (const s of result.toSuccess()) {
if (s.type === 'adresse') console.log(s.data.id, s.data.etage, s.data['dør']);
if (s.type === 'vejnavn') console.log(s.data.navn);
}
One kind of thing only:
await api.adresserAutocomplete('Amagerbrogade 217A, kl.', { postnr: 2300 }); // [{ tekst, adresse }] 1 look-up if not empty
await api.adgangsadresserAutocomplete('Amagerbrogade 217A'); // [{ tekst, adgangsadresse }]
await api.vejnavneAutocomplete('Amag'); // [{ tekst, vejnavn }] free
await api.postnumreAutocomplete('23'); // [{ tekst, postnummer }] free
Adresser and adgangsadresser
One record by id is one look-up; an id that is not found costs nothing. struktur picks the record type
(nestet by default, flad or mini) and format: 'geojson' returns a
GeoJSON Feature. See Adresser and
Adgangsadresser.
const nestet = await api.adresse('3bba05d4-c8c4-4c90-8a64-a4dff11b9d1b');
const mini = await api.adresse('3bba05d4-c8c4-4c90-8a64-a4dff11b9d1b', { struktur: 'mini' });
const flad = await api.adresse('3bba05d4-c8c4-4c90-8a64-a4dff11b9d1b', { struktur: 'flad', srid: 25832 });
const feature = await api.adresse('3bba05d4-c8c4-4c90-8a64-a4dff11b9d1b', { format: 'geojson' });
const adgang = await api.adgangsadresse('099f3e28-4fb8-4c61-86a1-edff5df52409', { struktur: 'mini' });
Search a page at a time — one look-up per record returned, per_side at most 100. Several values of
one parameter go as an array (sent DAWA's way, joined with |).
const page = await api.adresser({ postnr: 2300, vejnavn: 'Amagerbrogade', husnr: '217A', struktur: 'mini' });
const text = await api.adresser({ q: 'Amagerbrogade 217A, kl. 5, 2300 København S' });
const adgange = await api.adgangsadresser({ vejnavn: 'Amagerbrogade', postnr: [2300, 2100] });
Reverse geocoding
The access address nearest a point. One look-up; nothing within reach is a 404 that costs nothing.
const nearest = await api.reverse(12.6137425, 55.6490036); // longitude, latitude
const utm = await api.reverse(556488.89, 6323087.65, { srid: 25832, struktur: 'mini' }); // easting, northing
Vejnavne and postnumre
Street names and postcodes. Free, and rate limited.
const streets = await api.vejnavne({ postnr: 2300 });
const street = await api.vejnavn('Amagerbrogade');
const postnumre = await api.postnumre({ kommunekode: '0101' });
const postnummer = await api.postnummer(2300);
Usage
Pass your admin key as the second argument. See Usage.
const api = new Client('{your-api-key}', '{your-admin-key}');
const today = await api.usage();
const oneDay = await api.usage(1, 9, 2026);
const range = await api.usageRange(new Date('2026-09-01'), new Date('2026-09-14'));
Failures
Errors the API reports, and network failures, are results, never thrown. status is the HTTP status
(0 when no response arrived), message says what went wrong, a 429 carries
retryAfterSeconds, and code is what to branch on:
- DAWA's errors, exactly as DAWA sent them, in
dawaError:QueryParameterFormatError(400),ResourcePathFormatError(404) andResourceNotFoundError(404). - Our refusals, as problem details in
problem:unauthorized(401),forbidden(403),domain-token-scope(403),domain-token-rate-limit(429), and rate or daily limits (429). See Errors.
const result = await api.adresse(id);
if (!result.isSuccess) {
const failed = result.toFailed();
if (failed.isNotFound) { /* no such address — costs nothing */ }
else if (failed.isInvalid) console.log(failed.dawaError.details);
else if (failed.isRateLimited) await wait(failed.retryAfterSeconds ?? 60);
else if (failed.isUnauthorized) { /* key missing or unknown, or an API key sent from a browser */ }
else console.log(failed.code, failed.message);
}
Options
new Client('{your-api-key}', '{your-admin-key}', {
baseUrl: 'https://api.getaddress.dk', // the API origin
timeoutMs: 30000, // 0 for no timeout
fetch: customFetch // defaults to the global fetch
});