Æ, ø and å: character sets and URL encoding
Send æøå correctly URL-encoded, read UTF-8 responses without broken characters, open CSV in Excel with the right letters, and understand the oe/aa/ae folding in autocomplete.
Danish addresses are full of æ, ø and å. The API uses UTF-8 all the way: in the URL, in JSON and in CSV. Problems appear when one link in the chain uses another character set, usually Windows-1252 (also called Latin-1 or ANSI). The result is either empty responses or text like RÃ¥dhuspladsen.
Recognise the problem
| You see | It should be | Cause |
|---|---|---|
æ, ø, Ã¥ | æ, ø, å | UTF-8 read as Windows-1252 |
Æ, Ø, Ã… | Æ, Ø, Å | The same with capitals |
� or ? | æ, ø or å | Windows-1252 read as UTF-8, or the character lost in a conversion |
R%E5dhuspladsen in the URL | R%C3%A5dhuspladsen | The text was URL-encoded as Latin-1 |
å in JSON | å | Not a problem. The JSON parser turns it into å |
Send æøå in the URL
In a URL, æ, ø and å must be sent as UTF-8 bytes, percent-encoded:
| Character | URL-encoded |
|---|---|
| æ / Æ | %C3%A6 / %C3%86 |
| ø / Ø | %C3%B8 / %C3%98 |
| å / Å | %C3%A5 / %C3%85 |
| space | %20 (or + in the query string) |
Never build the URL by concatenating text. Let your library encode the parameters. With curl, -G and --data-urlencode do it for you:
curl -s -G "https://api.danadresse.dk/autocomplete" \ --data-urlencode "q=Rådhuspladsen 1" \ --data-urlencode "per_side=3" \ -H "X-Api-Key: $DANADRESSE_API_KEY"
In Python, requests encodes everything you pass in params:
import os import requests r = requests.get( "https://api.danadresse.dk/autocomplete", params={"q": "Rådhuspladsen 1", "per_side": 1}, headers={"X-Api-Key": os.environ["DANADRESSE_API_KEY"]}, timeout=10, ) r.raise_for_status() print(r.url) # ...?q=R%C3%A5dhuspladsen+1&per_side=1 print(r.json()[0]["forslagstekst"]) # r.json() always reads UTF-8
In PHP, use http_build_query, which encodes UTF-8 strings correctly. Do not use utf8_decode or mb_convert_encoding to Latin-1 before the call:
<?php $query = http_build_query(["q" => "Rådhuspladsen 1", "per_side" => 3]); $ctx = stream_context_create(["http" => [ "header" => "X-Api-Key: " . getenv("DANADRESSE_API_KEY"), "timeout" => 10, ]]); $body = file_get_contents("https://api.danadresse.dk/autocomplete?$query", false, $ctx); foreach (json_decode($body, true) as $suggestion) { echo $suggestion["forslagstekst"], "\n"; }
In JavaScript, use URL and searchParams.set(), as in Your first API call.
If your text comes from an old system, a file or a database in Windows-1252, convert it to UTF-8 before you send it. In Python: text = raw_bytes.decode("cp1252"). On the command line: iconv -f WINDOWS-1252 -t UTF-8 in.csv > out.csv.
Read the response as UTF-8
JSON responses have Content-Type: application/json without a charset parameter. JSON is always UTF-8, and most clients know that: r.json() in Python, res.json() in JavaScript and json_decode() in PHP give the right characters.
Problems come when a client guesses the character set from the header:
- Windows PowerShell 5.1 (
Invoke-RestMethod,Invoke-WebRequest) reads a response withoutcharsetas Latin-1. Use PowerShell 7, or read the bytes and decode them yourself:
$headers = @{ "X-Api-Key" = $env:DANADRESSE_API_KEY }
$res = Invoke-WebRequest -Uri "https://api.danadresse.dk/postnumre/2900" -Headers $headers -UseBasicParsing
$json = [System.Text.Encoding]::UTF8.GetString($res.RawContentStream.ToArray()) | ConvertFrom-Json
$json.navn- Older HTTP libraries in, for example, Java, .NET or VBA use the system's default character set when turning bytes into text. Specify UTF-8 explicitly.
- The database must be able to store the characters too. In MySQL and MariaDB the column and the connection must be
utf8mb4.
CSV in Excel
format=csv gives UTF-8 without a BOM (byte order mark). If you double-click the file in Excel on Windows, Excel guesses Windows-1252 and shows RÃ¥dhuspladsen. There are three fixes:
- Import instead of opening. In Excel: Data → From Text/CSV, choose the file, and set File Origin to 65001: Unicode (UTF-8).
- Power Query. Set the character set directly:
let Kilde = Web.Contents( "https://api.danadresse.dk/adresser", [Query = [postnr = "4200", per_side = "100", format = "csv"], Headers = [#"X-Api-Key" = "YOUR_API_KEY"]]), Csv = Csv.Document(Kilde, [Delimiter = ",", Encoding = 65001, QuoteStyle = QuoteStyle.Csv]), Tabel = Table.PromoteHeaders(Csv, [PromoteAllScalars = true]) in Tabel
- Save the file with a BOM. If you fetch the CSV with a script, write it with a BOM in front. Excel then opens it correctly on double-click:
import os import requests r = requests.get( "https://api.danadresse.dk/adresser", params={"postnr": "4200", "per_side": 1000, "format": "csv"}, headers={"X-Api-Key": os.environ["DANADRESSE_API_KEY"]}, timeout=30, ) r.raise_for_status() with open("adresser.csv", "w", encoding="utf-8-sig", newline="") as f: # utf-8-sig = UTF-8 with BOM f.write(r.content.decode("utf-8")) print("Saved adresser.csv")
Excel with Danish regional settings expects a semicolon as separator. The API's CSV uses commas, so pick comma in the import dialog, or use Power Query as above. See also Excel and Power Query.
oe, aa and ae in autocomplete
Users often type without Danish characters: raadhuspladsen, kobenhavn, Soendergade. /autocomplete therefore tries variants of what the user typed:
aa→ å,oe→ ø,ae→ æ (also with a capital first letter)- an
oat the start of a word → ø, soosterfindsØster - on top of that a typo tolerance that can fix one or two wrong letters in longer words
import os import requests r = requests.get( "https://api.danadresse.dk/autocomplete", params={"q": "raadhuspladsen 1", "per_side": 3}, headers={"X-Api-Key": os.environ["DANADRESSE_API_KEY"]}, timeout=10, ) r.raise_for_status() for suggestion in r.json(): print(suggestion["forslagstekst"])
The folding lives in /autocomplete. The filters on /adresser and /adgangsadresser (vejnavn=, husnr=, postnr=) do not do the same folding. Write the names with æ, ø and å. If your data lacks æøå, run it through /autocomplete or /datavask/adresser first. The datavask parser reads spellings such as Aabenraa, Åbenrå and Kobenhavn and matches them to the official name. See No results?
Next steps
Related guides
Markdown source Spotted an error? Tell us API-reference (Swagger)