CPR Lookup and Validation
Use the CPR endpoints to retrieve a structured snapshot of an individual's CPR record, or to validate customer-provided data against official CPR records.
Prerequisites
- Your account must have a broker with the
Cprcapability enabled. CallGET /api/brokersto confirm. The{brokerId}path parameter is theidvalue from that response. - CPR numbers must be exactly 10 digits.
CPR lookup — `POST /api/cpr/
Retrieves the official CPR record for a person. Use this to get structured name, address, and status data.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
audit.externalReference |
string | Yes | Your reference for this lookup (stored in audit log) |
audit.context |
string | No | Optional context string |
cpr |
string | Yes | 10-digit Danish CPR number |
Response — person object:
| Field | Type | Description |
|---|---|---|
person.cprNumber |
string | The CPR number looked up |
person.name.givenName |
string | First name |
person.name.middleName |
string | Middle name(s), if any |
person.name.surname |
string | Surname |
person.address.streetName |
string | Street name |
person.address.streetNumber |
string | House number |
person.address.floor |
string | Floor, if registered |
person.address.unit |
string | Door/side, if registered |
person.address.careOfName |
string | C/O name, if registered |
person.address.zipCode |
string | Postal code |
person.address.zipName |
string | Postal area name |
person.address.cityName |
string | City |
person.personStatus |
string | Current CPR status — see table below |
person.isNameAddressProtected |
boolean | true if the person has name/address protection (navne- og adressebeskyttelse) |
person.retrievedAtUtc |
string (ISO 8601) | When this snapshot was retrieved |
Person status values:
| Value | Meaning |
|---|---|
Active |
Normally registered person |
ActiveWithHighRoadCode |
Active, with high road code |
ActiveRegisteredResidenceInGreenlandicCpr |
Active, Greenlandic CPR |
ActiveRegisteredResidenceInGreenlandicCprWithHighRoadCode |
Active, Greenlandic CPR with high road code |
ActiveAdministrativeSocialSecurityNumber |
Administrative SSN, not a natural person |
CancelledSocialSecurityNumber |
Cancelled |
DeletedSocialSecurityNumber |
Deleted |
ChangedSocialSecurityNumber |
Changed to another number |
Missing |
Person missing |
Emigrated |
Person emigrated |
Deceased |
Person deceased |
Note: If
isNameAddressProtectedistrue, name and address fields may be withheld or redacted by the CPR authority. Do not display this information to end-users.
Error codes:
| Status | Meaning |
|---|---|
400 |
Invalid CPR format, rate limit exceeded, or missing required fields |
404 |
CPR number not found in the register |
429 |
Too many requests — see Retry-After header for seconds to wait |
CPR validation — `POST /api/cpr/
Validates customer-provided details against the official CPR record and returns a verdict with per-field scores. Use this for KYC customer validation.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
audit.externalReference |
string | Yes | Your reference (stored in audit log) |
audit.context |
string | No | Optional context string |
cpr |
string | Yes | 10-digit Danish CPR number |
firstNames |
string | Yes | Customer's first and middle names |
lastName |
string | Yes | Customer's surname |
street |
string | Yes | Street name |
houseNumber |
string | Yes | House number |
floor |
string | No | Floor (optional) |
door |
string | No | Door/side designation (optional) |
postalCode |
string | Yes | Postal code |
city |
string | Yes | City |
dateOfBirth |
string | Yes | Date of birth (YYYY-MM-DD) |
Response:
| Field | Type | Description |
|---|---|---|
verdict |
string | Overall match verdict — see table below |
overallScore |
integer | 0–100 composite score across all fields |
name.score |
integer | 0–100 name match score |
name.match |
boolean | Whether the name passed the match threshold |
name.comment |
string | Optional explanation |
address.score |
integer | 0–100 address match score |
address.match |
boolean | Whether the address passed the match threshold |
address.comment |
string | Optional explanation |
dateOfBirth.score |
integer | 0–100 date of birth match score |
dateOfBirth.match |
boolean | Whether the date of birth matched |
dateOfBirth.comment |
string | Optional explanation |
risk.level |
string | Risk level: Low, Medium, or High |
risk.reasons |
string[] | List of reasons contributing to the risk level |
evidence.normalizedCprName |
string | Normalized name from CPR record |
evidence.normalizedCustomerName |
string | Normalized name from your input |
evidence.normalizedCprAddress |
string | Normalized address from CPR record |
evidence.normalizedCustomerAddress |
string | Normalized address from your input |
evidence.cprDateOfBirth |
string | Date of birth from CPR record (YYYY-MM-DD) |
Verdict values:
| Verdict | Meaning |
|---|---|
Match |
All fields match — identity confirmed |
PartialMatch |
Some fields match — further review recommended |
NoMatch |
No significant match found |
NotFound |
CPR number not found in the register |
Error |
Lookup failed — retry or escalate |
The evidence block shows the normalized values used for comparison, so you can understand exactly what was compared and why a partial match was returned.
Error codes:
| Status | Meaning |
|---|---|
400 |
Invalid CPR format, rate limit exceeded, or missing required fields |
429 |
Too many requests — see Retry-After header |
Rate limiting
The CPR endpoints apply rate limiting to protect the upstream registry. If the rate limit is exceeded:
- 400 is returned when too many failed lookups have occurred in a short period
- 429 is returned when the request rate is too high — the
Retry-Afterresponse header contains the number of seconds to wait before retrying
Test data (pre-production)
In the pre-production environment, use fictional CPR numbers from the official CPR test data repository:
https://cprservicedesk.atlassian.net/wiki/spaces/CPR/pages/11436127/Testdata
All data in the pre-production environment is fictional and for testing purposes only.