# Vask adresser i Excel med Power Query

> Slå en kolonne af adresser op mod API'et fra Excel eller Power BI med Power Query, få postnummer, kommune og koordinater tilbage, og hold forbruget nede.
>
> https://danadresse.dk/guides/excel-power-query · verified 2026-11-02
Har du en liste af adresser i Excel, kan Power Query slå hver række op og skrive den rensede adresse, postnummer, kommune og koordinater ind i nye kolonner. Du skal ikke programmere, men du skal kopiere en lille funktion ind. Guiden er skrevet til Excel til Windows (Microsoft 365) og virker på samme måde i Power BI Desktop.

## 1. Gør nøglen klar

Opret en nøgle under [Nøgler](/dashboard/keys). I Excel: **Data → Hent data → Start Power Query-editor**. Vælg **Administrer parametre → Ny parameter**, kald den `DanadresseNoegle`, sæt typen til *Tekst* og indsæt nøglen som værdi. Så står nøglen ét sted og ikke inde i formlerne.

## 2. Indlæs din tabel

Markér dine data i regnearket og vælg **Data → Fra tabel/område**. Tabellen skal have en kolonne med hele adressen, fx `Adresse` med "Strandvejen 100, 2900 Hellerup".

## 3. Opret funktionen

Vælg **Ny kilde → Tom forespørgsel**, åbn **Avanceret editor**, og indsæt koden. Omdøb forespørgslen til `fnVask`.

```m id=fn-vask run=no
(betegnelse as text) as record =>
let
    Kald = (sti as text, q as record) => Web.Contents(
        "https://api.danadresse.dk",
        [
            RelativePath = sti,
            Query = q,
            Headers = [#"X-Api-Key" = DanadresseNoegle],
            ManualStatusHandling = {400, 401, 403, 404, 429, 500, 503}
        ]
    ),
    // 1) Datavask: kategori og det officielle match
    Svar = Kald("datavask/adgangsadresser", [betegnelse = betegnelse]),
    Status = Value.Metadata(Svar)[Response.Status],
    Json = if Status = 200 then Json.Document(Svar) else null,
    Top = if Json <> null and List.Count(Json[resultater]) > 0
          then Json[resultater]{0}[adresse] else null,
    // 2) Kommune og koordinater for den fundne adgangsadresse
    Detalje = if Top <> null then Kald("adgangsadresser/" & Top[id], []) else null,
    Adg = if Detalje <> null and Value.Metadata(Detalje)[Response.Status] = 200
          then Json.Document(Detalje) else null,
    Punkt = if Adg <> null then Adg[adgangspunkt][koordinater] else null,
    Felt = (r, navn as text) => if r <> null then Record.FieldOrDefault(r, navn) else null
in
    [
        Status = Status,
        Kategori = if Json <> null then Json[kategori] else null,
        AdgangsadresseId = Felt(Top, "id"),
        Vejnavn = Felt(Top, "vejnavn"),
        Husnr = Felt(Top, "husnr"),
        Postnr = Felt(Top, "postnr"),
        By = Felt(Top, "postnrnavn"),
        Kommune = if Adg <> null then Adg[kommune][navn] else null,
        Laengdegrad = if Punkt <> null then Punkt{0} else null,
        Breddegrad = if Punkt <> null then Punkt{1} else null
    ]
```

Tre ting i koden er værd at forstå:

- `RelativePath` og `Query` gør, at basis-URL'en er fast. Det kræver Power BI-tjenesten for at kunne opdatere planlagt, og Power Query koder selv æ, ø og å korrekt.
- `ManualStatusHandling` gør, at en fejl på én række giver en `Status`-værdi i stedet for at stoppe hele opdateringen.
- Vaskesvaret har den officielle vej, husnummer, postnummer og by, men ikke kommune eller koordinater. Funktionen laver derfor et ekstra kald til `/adgangsadresser/{id}` for den fundne adresse. Det er to kald pr. række; fjern det andet kald, hvis du kun skal bruge den vaskede tekst.
- `/datavask/adgangsadresser` matcher opgangen (vej, husnummer, postnummer). Skal du have den præcise lejlighed, så se trin 6.

## 4. Kald funktionen for hver række

Gå tilbage til din tabel, vælg **Tilføj kolonne → Brugerdefineret kolonne**, og skriv:

