Generator · Danish data

Danish CPR

Endpoint GET /v1/dk/cpr

What it covers

Returns a valid Danish CPR (Det Centrale Personregister) number. Constrain with `sex` and age/birth-date filters (the full date is encoded). Contradictory combinations are rejected with a 400. Use `checksum` to choose the mod-11 standard, `format` for the rendering, and `invalid=true` for a deliberately wrong check.

How it’s calculated

The Danish CPR number's 10th digit is a weighted mod-11 check digit that makes the whole 10-digit number's weighted sum divisible by 11 (a scheme abandoned for newly-issued numbers since 2007, which this generator can also model).

Algorithm

  1. Take the first 9 digits of the CPR: the 6-digit birth-date head DDMMYY, followed by a 7th digit (century/serial marker) and two more serial digits. Call these d1..d9.
  2. Apply the fixed position weights [4, 3, 2, 7, 6, 5, 4, 3, 2] to d1..d9 respectively, and sum the products: partial = 4*d1 + 3*d2 + 2*d3 + 7*d4 + 6*d5 + 5*d6 + 4*d7 + 3*d8 + 2*d9.
  3. Compute partial mod 11.
  4. The check digit (weight 1, the 10th and final digit) is the value that makes the total divisible by 11: last = (11 - (partial mod 11)) mod 11.
  5. If last comes out as 10 there is no single-digit answer, so this serial is invalid — discard it and redraw the middle serial digits (the generator loops until a valid one is found).
  6. Otherwise last is a digit 0-9; append it as the 10th digit. The full number then satisfies (4*d1 + 3*d2 + 2*d3 + 7*d4 + 6*d5 + 5*d6 + 4*d7 + 3*d8 + 2*d9 + 1*last) mod 11 == 0.

Worked example

CPR 010190-412? (born 1 Jan 1990, 7th digit 4, serial digits 1,2, check digit ?)

First nine digits: 0 1 0 1 9 0 4 1 2 (DDMMYY=010190, century/serial digit=4, middle=12).
Weights [4,3,2,7,6,5,4,3,2] applied position by position:
0*4=0, 1*3=3, 0*2=0, 1*7=7, 9*6=54, 0*5=0, 4*4=16, 1*3=3, 2*2=4.
partial = 0+3+0+7+54+0+16+3+4 = 87.
87 mod 11 = 10  (11*7 = 77, 87-77 = 10).
last = (11 - 10) mod 11 = 1 mod 11 = 1. Not 10, so it is valid.
Verify: full weighted sum = 87 + 1*1 = 88 = 11*8, divisible by 11.

The check digit is 1, giving the full CPR 0101904121 (formatted 010190-4121).

Edge cases

  • When (11 - (partial mod 11)) mod 11 == 10, no valid check digit exists for that serial; the generator rejects it and redraws the middle serial digits rather than emitting an 11-digit or truncated number.
  • The 7th digit combined with the 2-digit year encodes the birth century (per the official CPR table: 0-3 -> 1900s; 4/9 -> 2000s if YY<=36 else 1900s; 5-8 -> 2000s if YY<=57 else 1800s). Encodable date range is 1858-2057.
  • The last (check) digit also carries sex: even = female, odd = male. In modulus-11 mode a requested sex is honored by redrawing until the forced check digit has the matching parity.

Gotchas

  • Since 2007 the CPR office stopped guaranteeing the mod-11 check because valid serials were running out for some dates; the generator's 'none' checksum mode models these post-2007 numbers by drawing the last digit freely (correct parity only), so a real modern CPR may legitimately FAIL this mod-11 test.
  • Leading zeros are significant — CPR numbers are handled as strings throughout (toDigits enforces a digit string) and must never be parsed through Number, or dates like 01xxxx would lose their leading zero.
  • The check digit has weight 1, not a positional weight from the head; the [4,3,2,7,6,5,4,3,2] weights cover only the first nine digits.

Parameters

ParameterTypeDescription
atAgeintegerExact age in years at request time.
bornAfterstringpattern ^\d{4}(?:-\d{2}(?:-\d{2})?)?$Born after this date or fragment (YYYY, YYYY-MM, YYYY-MM-DD).
bornBeforestringpattern ^\d{4}(?:-\d{2}(?:-\d{2})?)?$Born before this date or fragment (YYYY, YYYY-MM, YYYY-MM-DD).
bornOnstringpattern ^\d{4}(?:-\d{2}(?:-\d{2})?)?$Born on a date or within a fragment: YYYY, YYYY-MM, or YYYY-MM-DD.
checksum"modulus-11" | "none"Checksum standard: `modulus-11` (default) or `none` (post-2007 model).
countinteger1–1000Number of records to return, 1–1000 (your plan may lower this). Omitted returns a single record; set it for a batch.
edgebooleanRestrict output to edge-case values.
extremebooleanReturn a correct value wrapped in a hostile encoding (untrimmed whitespace, invisible/zero-width characters, BOM, or bidi/combining marks). Homoglyphs are excluded so the value stays machine-parseable; it still validates after normalisation.
format"plain" | "with-hyphen"Rendering: bare 10 digits (default) or `DDMMYY-SSSS`.
invalidbooleanReturn a CPR with a deliberately wrong mod-11 check; date/sex stay valid.
olderThanintegerMinimum age in years at request time.
seedintegerInteger seed for reproducible output: the same seed always yields the same record.
sex"m" | "f"Encoded sex: "m" or "f".
youngerThanintegerMaximum age in years at request time.

Response fields

FieldTypeDescription
valuestringThe CPR rendered in the requested format (plain or `DDMMYY-SSSS`).
digitsstringThe canonical 10-digit CPR.
birthDatestringThe encoded birth date, ISO `YYYY-MM-DD`.
sex"m" | "f"Encoded sex: `m` (odd) or `f` (even) last digit.

Other generators for Denmark

See all generators for Denmark →

The same kind of identifier elsewhere

Try it

Generate a live example below — every response is valid by format and entirely synthetic.