CNP validation
Checks the check digit of a personal numeric code and reads the information encoded in it: sex, date of birth, age and county.
A correct check digit does NOT mean the person exists — the response says so explicitly, in confirma_existenta_persoanei. A valid code can be generated in two minutes; the route checks the form, not the identity.
Zero retention: the code is written nowhere — not to logs, not to statistics, not to a cache. The route is POST and non-cacheable, so the response is not stored either.
Request body
Sent as JSON in the request body (Content-Type: application/json) — not in the query string.
{
"cnp": "1800101410013"
}
{
"cnp": "2850722120015"
}
{
"cnp": "7900315400025"
}
{
"cnp": "1800101410014"
}
Query parameters
How it behaves
- Verifies the check digit of a Romanian personal numeric code and reads the information encoded in it: sex, date of birth, age and county.
- A correct check digit does NOT mean the person exists. The response says so explicitly, in
confirma_existenta_persoanei, which is alwaysfalse. A valid CNP can be generated in two minutes; this route checks the form, not the identity. Used as an identity check, it gives false confidence. - Zero retention. The CNP is never written anywhere: not to logs, not to usage statistics, not into cache keys. That is not merely a policy — the route is
POSTand non-cacheable, and the component that could store a response only works onGET, so the data has no path to storage. - An invalid CNP is not a bad request: the response is 200 with
valid: false. - Foreign residents (first digit 7, 8 or 9): the century is not encoded, so the date of birth cannot be established and is not guessed —
data_nasteriiandvarstaarenull, andan_incertistrue. With digit 9 the sex is not encoded either. An invented birth date with a 50% chance of being right would be worse than its absence. Day and month are still checked in both plausible centuries (1900 and 2000): the CNP is accepted if the date exists in at least one — 29 February of year "00" exists only in 2000. - County code 70 is not a county. Since May 2024, through the integrated civil-status system (SIIEASC), the county component is filled with
70on every new registration regardless of place of birth. The CNP is perfectly valid, but the county is no longer encoded —judet.denumireisnullandjudet.motivisnu_e_codificat. CNPs issued earlier are unchanged. - The county codes are a numbering of their own, not to be confused with SIRUTA or with vehicle-plate codes: 01 Alba … 39 Vrancea in the alphabetical order of the 1968 division, 40 Bucharest, 41–46 its districts, 47–48 the former districts 7 and 8 (abolished in 1979, but present in CNPs issued until then), and Călăraşi and Giurgiu — created in 1981 — at the end, 51 and 52.
- The sequence number inside the CNP (the daily counter) is not returned: it has no use for a client and would be one more personal detail travelling for nothing.
What it is for
- Checking a CNP in a form before storing it.
- Auto-filling sex and date of birth.
- Catching typing errors through a mismatch between the CNP and a manually entered date.
Response structure
M, F, or null when the first digit is 9.
null for foreign residents, where the century is not encoded.
null when the date of birth cannot be established.
denumire is null and motiv explains why.
true when the century of birth is not encoded.
true when the first digit is 7, 8 or 9.
false. Present precisely so it is not assumed otherwise.
Response examples
body: {"cnp": "1800101410013"}
{
"data": {
"valid": true,
"sex": "M",
"data_nasterii": "1980-01-01",
"varsta": 46,
"judet": {
"cod": 41,
"denumire": "Bucureşti — Sectorul 1",
"motiv": null
},
"an_incert": false,
"rezident_strain": false,
"confirma_existenta_persoanei": false
},
"meta": {
"temei": "structura CNP + cifra de control (cheia 279146358279).",
"retentie": "zero — CNP-ul şi datele derivate nu se stochează şi nu se cache-uiesc.",
"nota": "CNP valid ca structură. Verificarea nu atestă că persoana există."
}
}
body: {"cnp": "2850722120015"}
{
"data": {
"valid": true,
"sex": "F",
"data_nasterii": "1985-07-22",
"varsta": 41,
"judet": {
"cod": 12,
"denumire": "Cluj",
"motiv": null
},
"an_incert": false,
"rezident_strain": false,
"confirma_existenta_persoanei": false
},
"meta": {
"temei": "structura CNP + cifra de control (cheia 279146358279).",
"retentie": "zero — CNP-ul şi datele derivate nu se stochează şi nu se cache-uiesc.",
"nota": "CNP valid ca structură. Verificarea nu atestă că persoana există."
}
}
body: {"cnp": "7900315400025"}
{
"data": {
"valid": true,
"sex": "M",
"data_nasterii": null,
"varsta": null,
"judet": {
"cod": 40,
"denumire": "Bucureşti",
"motiv": null
},
"an_incert": true,
"rezident_strain": true,
"confirma_existenta_persoanei": false
},
"meta": {
"temei": "structura CNP + cifra de control (cheia 279146358279).",
"retentie": "zero — CNP-ul şi datele derivate nu se stochează şi nu se cache-uiesc.",
"nota": "CNP valid de rezident străin: prima cifră (7, 8 sau 9) nu codifică secolul, deci data naşterii NU se poate stabili şi nu e ghicită. Ziua şi luna au fost verificate în ambele secole plauzibile (1900 şi 2000) — CNP-ul e acceptat dacă data există în cel puţin unul, de pildă 29 februarie din anul „00”, care există doar în 2000. La cifra 9 nici sexul nu e codificat."
}
}
body: {"cnp": "1800101410014"}
{
"data": {
"valid": false,
"sex": null,
"data_nasterii": null,
"varsta": null,
"judet": null,
"an_incert": null,
"rezident_strain": null,
"confirma_existenta_persoanei": false
},
"meta": {
"temei": "structura CNP + cifra de control (cheia 279146358279).",
"retentie": "zero — CNP-ul şi datele derivate nu se stochează şi nu se cache-uiesc.",
"nota": "CNP invalid: fie cifra de control nu se verifică, fie data ori codul de judeţ nu există. Nu e o eroare a cererii — e rezultatul verificării."
}
}
Error codes
| Code | Slug | When it occurs |
|---|---|---|
| 401 | neautentificat | Token missing, invalid, expired or revoked. |
| 403 | feature_indisponibil | The current plan does not include the feature the route depends on. |
| 422 | validare | cnp missing or not exactly 13 characters. A CNP that is present but wrong returns 200 with valid: false. |
| 429 | cota_depasita | The plan's monthly quota has been exceeded (see Retry-After). |
All errors share the same shape. eroare.cod is stable and is the value you should branch on in code — not the text in mesaj, which may change.
{
"eroare": {
"cod": "validare",
"mesaj": "cnp missing or not exactly 13 characters. A CNP that is present but wrong returns 200 with valid: false."
}
}