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
- 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.
- 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.
- Compute partial mod 11.
- 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.
- 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).
- 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
| Parameter | Type | Description |
|---|---|---|
| atAge | integer | Exact age in years at request time. |
| bornAfter | stringpattern ^\d{4}(?:-\d{2}(?:-\d{2})?)?$ | Born after this date or fragment (YYYY, YYYY-MM, YYYY-MM-DD). |
| bornBefore | stringpattern ^\d{4}(?:-\d{2}(?:-\d{2})?)?$ | Born before this date or fragment (YYYY, YYYY-MM, YYYY-MM-DD). |
| bornOn | stringpattern ^\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). |
| count | integer1–1000 | Number of records to return, 1–1000 (your plan may lower this). Omitted returns a single record; set it for a batch. |
| edge | boolean | Restrict output to edge-case values. |
| extreme | boolean | Return 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`. |
| invalid | boolean | Return a CPR with a deliberately wrong mod-11 check; date/sex stay valid. |
| olderThan | integer | Minimum age in years at request time. |
| seed | integer | Integer seed for reproducible output: the same seed always yields the same record. |
| sex | "m" | "f" | Encoded sex: "m" or "f". |
| youngerThan | integer | Maximum age in years at request time. |
Response fields
| Field | Type | Description |
|---|---|---|
| value | string | The CPR rendered in the requested format (plain or `DDMMYY-SSSS`). |
| digits | string | The canonical 10-digit CPR. |
| birthDate | string | The encoded birth date, ISO `YYYY-MM-DD`. |
| sex | "m" | "f" | Encoded sex: `m` (odd) or `f` (even) last digit. |
Other generators for Denmark
The same kind of identifier elsewhere
Try it