Denmark CPR check
Enter 0707610018 and see a valid Danish CPR number for a woman born on 7 July 1961, with the mod-11 control passing. Hyphenated forms such as 070761-0018 work too. Useful for account opening and HR imports. Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
Worked example
- Number
- 0707610018
- Input
- 0707610018
- Birth date
- 1961-07-07
- Day
- 7
- Month
- 7
The Danish CPR number (Det Centrale Personregister personnummer) is the national civil identifier used for taxation, healthcare, banking, employment, and most public-facing forms in Denmark. Classic numbers apply a weighted modulo-eleven check across all ten digits so that a single mistyped figure is usually rejected before the identifier is stored or matched. This capability validates that structure only. It checks that the embedded date is a real calendar day under the official century rules on the seventh digit, reports whether the classic mod-eleven sum divides cleanly, and returns the ISO birth date plus gender inferred from the last digit (odd male, even female). Hyphenated display forms such as DDMMYY-XXXX are accepted after normalization.
How a Danish CPR number is structured and checked
Under Danish civil-registration practice, a CPR personnummer is ten digits written either compactly as DDMMYYXXXX or with a hyphen after the date as DDMMYY-XXXX. Positions one and two are the day of birth, three and four the month, and five and six the two-digit year.
The seventh digit participates in century resolution so that the full four-digit birth year can be reconstructed without a separate century field: digits zero through three map to nineteen hundred plus the two-digit year; digits four and nine map to two thousand when the year is thirty-six or less and otherwise stay in nineteen hundred; digits five through eight map to two thousand when the year is fifty-seven or less and otherwise to eighteen hundred for the oldest cohorts.
Once the full year is known, the capability verifies that the day exists in that month, including twenty-nine February only in Gregorian leap years, using pure arithmetic rather than the host clock. The classic control rule multiplies the ten digits by the fixed weights four, three, two, seven, six, five, four, three, two, and one and requires the weighted sum to be divisible by eleven. The last digit is also the gender indicator: odd for male and even for female.
Where Danish CPR checks fit real workflows
Banks and fintechs validate ten-digit CPR values at account opening so a single transposition does not create a phantom customer and a rejected KYC record. ETL jobs that load Danish customer or employee master files walk every CPR column in a CSV or XML feed, call this capability, and quarantine rows where valid is false—catching both human edits and off-by-one bugs in generators.
Municipal and school systems often accept both hyphenated and compact forms; stripping spaces and hyphens before the digit check keeps the contract simple without inventing a second algorithm. Do not treat a passing CPR check as proof that the holder is alive in the population register, has a Danish health card, or is authorized for a service; it only proves the date is calendar-valid under the century rules and that the classic mod-eleven digit is consistent with the official weights when that rule still applies.
What you can do with it
Validate typed Danish CPR on onboarding forms
When a signup, bank KYC, or HR form collects a personnummer, call the check and block submission when valid is false while still showing the extracted ISO birth date for correction.
Audit CPR columns in Danish master data
Batch-validate every CPR in a CRM, EHR, or payroll CSV and quarantine rows whose date is impossible or whose classic mod-eleven control no longer matches.
FAQ
What does the Denmark CPR check validate?
It checks that a ten-digit Danish personnummer embeds a real calendar birth date (with century from the seventh digit) and that the classic weighted mod-11 control over all ten digits passes.
What format should I send?
Send ten digits DDMMYYXXXX, optionally with a hyphen or spaces (for example 070761-0018). After stripping spaces and hyphens the body must be exactly ten digits 0–9.
What does the Denmark CPR check cost?
The browser tool is free and runs locally.
Can a genuine CPR number fail the mod-11 check?
Yes. Numbers issued after the October 2007 relaxation of the rule may be real even when the mod-11 sum fails. The tool reports that check separately, so you can apply a strict policy or accept a valid date alone.
How is the full birth year worked out?
From the seventh digit and the two-digit year. Digits 0 to 3 mean the 1900s; 4 and 9 mean the 2000s up to year 36, otherwise the 1900s; 5 to 8 mean the 2000s up to year 57, otherwise the 1800s.
Related tools
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/enc/cpr-denmark \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"number":"0707610018"}'const res = await fetch("https://api.kit.forhosting.com/enc/cpr-denmark", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"number": "0707610018"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/enc/cpr-denmark",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"number": "0707610018"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/enc/cpr-denmark", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"number":"0707610018"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"number":"0707610018"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/enc/cpr-denmark", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"number": "0707610018"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "enc.cpr_denmark",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |
Technical notes
In its standard form it is ten digits: six digits for the birth date as day-month-year (DDMMYY), then four digits for a serial that encodes century hints, a running sequence, and a trailing control digit. Wrong length, non-numeric bodies, and impossible dates fail with invalid_input instead of silent stripping. The same pure arithmetic runs free in the browser and at the published API price, with no population-register lookup and no claim that a passing check means the person is living, registered, or entitled to any service.
How a Danish CPR number is structured and checked
Numbers issued after the October two thousand seven relaxation of the check may be government-issued even when the mod-eleven sum fails; this tool still reports mod11_valid so callers can apply a strict policy or a date-only policy.
Input contract, response shape, and error behaviour
After normalization the engine requires exactly ten digits. It parses day, month, and two-digit year, resolves the full year from the seventh digit, and rejects impossible calendar dates with a clear message naming the bad day or month. Wrong length after strip, empty strings, missing number, and non-digit bodies fail before any checksum runs and are not billed. POST a JSON body with a required number field containing the CPR body. Spaces and hyphens are stripped so common Danish display forms such as zero seven zero seven six one hyphen zero zero one eight are accepted after normalization; other punctuation and letters are rejected as non-numeric input with invalid_input so a silently stripped value never reaches storage. On a real date the response always includes input as the normalized ten-digit string, valid true only when the classic mod-eleven control also passes, birth_date as an ISO year-month-day string, day, month, and year as integers, date as the six-digit DDMMYY fragment, serial as the four-digit tail, gender as male or female, mod11_valid as a boolean mirror of the checksum, check_digit as the last character, length ten, and algorithm denmark-cpr-mod11. A well-formed string with a real birth date but a wrong control digit returns valid false and mod11_valid false without throwing, so forms can show the extracted birth date while still blocking enrollment. Alias keys such as cpr, digits, text, value, code, and id are accepted for the same payload field so integrations that already speak Danish field names do not need a rename. Because the path is pure parse with no clock, random source, or network, the same JSON body always yields the same JSON result on the free browser tool and on the API at $0.002 per successful request. Prefer this endpoint when you gate a form, KYC file, HR import, or healthcare intake on a CPR typed by a user or partner feed and need both the date check and the classic control in one call.
Where Danish CPR checks fit real workflows
Healthcare portals and pharmacy systems recompute birth_date and mod11_valid when a clinic reads a number over the phone or pastes it from a scanned card, catching mistypes before a journal entry or prescription is filed. Integration tests assert birth_date and gender for fixed samples so form libraries, label printers, and this API never drift.
Integration use cases
Pin fixtures for healthcare and payroll tests
Use known valid samples such as 0707610018 in sandbox patients and employees, asserting birth_date, gender, and mod11_valid so test data stays stable across releases.
Technical questions
What happens if the mod-11 check fails but the date is real?
The task succeeds with valid false and mod11_valid false, still returning birth_date and gender. Post-2007 CPR numbers may be government-issued without a passing classic checksum; use mod11_valid if you need a looser policy.
When do I get invalid_input?
Missing number, empty string, non-numeric characters after strip, length other than ten, or an impossible calendar date (bad month or day) all fail with invalid_input and are not billed.