Error 403: access denied

Work out why a valid key is refused from the code in the 403 response, and fix the domains, key type, IP list or a blocked key.

5 min Level: Intermediate ✓ Code tested

Status 403 means the API knows your key but will not run this particular call with it. There are several reasons, and the code in the response tells you which. Find the code and go straight to the matching section.

codeIn short
origin_not_allowedBrowser key used from a domain not on its list, or from a server
browser_key_scopeBrowser key used on an endpoint it may not call
sandbox_scopeTest key used on an endpoint it may not call
key_blockedThe key is blocked because it looks leaked
ip_not_allowedIP-locked key used from another IP address
account_suspendedThe account is suspended
forbiddenYour IP is blocked by the abuse filter

On the DAWA paths the code is in code and type is ForbiddenError. On /api/v1 it is in error.code. See The error format.

This script shows the status, code and message for a call with your key, and whether the key is flagged as possibly leaked:

Python#diagnose-403
import os

import requests

r = requests.get(
    "https://api.danadresse.dk/postnumre/8000",
    headers={"X-Api-Key": os.environ["DANADRESSE_API_KEY"]},
    timeout=10,
)
try:
    body = r.json()
except ValueError:
    body = {}
err = body.get("error") if isinstance(body.get("error"), dict) else body
print(r.status_code, err.get("code") or "OK", err.get("title") or err.get("message") or "")
status = r.headers.get("X-Danadresse-Key-Status")
if status:
    print("Key status:", status)

origin_not_allowed

JSON#origin-body
{
  "type": "ForbiddenError",
  "title": "This API key is locked to shop.firma.dk, *.firma.dk and this request came from firma.dk. Add the domain under API-nøgler → Tilladte domæner, or use a server-side key.",
  "details": null,
  "code": "origin_not_allowed"
}

The key has Allowed domains, and the host in the call's Origin header (or Referer when Origin is missing) is not on the list. The message shows both the list and the host that was seen.

The call comes from your server.

  • Cause: A server sends neither Origin nor Referer, so the message says came from no Origin/Referer. A domain-locked key only works from a browser.
  • Fix: Create a separate key without domains for the server.

The domain is missing a variant.

  • Cause: The list has www.firma.dk, but the site runs on firma.dk, or the other way round. *.firma.dk only covers subdomains, not firma.dk itself.
  • Fix: Add both, for example firma.dk and *.firma.dk. The port does not matter, only the host name counts.

Development on your own machine.

  • Cause: localhost is not allowed by itself.
  • Fix: Add localhost to the list, or use a test key for development.

The page sends no Origin or Referer.

  • Cause: Pages opened as file://, sandboxed iframes (Origin null) and <script> tags on pages with Referrer-Policy: no-referrer give no host to compare against.
  • Fix: Serve the page from a real web address, and use strict-origin-when-cross-origin or something more permissive as the Referrer-Policy.

You set domains under Keys. See Browser keys and allowed domains.

browser_key_scope

  • Cause: A domain-locked key may only do address lookups. /api/v1/enrich/…, /api/v1/datavask/parse and /api/v1/stats/national are refused because they fetch data from public registers with a shared key.
  • Fix: Make the call from your own server with a key without domains, and pass only what the page needs on to the browser.

sandbox_scope

  • Cause: A test key (dawa_test_…) was used on /api/v1/enrich/…, /api/v1/datavask/parse or /api/v1/stats/national.
  • Fix: Use a live key for those endpoints. See Test keys and the sandbox.

key_blocked

A live key used from many different IP addresses in one day looks like a leaked key: it sits in a public app, in frontend code or in a repository. The sequence is:

  1. The first day, responses get the header X-Danadresse-Key-Status: warn and you get an email. Calls still work.
  2. If the pattern continues after 72 hours, the key is blocked. Responses get X-Danadresse-Key-Status: blocked and status 403 with key_blocked.
JSON#blocked-body
{
  "type": "ForbiddenError",
  "title": "This API key is blocked: it was used from … or more different IP addresses in one day — the pattern of a leaked key. …",
  "details": {
    "fix": "https://danadresse.dk/dashboard/keys",
    "page_host": "shop.firma.dk"
  },
  "code": "key_blocked"
}

details.page_host is included when the calls come from a web page. The key has then usually been put in frontend code.

Fix:

  • If the key is used in a browser, set Allowed domains on it. Domain-locked keys are not covered by leak control, because each visitor has their own IP.
  • If the key has leaked, create a new one under Keys, switch to it and delete the old one. See Rotate keys without downtime.
  • If you run many servers behind changing IPs, write to us through support.

Test keys are not covered by leak control.

ip_not_allowed

JSON#ip-body
{
  "type": "ForbiddenError",
  "title": "This API key is locked to specific IP addresses and this request came from 203.0.113.7. Add the address under API-nøgler → Tilladte IP-adresser.",
  "details": {"ip": "203.0.113.7"},
  "code": "ip_not_allowed"
}

IP locking is available on Enterprise. details.ip is the address the API saw.

  • The server reaches the internet through a different address than the one on the list (NAT, a new load balancer, a new cloud region).
  • The server uses IPv6 and the list only has IPv4, or the other way round.
  • Outgoing IPs on serverless and PaaS change often. Use a network with a fixed outgoing IP.

Add the address or network under Keys.

account_suspended

  • Cause: The whole account is suspended, for example because of a missing payment or abuse. Every key gets the same error, and so does login to the dashboard.
  • Fix: Write to us through support.

forbidden

JSON#forbidden-body
{
  "type": "ForbiddenError",
  "title": "Blocked: abuse detected. Contact support if this is an error.",
  "details": null,
  "code": "forbidden"
}
  • Cause: Our abuse filter has blocked the IP address, for example after attempts to fetch files such as /.env or attack patterns in the URL. The block applies to every call from that IP, whatever the key. On the DAWA paths the response has the DAWA format shown above, on /api/v1 it is {"error": {"code": "forbidden", …}}. It carries Access-Control-Allow-Origin: *, so browser code can read the 403.
  • Fix: Write to us through support with the IP address and the time.

Next steps

Related guides

Markdown source Spotted an error? Tell us API-reference (Swagger)

Try it with a free key

2,000 calls/month on the free plan. No credit card.

Create account →