- Blog
- SUNAT
Cómo validar un RUC con el dígito verificador (JavaScript y Python)
Aprende a validar un RUC antes de consultarlo: el algoritmo del dígito verificador paso a paso, con código en JavaScript y Python, y por qué no basta con eso.
Muchos de los RUC que llegan a un formulario vienen con errores de tipeo: un dígito de más, dos cifras intercambiadas, un número copiado a medias de una factura. Si envías cada uno de esos números a consultar, gastas tiempo, créditos y paciencia del usuario para recibir un "no encontrado".
La buena noticia es que el RUC trae su propio mecanismo de control: el último dígito se calcula a partir de los diez anteriores. En este artículo vas a ver cómo funciona ese cálculo, cómo implementarlo en JavaScript y Python, y por qué validar el formato es solo la mitad del trabajo.
Qué es el dígito verificador
El RUC tiene 11 dígitos. Los dos primeros indican el tipo de contribuyente (10 para persona natural, 20 para persona jurídica, 15 y 17 para casos especiales), los ocho siguientes identifican al contribuyente y el undécimo es el dígito verificador. Si quieres el detalle de cada parte, lee primero qué es el RUC y cómo está compuesto.
El verificador no aporta información nueva. Es un número derivado de los otros diez mediante un cálculo de módulo 11, el mismo principio que usan los códigos de barras, el ISBN y muchos números de cuenta. Si cambias cualquiera de los diez primeros dígitos, el verificador esperado cambia y deja de coincidir con el que te dieron.
Eso lo convierte en un filtro barato y muy efectivo. Detecta la gran mayoría de errores de un solo dígito, detecta la transposición de dos dígitos adyacentes y lo hace sin red, sin base de datos y en microsegundos.
El algoritmo paso a paso
El procedimiento es el siguiente:
- Toma los diez primeros dígitos del RUC.
- Multiplica cada uno por su peso correspondiente. Los pesos, en orden, son 5, 4, 3, 2, 7, 6, 5, 4, 3, 2.
- Suma los diez productos.
- Calcula el residuo de dividir esa suma entre 11.
- Resta ese residuo de 11.
- Si el resultado es 10, el dígito verificador es 0. Si es 11, es 1. En cualquier otro caso, el dígito verificador es el propio resultado.
El paso 6 es el que más se olvida. Cuando el residuo es 1, la resta da 10, y un dígito no puede ser 10, así que se usa 0. Cuando el residuo es 0, la resta da 11, y se usa 1. Si tu implementación no contempla estos dos casos, va a rechazar RUC perfectamente válidos.
Por último, compara el dígito calculado con el dígito 11 del RUC que te dieron. Si coinciden, el formato es válido.
Ejemplo con un RUC real
Tomemos el RUC de SUNAT, 20131312955, que es público por tratarse de una entidad del Estado. Los diez primeros dígitos son 2, 0, 1, 3, 1, 3, 1, 2, 9 y 5.
| Dígito | Peso | Producto |
|---|---|---|
| 2 | 5 | 10 |
| 0 | 4 | 0 |
| 1 | 3 | 3 |
| 3 | 2 | 6 |
| 1 | 7 | 7 |
| 3 | 6 | 18 |
| 1 | 5 | 5 |
| 2 | 4 | 8 |
| 9 | 3 | 27 |
| 5 | 2 | 10 |
La suma es 94. El residuo de 94 entre 11 es 6, porque 11 por 8 es 88 y 94 menos 88 da 6. Restamos: 11 menos 6 es 5. Y el último dígito del RUC es, en efecto, 5. El formato es válido.
Ahora prueba con un error de tipeo: 20131312956. Los diez primeros dígitos son los mismos, así que el dígito calculado sigue siendo 5, pero el que nos dieron es 6. No coinciden, y el número se rechaza sin consultar nada.
Código en JavaScript
Esta función sirve en el navegador y en Node.js. Recibe una cadena, limpia espacios, comprueba el patrón y aplica el algoritmo:
function rucValido(valor) {
const ruc = String(valor).trim();
if (!/^(10|15|17|20)\d{9}$/.test(ruc)) return false;
const pesos = [5, 4, 3, 2, 7, 6, 5, 4, 3, 2];
let suma = 0;
for (let i = 0; i < 10; i++) {
suma += Number(ruc[i]) * pesos[i];
}
const resto = 11 - (suma % 11);
const digito = resto === 10 ? 0 : resto === 11 ? 1 : resto;
return digito === Number(ruc[10]);
}
rucValido('20131312955'); // true
rucValido('20131312956'); // false
rucValido('2013131295'); // false, solo 10 dígitos
Dos detalles de diseño. Primero, la función recibe una cadena y no un número. Un RUC nunca empieza en cero, pero guardarlo como entero es una mala costumbre que luego se traslada al DNI, donde los ceros a la izquierda sí importan. Segundo, la expresión regular limita los prefijos a los que SUNAT utiliza; si en el futuro apareciera otro, bastaría con añadirlo ahí.
Código en Python
La misma lógica en Python, lista para usar en Django, FastAPI o un script de limpieza de datos:
import re
PESOS = [5, 4, 3, 2, 7, 6, 5, 4, 3, 2]
def ruc_valido(valor: str) -> bool:
ruc = str(valor).strip()
if not re.fullmatch(r"(10|15|17|20)\d{9}", ruc):
return False
suma = sum(int(d) * p for d, p in zip(ruc[:10], PESOS))
resto = 11 - (suma % 11)
digito = 0 if resto == 10 else 1 if resto == 11 else resto
return digito == int(ruc[10])
print(ruc_valido("20131312955")) # True
print(ruc_valido("20131312956")) # False
Si procesas un archivo con miles de RUC de proveedores, pasa cada fila por esta función antes de cualquier otra cosa. Los que fallen van a una lista de "revisar a mano" y los que pasen siguen al siguiente paso.
Formato válido no es RUC existente
Aquí está la trampa. El dígito verificador confirma que el número está bien formado, nada más. Hay muchísimos números de 11 dígitos con prefijo válido y verificador correcto que nunca han sido asignados a nadie. Y hay RUC bien formados que existen, pero cuyo titular está dado de baja o figura como no habido.
Un RUC que pasa el algoritmo puede ser:
- Un número que nadie tiene asignado.
- Un contribuyente que existió y hoy está en BAJA DEFINITIVA o BAJA DE OFICIO.
- Un contribuyente ACTIVO pero con domicilio NO HABIDO.
- Un RUC real que pertenece a otra empresa, no a la que tu usuario dice ser.
Ninguno de esos casos lo detecta el cálculo local. Para saber si un RUC existe y en qué situación está, hay que consultar el padrón de SUNAT. Si no tienes claro qué significan esos valores, revisa qué significan ACTIVO y HABIDO.
Por eso la validación correcta tiene dos pasos:
- Formato, en local. Longitud, prefijo y dígito verificador. Se hace en el navegador o en el backend, es instantáneo y no cuesta nada. Filtra los errores de tipeo.
- Existencia y estado, contra SUNAT. Solo para los RUC que pasaron el paso 1. Devuelve razón social, estado, condición y dirección, y es lo que te permite completar el comprobante con datos exactos.
El segundo paso se hace con una llamada a la API de consulta RUC:
curl https://api.endpoint.pe/v1/ruc/20131312955 \
-H "Authorization: Bearer TU_TOKEN"
Si el RUC no existe, la respuesta es un HTTP 404 con un mensaje claro, y esa consulta no se cobra. Así el costo de preguntar por un RUC bien formado pero inexistente es cero, y el costo de preguntar por uno mal formado también, porque ni siquiera llegaste a preguntar.
La validación de formato en el frontend mejora la experiencia, pero cualquiera puede saltársela. Repite el cálculo en el backend y haz la consulta a SUNAT siempre desde el servidor, donde vive tu token.
Tienes ejemplos completos de este flujo en cómo consultar un RUC con Node.js y en validar RUC y DNI en frontend y backend.
En resumen
- El undécimo dígito del RUC se calcula a partir de los diez anteriores con pesos 5, 4, 3, 2, 7, 6, 5, 4, 3, 2 y módulo 11.
- Si 11 menos el residuo da 10, el dígito es 0; si da 11, es 1.
- Implementarlo son diez líneas en JavaScript o Python, sin dependencias.
- Un formato válido solo descarta errores de tipeo: no prueba que el RUC exista ni que esté activo.
- Valida en dos pasos: formato en local y existencia contra el padrón de SUNAT.
- Las consultas sin resultado no se cobran, así que el segundo paso no castiga los RUC inexistentes.
Preguntas frecuentes
¿Por qué los pesos son 5, 4, 3, 2, 7, 6, 5, 4, 3, 2?
Es la secuencia que define el algoritmo de módulo 11 que usa el RUC. Los pesos se aplican de izquierda a derecha sobre los diez primeros dígitos. No hace falta entender por qué son esos y no otros: basta con aplicarlos en ese orden.
¿Qué pasa con los RUC que empiezan en 15 o 17?
Se validan con el mismo algoritmo. Son casos especiales que SUNAT asigna a personas naturales en situaciones particulares; tu expresión regular debe aceptarlos junto con 10 y 20 para no rechazar números legítimos.
¿Puedo generar RUC válidos con este algoritmo?
Puedes generar números que pasan el cálculo, sí, y eso es justamente la prueba de que el formato válido no significa nada por sí solo. Un RUC generado no está inscrito en SUNAT y una consulta al padrón lo devolverá como no encontrado.
¿Debo validar el formato si de todas formas voy a consultar la API?
Sí. Es gratis, instantáneo y evita llamadas inútiles. Además, te permite mostrar al usuario un error inmediato ("revisa el RUC, parece mal escrito") en lugar de hacerlo esperar por una respuesta de la red.