```m id=add-column run=no
= Table.ExpandRecordColumn(
    Table.AddColumn(Kilde, "Vask", each fnVask([Adresse])),
    "Vask",
    {"Status", "Kategori", "AdgangsadresseId", "Vejnavn", "Husnr", "Postnr", "By", "Kommune", "Laengdegrad", "Breddegrad"}
)
```

Erstat `Kilde` med navnet på det forrige trin, og `Adresse` med navnet på din kolonne. Klik **Luk og indlæs**.

Første gang spørger Excel, hvordan der skal logges ind på `https://api.danadresse.dk`. Vælg **Anonym**: nøglen sendes allerede i headeren. Får du fejlen `Formula.Firewall`, så gå til **Filer → Indstillinger → Forespørgselsindstillinger → Beskyttelse af personlige oplysninger** og sæt begge kilder til samme niveau, fx *Organisation*.

## 5. Læs resultatet

| Kategori | Betydning | Hvad du gør |
|---|---|---|
| A | Præcist match (bortset fra store/små bogstaver og tegnsætning) | Brug resultatet |
| B | Ét sikkert match med små stavefejl | Brug det, og gem den officielle tekst fra resultatet |
| C | Usikkert: flere kandidater eller større afvigelser | Tjek rækken manuelt. Godkend den aldrig automatisk |

`Status` er 200 for rækker, der blev slået op. 429 betyder, at du har ramt en grænse (se trin 7). 401 eller 403 betyder, at nøglen er forkert, spærret eller domænelåst. En domænelåst browsernøgle virker ikke i Excel, for Excel sender ingen `Origin`.

## 6. Etage og dør

Skift `RelativePath` i det første kald til `"datavask/adresser"`. Parseren læser etage og dør direkte fra teksten (`Strandvejen 100, 2. tv, 2900 Hellerup`, også `st. th` og `1.mf`), og den præcise lejlighed bliver matchet. `AdgangsadresseId` indeholder så adressens (lejlighedens) id. Tilføj `Felt(Top, "adgangsadresseid")`, `Felt(Top, "etage")` og `Felt(Top, "dør")`, hvis du skal bruge dem. Det andet kald virker stadig, for `/adgangsadresser/{id}` med et adresse-id svarer med dens opgang.

Har du etage og dør i separate kolonner, kan du i stedet sende hvert felt for sig:

```m id=fn-vask-enhed run=no
[vejnavn = [Vej], husnr = Text.From([Husnr]), etage = [Etage], #"dør" = [Doer], postnr = Text.From([Postnr])]
```

Giv recorden til `Kald` i stedet for `[betegnelse = betegnelse]`, og lad funktionen tage rækken i stedet for en tekst. Tomme felter skal fjernes fra recorden, ellers sendes en tom parameter.

## 7. Hold øje med forbruget

Hver række er ét kald (to med kommuneopslaget), **hver gang** du opdaterer. Forhåndsvisningen i editoren laver også kald. På gratisplanen har kontoen 2 000 kald om måneden og 360 kald i minuttet, og Power Query sender flere kald ad gangen.

`Status` 429 har to årsager. `rate_limit_per_minute` går over inden for et minut: opdater igen. `quota_exceeded` betyder, at gratisplanens månedskvote plus bufferen på 20 % er brugt, indtil den 1. i næste måned; svaret siger det i klar tekst og linker til opgraderingssiden. Afviste kald tæller ikke.

- Arbejd på en lille stikprøve (**Behold rækker → Behold øverste rækker**), til formlen virker.
- Slå automatisk opdatering fra under **Egenskaber for forespørgsel**, når listen er vasket.
- Ved større lister: upload en CSV under [Bulk-vask](/dashboard/bulk) i stedet.
- Se dit forbrug under [Forbrug](/dashboard/usage). Grænserne er forklaret i [Grænser, kvoter og 429](/guides/graenser-og-429).

## Test kaldet uden Excel

Samme kald som funktionen laver, fra en terminal:

```bash id=test-call run=live expect="kategori"
curl -s "https://api.danadresse.dk/datavask/adgangsadresser?betegnelse=Strandvejen%20100%2C%202900%20Hellerup" \
  -H "X-Api-Key: $DANADRESSE_API_KEY"
```

## Næste skridt

- [Vask adresser i Google Sheets](/guides/google-sheets)
- [Rens en CRM-eksport som CSV](/guides/rens-crm-csv)
