Error 400 and 404 – wrong parameters and unknown ids
Read details in a 400 error, catch misspelled parameters that are ignored without an error, and tell not_found, route_not_found and unknown_entity apart.
A 400 means a parameter has a value the API cannot use. A 404 means the path or the id you ask for does not exist. Both count as a call on your quota, so it pays to catch them in code. The key is checked before the parameters, so if you also have a key problem you see 401 or 403 first.
400 invalid_query_parameter
On the DAWA paths a wrong parameter always gives status 400 with code invalid_query_parameter. details is a list with one entry per error:
{
"type": "QueryParameterFormatError",
"title": "Invalid query parameter",
"details": [
{
"type": "literal_error",
"loc": ["query", "format"],
"msg": "Input should be 'json', 'csv', 'geojson', 'geojsonz' or 'ndjson'",
"input": "xml",
"ctx": {"expected": "'json', 'csv', 'geojson', 'geojsonz' or 'ndjson'"}
}
],
"code": "invalid_query_parameter"
}loc[1] is the parameter name, msg is the reason, and input is the value you sent. Write them to your log:
import os import requests r = requests.get( "https://api.danadresse.dk/adresser", params={"postnr": "8000", "per_side": 5000}, headers={"X-Api-Key": os.environ["DANADRESSE_API_KEY"]}, timeout=10, ) if r.status_code == 400: body = r.json() print(r.status_code, body["code"]) for err in body["details"]: print(f" {err['loc'][-1]} = {err['input']!r}: {err['msg']}")
The common causes:
Symptom in details | Cause | Fix |
|---|---|---|
per_side, less_than_equal | per_side above the limit | At most 1000 on most lists, 100 on /autocomplete. See Pagination |
side, greater_than_equal | side=0 | Pages start at 1 |
format or struktur, literal_error | A value that does not exist | See Formats |
srid, x, y, int_parsing or float_parsing | Text instead of a number, e.g. a comma as decimal separator | Use a dot: x=12.5683 |
srid, value_error, "srid skal være 4326 (WGS84) eller 25832 (ETRS89/UTM32)" | Another coordinate system, e.g. srid=3857 | Use 4326 (default) or 25832. See Coordinates and srid |
x or y, missing | Reverse without coordinates | Send both x and y |
entitet, missing | /replikering/udtraek without an entity | ?entitet=adresse |
On /api/v1 the matching error is 422 validation_error, and the list is in error.details.
Other 400 codes
code | Cause | Fix |
|---|---|---|
invalid_kvh | kvh is not exactly 12 characters | Pad the house number with leading _: 03301407___1 |
invalid_kvhx | kvhx is not exactly 19 characters | kvh + floor (3) + door (4), padded with _ |
invalid_cirkel | cirkel is not three numbers, or the radius is outside 1-50,000 m | cirkel=longitude,latitude,radius |
invalid_polygon | polygon is not valid GeoJSON, or has fewer than three points | polygon=[[[longitude,latitude],…]], URL-encoded |
invalid_parameter | matrikelnr without ejerlavkode, or husnrfra/husnrtil that is not a house number | details.parameter names the parameter. Send ejerlavkode too, or a house number such as 5 or 5A |
Misspelled parameter names give no error
A parameter name the API does not know is ignored, as it was in DAWA. Known filters do filter, also on /autocomplete (postnr, kommunekode, adgangsadresseid) and together with cirkel, polygon and medtagnedlagte. But ?postnummer=8000 does not give an error. It gives the first 100 addresses in the whole country, because the filter was never applied. The same happens with ?kommune=0101 instead of kommunekode, ?pr_side= instead of per_side, and format=csv on endpoints that only return JSON.
If the result is too broad, check the names against the API description at /openapi.json. This function does it automatically before sending the call:
import os import requests API = "https://api.danadresse.dk" _spec = requests.get(f"{API}/openapi.json", timeout=10).json() def unknown_params(path: str, params: dict) -> list[str]: """Parameter names the endpoint does not know (and would therefore ignore).""" known = {p["name"] for p in _spec["paths"][path]["get"].get("parameters", [])} return sorted(set(params) - known - {"api_key"}) def checked_get(path: str, params: dict) -> requests.Response: bad = unknown_params(path, params) if bad: raise ValueError(f"Unknown parameters on {path}: {bad}") return requests.get(API + path, params=params, timeout=10, headers={"X-Api-Key": os.environ["DANADRESSE_API_KEY"]}) print("Unknown:", unknown_params("/adresser", {"postnummer": "8000", "per_side": 10})) r = checked_get("/adresser", {"postnr": "8000", "per_side": 1}) print(r.status_code, r.json()[0]["adressebetegnelse"])
Fetch the specification once at startup, not on every call.
404 not_found: the id or code does not exist
{
"type": "ResourceNotFoundError",
"title": "Adresse 0a3f507a-ec01-32b8-e044-0003ba298018 not found",
"details": null,
"code": "not_found"
}Go through the list:
- Access-address id on an address endpoint. The ids are different. The example above is an access-address id sent to
/adresser/{id}. Use/adgangsadresser/{id}, or/adresser?adgangsadresseid=…for the units behind the door. The other way round works:/adgangsadresser/{id}with an address id answers with the access address the unit belongs to. See Adresse or adgangsadresse? - An id from another system. An id that does not come from the address register (for example an internal customer id or an id from a test system) does not exist here.
- Reverse with no address nearby.
/adgangsadresser/reversegives404with"title": "No address within range"when there is no address near the point. Usuallyxandyare swapped, or UTM coordinates were sent withoutsrid=25832. See Coordinates and srid. - Whitespace or a line break in the id. Strip whitespace before putting the id in the URL.
Codes without leading zeros are not a cause: /kommuner/101 and kommunekode=101 work and answer with 0101. Enrichments under /api/v1/enrich/adresser/{id}/… take both an access-address id and an address id.
Try it yourself with an access-address id on the address endpoint:
curl -s "https://api.danadresse.dk/adresser/0a3f507a-ec01-32b8-e044-0003ba298018" \ -H "X-Api-Key: $DANADRESSE_API_KEY"
404 route_not_found: the path does not exist
route_not_found means the URL does not match any endpoint:
/api/v1in front of a DAWA path. The DAWA paths live at the root:https://api.danadresse.dk/adresser, not/api/v1/adresser.- An old path from DAWA's documentation that does not exist here. See Compatibility for the list of supported paths.
- Typos:
/adresseinstead of/adresser,/postnummerinstead of/postnumre.
The format depends on the path. If it starts with a DAWA path (/adresser…), you get the DAWA format. Otherwise you get the /api/v1 format:
{
"detail": {"error": {"code": "route_not_found", "message": "Not Found"}},
"error": {"code": "route_not_found", "message": "Not Found"}
}404 unknown_entity: wrong replication entity
/replikering/udtraek?entitet=… and /replikering/{entitet}/haendelser know a fixed set of entities. A wrong name gives unknown_entity, and details.supported lists the ones that work:
{
"type": "ResourceNotFoundError",
"title": "Unknown replication entity 'adressen'",
"details": {"supported": ["adgangsadresse", "adresse", "…"], "legacy_names": ["adgangsadresser", "adresser", "…"]},
"code": "unknown_entity"
}Entities are singular: adresse, adgangsadresse, postnummer. The old plural names such as adgangsadresser are accepted too and listed in details.legacy_names. See Replication: events.
Next steps
- No results? when the call succeeds but the list is empty.
- The error format
- Adresse or adgangsadresse?
Related guides
Markdown source Spotted an error? Tell us API-reference (Swagger)