Rotate API keys without downtime
Replace an API key without a single call failing, respond to a leaked or blocked key, and store the key safely on your platform.
An API key should be replaced when an employee with access leaves, when the key has been visible somewhere it should not (a commit, a log, a screen share), or when we have flagged it as possibly leaked. Replacing it at fixed intervals is also a good habit.
Because an account can have several active keys at once, you can switch without downtime: create the new one, put it to use, and revoke the old one only when nothing uses it any more.
1. Create a new key
Go to Keys, give the new key a name that says where it will be used (e.g. webshop-prod-2026-10), choose live and click Opret (create). The key is shown only once. We store only a hash of it and cannot show it again.
The number of active live keys per account is limited:
| Plan | Live keys |
|---|---|
| Hobby | 2 |
| Basis | 8 |
| Pro | 20 |
| Enterprise | 50 |
Test keys do not count. If you are at the cap you cannot create a new live key (403 key_cap_reached from the API). Revoke a key you do not use before you start. If you only have one key in use, there is always room for one more, also on Hobby.
If the old key was a browser key with Tilladte domæner (allowed domains), enter the same domains on the new one before you use it. Otherwise it will not answer from your pages, or it will be seen from many IP addresses and flagged as leaked.
2. Test the new key
Make one call with the new key before it goes to production:
curl -s -o /dev/null -w "%{http_code}\n" \ "https://api.danadresse.dk/postnumre/8000" \ -H "X-Api-Key: $DANADRESSE_API_KEY"
200 means the key works. 401 means it was copied wrong, often with an extra space or line break.
3. Put the new key to use
Put the new key in your secret store and restart or redeploy your service. Both keys work at the same time, so it does not matter that some instances run with the old one for a while.
It gets easier next time if the code reads the key from a file or an environment variable at start-up instead of having it in the code:
import os from pathlib import Path def api_key() -> str: """Read the key from a secret file (Docker/Kubernetes) or from an environment variable.""" path = os.environ.get("DANADRESSE_API_KEY_FILE") if path: return Path(path).read_text(encoding="utf-8").strip() key = os.environ.get("DANADRESSE_API_KEY", "").strip() if not key: raise RuntimeError("DANADRESSE_API_KEY or DANADRESSE_API_KEY_FILE is missing") return key
.strip() removes the line break that many tools put at the end of a secret file.
Where the key belongs depends on the platform:
| Platform | Place |
|---|---|
| Local development | A .env file listed in .gitignore |
| Docker / Kubernetes | Docker secrets or a Kubernetes Secret, mounted as a file or environment variable |
| GitHub Actions | Repository or environment secrets |
| Vercel, Netlify and similar | The project's environment variables, marked as secret |
| AWS, Azure, Google Cloud | Secrets Manager or Parameter Store, Key Vault, Secret Manager |
| PHP on shared hosting | A configuration file outside the web root, or an environment variable in the virtual host setup |
| WordPress | A constant in wp-config.php that is not in your repository |
| Browser | Nothing can be kept secret. Use a domain-locked browser key |
4. Wait until the old key is quiet
The Sidste brug (last used) column on Keys shows when each key was last used. It updates at most once a minute. Wait until the time for the old key stops moving. If you have nightly jobs or cron tasks, wait a day so they have also run with the new key.
If the old key keeps being used, something still runs with it. Typical places are a forgotten staging server, a cron job, a colleague's local .env or a plugin setting in a CMS.
5. Revoke the old key
Click Tilbagekald (revoke) next to the old key. It takes effect at once: the next call with it gets 401 invalid_api_key. If you missed a place in step 4, that place will start failing now, so watch your errors for the next few hours.
When a key is leaked or blocked
We watch how many different IP addresses a live key is used from in a day. If it is many, it looks like a key that got out:
- The key is flagged, responses get the header
X-Danadresse-Key-Status: warn, and you get an email. - If it keeps being used from many IP addresses after 72 hours, it is blocked. Calls get
403 key_blocked, and the header saysblocked.
Test keys and domain-locked browser keys are not covered, because they are meant to be used from many places.
What to do depends on the cause:
- The key sits in a web page. The many IP addresses are your visitors, so the key has not leaked. Lock it to your domains under Tilladte domæner. See Browser keys and allowed domains.
- The key got out (a public repository, an app, a customer). Follow steps 1 to 5 right away, and revoke the old key without waiting for step 4 if you know it is being misused.
- You know why the number is high (e.g. a server behind many changing outbound IP addresses). Use Lås op (lift the block) or Nulstil advarsel (reset warning) on the key in the dashboard. On Enterprise you can instead lock the key to your IP addresses.
Also check whether the key is in your Git history. A key deleted in the latest commit is still in the older ones:
git log -p --all -S "dawa_live_" -- . | grep -n "dawa_live_" | head
If you find it, rotate the key. Rewriting the history is not enough, because copies of the repository may exist elsewhere.
Next steps
- Lock a key to domains: Browser keys and allowed domains.
- See the key status in your logs: Monitor usage.
- The full list before production: Go-live checklist.
Related guides
Markdown source Spotted an error? Tell us API-reference (Swagger)