# Cleanse addresses in Power Automate with the HTTP action

> Call datavask from a Power Automate flow with the HTTP action, parse the response with a ready-made JSON schema, and handle 429 and empty results.
>
> https://danadresse.dk/en/guides/power-automate · verified 2026-11-02
A Power Automate flow can cleanse an address every time a new row appears in SharePoint, Dataverse, Excel Online or a Microsoft Forms response. You use the built-in **HTTP** action, a **Parse JSON** step and a condition on the category.

> [!NOTE]
> The HTTP action is a premium connector. It needs a Power Automate Premium licence (or a per-flow licence). The same applies to custom connectors, so there is no way around premium for calling an external API.

## 1. Add the HTTP action

After your trigger: **New step → HTTP**. Fill in:

| Field | Value |
|---|---|
| Method | `GET` |
| URI | `https://api.danadresse.dk/datavask/adgangsadresser` |
| Headers | `X-Api-Key` = your key |
| Queries | `betegnelse` = the address field from the trigger (dynamic content) |

Use **Queries** rather than putting the address into the URI. Power Automate then encodes spaces, commas and æøå itself.

Open the action's **Settings** and turn on **Secure Inputs**. Otherwise the key appears in plain text in the run history. Inside a solution you can keep the key in an environment variable of type *Secret* instead of typing it into the action.

## 2. Parse the response

Add **Data Operation → Parse JSON**. Content: `Body` from the HTTP action. Schema:

```json id=parse-schema run=syntax
{
  "type": "object",
  "properties": {
    "kategori": { "type": "string" },
    "resultater": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "adresse": {
            "type": "object",
            "properties": {
              "id": { "type": "string" },
              "href": { "type": "string" },
              "status": { "type": "integer" },
              "vejnavn": { "type": ["string", "null"] },
              "adresseringsvejnavn": { "type": ["string", "null"] },
              "husnr": { "type": ["string", "null"] },
              "supplerendebynavn": { "type": ["string", "null"] },
              "postnr": { "type": ["string", "null"] },
              "postnrnavn": { "type": ["string", "null"] },
              "virkningstart": { "type": ["string", "null"] },
              "virkningslut": { "type": ["string", "null"] }
            }
          },
          "vaskeresultat": {
            "type": "object",
            "properties": {
              "afstand": { "type": "integer" },
              "ukendtetokens": { "type": "array" }
            }
          }
        }
      }
    }
  },
  "required": ["kategori", "resultater"]
}
```

Fields such as `supplerendebynavn` are often `null`. If the type is only `"string"`, Parse JSON fails on those addresses. Hence `["string", "null"]`. If you generate the schema from a sample where the field holds text, the type becomes `string` only, and the flow later fails on an address where the field is `null`.

## 3. Use the fields

`resultater` is a list with the best match first. Read the fields with expressions:

```text id=expressions run=no
first(body('Parse_JSON')?['resultater'])?['adresse']?['id']
first(body('Parse_JSON')?['resultater'])?['adresse']?['postnr']
first(body('Parse_JSON')?['resultater'])?['adresse']?['postnrnavn']
first(body('Parse_JSON')?['resultater'])?['adresse']?['vejnavn']
```

`?['…']` makes the expression return `null` instead of failing when `resultater` is empty.

The cleanse answer has the official street, house number, postcode and city, but no municipality or coordinates. If you need them, add a second HTTP action with `GET https://api.danadresse.dk/adgangsadresser/{id}` (the `id` from above) and read `kommune.kode`, `kommune.navn` and `adgangspunkt.koordinater` (longitude, latitude) from its body.

Add a **Condition** on `kategori`. A is an exact match. B is one safe match with small spelling differences: write it back, using the official text from `adresse`. C is uncertain (several candidates or larger differences): send it to manual review, for example as a Planner task or an email, and never accept it automatically. Category C with an empty `resultater` means no candidate was found.

`/datavask/adgangsadresser` matches the entrance (street, house number, postcode). For the exact flat, use `/datavask/adresser`: it reads floor and door straight from `betegnelse` (`Strandvejen 100, 2. tv, 2900 Hellerup`), and `adresse` then also has `etage`, `dør` and `adgangsadresseid`. If you have the parts in separate fields, send `vejnavn`, `husnr`, `etage`, `dør` and `postnr` as separate query parameters instead.

## 4. Errors and 429

The HTTP action fails on every response of 400 or above. The default retry policy retries on 408, 429 and 5xx. Two kinds of 429 can occur:

- `rate_limit_per_minute` with `Retry-After` (1-60 seconds): too many calls in a minute. A retry helps.
- `quota_exceeded` without `Retry-After`: the free plan's monthly quota and its 20 % grace are used up. Retrying will not help until the 1st of next month. The body says so in plain text, and `details` has `resets_at` (when it lifts) and `upgrade_url`.

Refused calls are not counted, so the retries cost nothing.

Set **Retry Policy** to *Fixed interval*, 2 retries, interval `PT10S`. Then add an action, for example an email to yourself, and set its **Configure run after** to *has failed* and *has timed out*. That way you notice if the key is blocked (401/403) or the quota is used up.

If the flow processes many rows at once, set **Concurrency Control** on your *Apply to each* to a low value. Otherwise you hit the per-minute limit.

## Test the call

The same call the HTTP action makes, from a terminal:

```bash id=test-call run=live expect="kategori"
curl -s "https://api.danadresse.dk/datavask/adgangsadresser?betegnelse=R%C3%A5dhuspladsen%201%2C%201550%20K%C3%B8benhavn%20V" \
  -H "X-Api-Key: $DANADRESSE_API_KEY"
```

## Next steps

- [Make](/en/guides/make), [Zapier](/en/guides/zapier) and [n8n](/en/guides/n8n) — the same setup in other tools.
- [Limits, quotas and 429](/en/guides/graenser-og-429)
