- Blog
- RENIEC
Cómo validar un DNI en tu sistema: formato y RENIEC
Cómo validar un DNI: formato de 8 dígitos, ceros a la izquierda y consulta a RENIEC vía API. Errores típicos, qué hacer sin resultado y código en JS y Python.
Un cliente escribe su DNI en el formulario de tu tienda, el sistema lo acepta y la boleta sale con un número que no existe. O peor: sale con el DNI de otra persona porque un dígito estaba cambiado. Nadie lo nota hasta que el cliente reclama o hasta que contabilidad revisa los comprobantes del mes.
Validar un DNI es barato y evita exactamente ese tipo de problemas. En esta guía ves cómo hacerlo en dos pasos: primero el formato (8 dígitos, ceros a la izquierda, guardado como texto) y después la existencia, consultando la fuente oficial vía API. Incluye los errores más comunes que vemos en sistemas reales, qué hacer cuando la consulta no devuelve nada y código listo para usar en JavaScript y Python.
Por qué validar el DNI en dos pasos
Validar un DNI significa responder dos preguntas distintas, y cada una tiene su propio costo.
La primera es ¿tiene el formato correcto?. Se responde en el navegador o en tu servidor, sin salir de tu sistema y sin costo. Descarta los errores de tipeo más evidentes: un dígito de menos, una letra, un espacio.
La segunda es ¿existe y a quién corresponde?. Solo la responde la fuente oficial, RENIEC. Esa consulta cuesta (tiempo, y créditos si usas una API), así que conviene hacerla únicamente cuando el formato ya pasó. Si todavía no tienes claro qué contiene el documento y qué devuelve una consulta, empieza por qué es el DNI y qué datos contiene.
Hay una diferencia importante con el RUC. El RUC lleva un dígito verificador dentro de sus 11 dígitos, así que puedes comprobar localmente si está bien escrito. El DNI no: sus 8 dígitos no incluyen ninguna suma de control que puedas calcular. El dígito de verificación que aparece impreso en el documento va aparte y no lo captura un formulario normal. Por eso, en el caso del DNI, el paso dos es la única manera de saber si el número realmente existe.
Validación de formato: 8 dígitos exactos
La regla es una sola: un DNI son exactamente 8 dígitos numéricos. Nada más y nada menos. En JavaScript basta una expresión regular:
function dniFormatoValido(valor) {
const dni = String(valor ?? "").trim();
return /^\d{8}$/.test(dni);
}
dniFormatoValido("12345678"); // true
dniFormatoValido("1234567"); // false: solo 7 dígitos
dniFormatoValido("12345678-3"); // false: incluye el dígito de verificación
dniFormatoValido("1234 5678"); // false: espacio en medio
Y en Python:
import re
def dni_formato_valido(valor: str) -> bool:
dni = (valor or "").strip()
return re.fullmatch(r"\d{8}", dni) is not None
Dos decisiones de diseño que conviene tomar aquí. Primero, limpia antes de validar: quita espacios al inicio y al final, y considera eliminar guiones o puntos que el usuario pudo escribir por costumbre. Segundo, no "arregles" el número en silencio. Si alguien escribe 7 dígitos, no le agregues un cero por tu cuenta: muéstrale el error y deja que lo corrija. Un número inventado por el sistema es peor que un número vacío.
En el formulario, ayuda limitar el campo a 8 caracteres, aceptar solo números y mostrar el teclado numérico en móviles. Son detalles pequeños que reducen muchísimo la cantidad de consultas fallidas.
Ceros a la izquierda y guardar como texto
Este es el error más caro y el más silencioso. Hay números de DNI que empiezan con cero, y son perfectamente válidos. Si tu columna en la base de datos es de tipo entero, "01234567" se guarda como 1234567, con 7 dígitos. Cuando lo vuelvas a leer, no coincidirá con el documento, la consulta a RENIEC fallará y la boleta saldrá con un número incorrecto.
La regla es simple: el DNI es un identificador, no una cantidad. Nunca vas a sumarlo, restarlo ni ordenarlo numéricamente. Guárdalo como texto de 8 caracteres, en la base de datos, en el JSON de tu API interna y en cualquier archivo de intercambio.
Lo mismo aplica a las hojas de cálculo. Si importas un CSV de clientes y Excel o Google Sheets interpretan la columna como número, los ceros iniciales desaparecen antes de que llegues a verlos. Al importar, fuerza la columna a texto o rellena con ceros a la izquierda hasta completar 8 caracteres.
Antes de migrar, rellena con ceros a la izquierda todos los valores de menos de 8 dígitos y guarda el resultado como texto. Después valida la existencia de cada uno, porque algunos de esos ceros pueden ser en realidad datos perdidos.
Errores típicos al capturar un DNI
Después de revisar muchos sistemas de facturación, ventas y RR. HH., los errores se repiten. Esta lista te sirve de checklist:
- Guardar el DNI como número. Ya lo vimos: se pierden los ceros a la izquierda.
- Permitir menos de 8 dígitos. Un campo sin longitud mínima acepta "1234567" sin protestar.
- Aceptar el dígito de verificación. El usuario copia "12345678-3" tal como está en su documento y el sistema guarda 10 caracteres.
- Confundir tipos de documento. Un RUC de 11 dígitos o un carnet de extranjería en el campo de DNI. Tu formulario debe pedir primero el tipo de documento y validar el formato según ese tipo.
- Letras que parecen números. La "O" mayúscula en lugar del "0", la "l" minúscula en lugar del "1". Pasa más de lo que parece cuando se transcribe desde papel.
- Validar solo en el navegador. Una validación en JavaScript se salta con cualquier herramienta. El servidor tiene que repetirla; en validar RUC y DNI en frontend y backend está el patrón completo.
- Guardar lo que escribió el usuario en lugar de lo que devolvió la fuente. Si consultaste el DNI y obtuviste los nombres oficiales, esos son los que van en el comprobante, no los que el cliente tipeó con errores.
Ninguno de estos errores requiere tecnología sofisticada para evitarse. Requieren decidirlo al diseñar el formulario y la base de datos, que es cuando sale gratis.
Validación contra RENIEC vía API
Con el formato validado, el segundo paso es confirmar que el número existe y obtener los nombres oficiales. La forma de automatizarlo es una API que consulte la fuente oficial y devuelva un JSON. Con la API de consulta DNI de endpoint.pe, la llamada es una sola petición autenticada con tu token:
curl https://api.endpoint.pe/v1/dni/12345678 \
-H "Authorization: Bearer TU_TOKEN"
La respuesta trae dni, nombres, apellido_paterno y apellido_materno. Si el número no existe, devuelve un error 404 y esa consulta no se cobra. El número y los nombres de los ejemplos que siguen son ficticios.
Esta llamada siempre se hace desde tu backend. El token no puede estar en el navegador ni en una app móvil, porque cualquiera podría extraerlo. En Node, una función que integra los dos pasos queda así:
const BASE = "https://api.endpoint.pe/v1";
export async function validarDni(dni) {
if (!/^\d{8}$/.test(dni)) {
return { ok: false, motivo: "formato" };
}
const r = await fetch(`${BASE}/dni/${dni}`, {
headers: { Authorization: `Bearer ${process.env.ENDPOINT_TOKEN}` },
});
if (r.status === 404) return { ok: false, motivo: "no_encontrado" };
if (r.status === 429) return { ok: false, motivo: "reintentar" };
if (!r.ok) throw new Error(`Error ${r.status} al consultar el DNI`);
const { data: persona } = await r.json();
return {
ok: true,
nombres: persona.nombres, // "JUAN CARLOS"
apellidoPaterno: persona.apellido_paterno, // "PÉREZ"
apellidoMaterno: persona.apellido_materno, // "QUISPE"
};
}
La misma lógica en Python, con requests:
import os
import re
import requests
BASE = "https://api.endpoint.pe/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['ENDPOINT_TOKEN']}"}
def validar_dni(dni: str) -> dict:
dni = (dni or "").strip()
if not re.fullmatch(r"\d{8}", dni):
return {"ok": False, "motivo": "formato"}
r = requests.get(f"{BASE}/dni/{dni}", headers=HEADERS, timeout=10)
if r.status_code == 404:
return {"ok": False, "motivo": "no_encontrado"}
if r.status_code == 429:
return {"ok": False, "motivo": "reintentar"}
r.raise_for_status()
persona = r.json()["data"]
return {
"ok": True,
"nombres": persona["nombres"],
"apellido_paterno": persona["apellido_paterno"],
"apellido_materno": persona["apellido_materno"],
}
Fíjate en que los distintos resultados se devuelven con un motivo en lugar de lanzar excepciones. Un 404 no es un fallo del sistema: es información ("ese número no existe") y tu interfaz debe tratarlo como tal. Los errores reales (5xx, timeouts) sí se propagan para que tu capa de manejo de errores los reintente o los registre. Si quieres profundizar en la versión Python, en consultar DNI con Python hay una implementación más completa, con lotes y pausas.
Qué hacer si no hay resultado
Que la consulta devuelva 404 no significa que tu sistema falló. Significa que ese número, tal como fue escrito, no figura en la fuente. Las causas, en orden de probabilidad, son: un error de tipeo, un cero inicial perdido en algún punto del camino o un documento que la fuente todavía no refleja. Lo que hagas a continuación depende de tu proceso:
- Muestra un mensaje claro y deja corregir. "No encontramos ese DNI, revísalo" funciona mejor que un error genérico. La mayoría de las veces el usuario ve el dígito equivocado de inmediato.
- No bloquees la operación si el negocio no lo exige. En una venta presencial, perder al cliente por un DNI que no se pudo verificar es peor que emitir con los datos que él te da. Permite el ingreso manual, pero marca el registro como "no verificado" para revisarlo después.
- No guardes el resultado negativo por mucho tiempo. Un 404 de hoy no garantiza un 404 mañana. Si cacheas respuestas, dale a las negativas una vida corta.
- Distingue "no existe" de "no pude consultar". Un 429 (límite de velocidad), un 5xx o un timeout no dicen nada sobre el DNI. En esos casos reintenta con espera creciente; la estrategia completa está en manejo de errores y reintentos al consumir APIs.
Y una aclaración que conviene dejar por escrito en tu sistema: validar que un DNI existe y obtener sus nombres no demuestra que la persona que lo escribió sea su titular. Para eso hacen falta mecanismos de verificación de identidad (presencial, biométrica, con el DNI electrónico), que son otro proceso. La validación que describe esta guía evita errores de datos; no sustituye un control de identidad.
En resumen
- Valida en dos pasos: formato local (gratis, inmediato) y existencia contra la fuente oficial (vía API, desde el backend).
- Un DNI son exactamente 8 dígitos; a diferencia del RUC, no tiene suma de control que puedas calcular localmente.
- Guarda el DNI siempre como texto: hay números que empiezan con cero y un campo entero los destruye.
- No corrijas en silencio: muestra el error y deja que el usuario lo arregle.
- Trata el 404 como información, no como fallo, y no lo caches por mucho tiempo.
- Usa los nombres que devuelve la fuente, no los que tipeó el usuario, y recuerda que validar no es verificar identidad.
Preguntas frecuentes
¿Puedo validar un DNI sin consultar a RENIEC?
Solo el formato. Puedes comprobar que son 8 dígitos, sin letras ni espacios, pero no puedes saber localmente si el número existe ni a quién pertenece. Para eso necesitas consultar la fuente oficial, directamente o mediante una API que lo haga por ti.
¿Qué hago si un DNI tiene menos de 8 dígitos?
Si viene de una base antigua donde se guardó como número, lo más probable es que perdió ceros a la izquierda; rellénalo hasta 8 caracteres y luego valida su existencia. Si lo está escribiendo un usuario en un formulario, no lo completes por él: muéstrale el error para que lo revise.
¿Cuánto cuesta validar un DNI con la API?
En endpoint.pe cada consulta con resultado cuesta un crédito, y las que terminan en 404 no se cobran. Cada cuenta tiene 150 consultas gratis al mes sin tarjeta, suficientes para integrar y probar; en la documentación está el detalle de cada respuesta.
¿Validar el DNI confirma que la persona es quien dice ser?
No. Confirma que el número existe y te entrega los nombres asociados. Que quien lo escribió sea realmente esa persona es una pregunta de verificación de identidad, que se resuelve con otros mecanismos. No mezcles las dos cosas en tu sistema ni en tu política de riesgo.






