Las trampas de los datos abiertos peruanos
Una fuente pública no falla con un error: falla devolviendo menos, y menos se parece muchísimo a nada. Estas son 20 trampas medidas trabajando con fuentes abiertas peruanas, agrupadas por modo de fallo y con la corrección concreta de cada una.
- Por qué una fuente pública falla distinto
- Familia 1: el éxito falso
- Familia 2: el campo que dice otra cosa
- Familia 3: la llave que no está donde crees
- Familia 4: el archivo truncado que parece entero
- Familia 5: el valor válido que no es cierto
- Familia 6: la fuente que no es determinista
- Cómo se construye un conector que no miente
- Cómo verificar tu propia verificación
- Lo que sí funciona
Por qué una fuente pública falla distinto
Quien viene de consumir APIs comerciales llega con un modelo mental que aquí no funciona. En una API comercial, cuando algo sale mal, te lo dicen: un 404, un 429, un mensaje de error. El contrato entre las partes está escrito y alguien cobra por cumplirlo.
Una fuente pública no tiene ese contrato. No falla con un error: falla devolviendo menos. Y menos se parece muchísimo a nada, que a su vez se parece muchísimo a "no hay nada que reportar". Ahí está el problema entero.
Cada una se encontró trabajando con una fuente pública peruana, se midió y quedó cableada en un conector. Ninguna es hipotética.
Este documento las agrupa por modo de fallo y no por fuente, a propósito. Si trabajas con otras fuentes, los nombres de este texto no te van a servir; los patrones sí.
La pregunta útil no es si la fuente respondió. Es si lo que respondió significa lo que crees que significa.
Familia 1: el éxito falso
Es la más peligrosa porque produce el peor resultado disfrazado del mejor: un estado limpio que en realidad es un estado desconocido.
HTTP 200 con todas las listas vacías
La fuente responde con código de éxito, un mensaje de "procesamiento completado" y todos los arreglos vacíos aunque el identificador no exista. Un dígito mal tecleado devolvía, literalmente, "sin sanción vigente en la fuente oficial".
200 con cero bytes de cuerpo
Un endpoint enlazado desde el portal oficial devolvía 200 con el cuerpo
completamente vacío en tres combinaciones distintas de parámetros. No hay error, no hay
redirección, no hay mensaje.
Cambia el content-type a JSON bajo un 200
Cuando el conjunto de datos no está disponible, el servidor responde con éxito pero cambia
el tipo de contenido: donde debía llegar una hoja de cálculo llega un JSON de error. Quien guarde
el cuerpo en un archivo .xlsx obtiene un archivo corrupto sin enterarse.
Content-Type Y la firma binaria del archivo. Una hoja de cálculo
moderna empieza con PK; si no empieza así, no es lo que pediste.302 que hay que seguir, o te llevas HTML
El recurso responde con una redirección. Sin seguirla, lo que llega son 743 bytes de HTML con aspecto de página válida en lugar del archivo de datos.
Familia 2: el campo que dice otra cosa
La columna existe, tiene datos, pasa cualquier validación de tipo, y significa algo distinto de lo que su nombre promete. Es la familia que más silenciosamente contamina un análisis.
«Other Name» no es un alias
La columna se llama Other Name y cualquiera asume que trae nombres alternativos de la empresa. En 1 032 registros solo tiene cuatro valores distintos: nulo en 849 casos, y en el resto el nombre del órgano que impuso la sanción.
Indexarla como alias metía cadenas como "Sanctions Committee" dentro del comparador de razones sociales de empresas.
El sufijo del archivo no es una partición
Los archivos se llaman REPORTE_..._1 hasta REPORTE_..._10 y la
lectura natural es que son partes de un mismo conjunto. No lo son: cada número es un
corte temporal completo. Sumarlos habría contado la misma deuda diez veces.
Una columna que mezcla dos taxonomías
El campo que parece el departamento trae, según el nivel de gobierno de la fila, un departamento (Lima, Áncash) o un sector (Salud, Educación). Agrupar por él produce un ranking donde Salud compite con Cusco.
El guion es ausencia de dato, no un valor
El código de ubicación geográfica llega como - en 30 218 registros. Guardado
tal cual, convierte treinta mil ausencias en treinta mil valores con formato inválido, y
cualquier validación posterior los reporta como errores de datos que no lo son.
Un archivo entero que no está en vigor
La fuente publica varios archivos y uno de ellos son sanciones diferidas: existen, están inscritas y todavía no rigen. No traen ventana de vigencia porque aún no la tienen. Mezclarlas con las vigentes es afirmar que alguien está inhabilitado cuando no lo está.
Familia 3: la llave que no está donde crees
Trampas de acceso. La fuente tiene el dato y te deja fuera por una diferencia de un carácter.
El identificador del conjunto lleva tildes
Consultar el catálogo por padron-ruc-...administracion-tributaria devuelve
404. El identificador real es padrón-ruc-...administración-tributaria,
con tildes, y hay que codificarlo en la URL.
Las cabeceras del archivo también llevan tildes
Las columnas se llaman RAZÓN SOCIAL, CONDICIÓN DE DOMICILIO, NOMBRE DE VÍA, NÚMERO. Un detector de columnas que compare sin normalizar deja esas columnas en nulo sin avisar, y el resto del proceso corre feliz sobre campos vacíos.
Los identificadores del CDN rotan en cada actualización
Los enlaces de descarga incluyen un identificador numérico que cambia cada vez que la entidad publica una versión nueva. Cualquier enlace guardado deja de funcionar sin previo aviso.
El nombre del archivo no sigue su propio patrón
Los cortes se llaman PadronRUC_AAAAMM.zip, salvo uno que se llama
PadronRUC.zip a secas. Y existe un PadronRUC_209901.zip, un corte del
año 9901 que no es un corte de nada.
Familia 4: el archivo truncado que parece entero
Esta es la que más caro cuesta, porque contamina todo lo que venga después y no deja rastro.
El archivo resultante conservaba la firma correcta en sus primeros bytes, así que cualquier validación que mire solo la cabecera lo aprueba.
La descarga se corta sin lanzar excepción
Una descarga de 258 832 704 bytes terminó en 4 947 651. La lectura devolvió vacío
como si el archivo hubiera terminado: sin excepción, sin timeout, sin aviso. El
archivo resultante conservaba la firma PK correcta en sus primeros bytes, así que
cualquier validación que mire la cabecera lo aprueba.
La consecuencia se descubrió meses después: dos conteos del mismo archivo daban 13 034 500 y 13 364 805 filas. No eran dos lecturas del mismo archivo, eran dos descargas distintas y una estaba incompleta.
Content-Length, y reanudar con
peticiones de rango si el servidor las admite. Una firma correcta al inicio no prueba nada sobre
el final.Una fila con un campo mayor al límite del lector
Una sola fila traía un campo por encima del límite de 128 KB que el lector de CSV admite por defecto, y eso abortaba la pasada entera sobre dieciocho millones de filas.
Familia 5: el valor válido que no es cierto
Aquí no hay nada roto. El dato tiene el tipo correcto, está dentro de rango, pasa toda validación imaginable, y contradice la realidad.
El cero que no significa cero
El número de trabajadores declarado permite construir una señal atractiva: empresas de capacidad mínima que ganan contratos millonarios. Al medirla aparecían 1 449 empresas con uno o cero trabajadores y más de ocho mil millones adjudicados.
Entre ellas, una cadena de supermercados con cientos de locales, declarando cero trabajadores y con S/ 362 483 759 adjudicados. El cero no significa "sin planilla": significa, muchas veces, "no informado".
La fecha de actualización del catálogo miente
Un conjunto declaraba metadata_modified de junio de 2026. El corte real de los
datos era abril de 2024. Dos años de diferencia entre lo que dice el catálogo y lo que contiene
el archivo.
El padrón no es el universo
De 95 475 identificadores que contratan con el Estado, solo 74 767 figuran en el padrón. Los ausentes incluyen empresas grandes, vivas y verificables una por una contra otra fuente.
Cualquier señal construida sobre "no figura en el padrón" habría marcado a miles de empresas perfectamente activas.
Identificadores que no son identificadores
Nuestros propios agregados publicaban 103 códigos de prefijo 99, comodines que
el sistema de contrataciones usa para proveedor no identificado, con S/ 1 948 461 690 atribuidos.
Encabezados por uno formado solo por nueves.
Familia 6: la fuente que no es determinista
El supuesto que casi nadie cuestiona: que consultar dos veces lo mismo devuelve lo mismo.
Doce llamadas consecutivas al mismo identificador, que sí tiene el dato registrado. Dos devolvieron el bloque vacío bajo un HTTP 200 correcto.
Dos consultas idénticas, dos respuestas distintas
Doce llamadas consecutivas al mismo identificador, que tiene un impedimento vigente registrado. Diez devolvieron el bloque completo. Dos lo devolvieron vacío, con el mismo código de éxito y sin diferencia visible en la respuesta.
El efecto es que una de cada seis consultas subreporta, y no hay forma de saber cuál.
Esto rompe algo más profundo que una consulta: rompe la reproducibilidad. Si tu análisis corrió una vez y dio un resultado, no puedes afirmar que volverá a darlo. Y si publicas una cifra que no se puede reproducir, publicaste una anécdota.
- Reintentar ante ausencia de bloque, no solo ante error HTTP. Un reintento llevó nuestras corridas correctas de diez sobre doce a seis sobre seis.
- Declarar lo que quedó sin verificar en la propia respuesta, con un campo explícito.
- Guardar el hash del archivo con el que se calculó cada cifra publicada. Es lo único que permite que un tercero llegue al mismo número.
Cómo se construye un conector que no miente
Todo lo anterior se resume en una lista corta. No es teoría: cada punto existe porque algo falló antes.
- Listar del catálogo, nunca construir la URL. Los nombres no siguen su propio patrón.
- Seguir redirecciones y validar en el destino final.
- Validar el content-type y la firma binaria, no solo el código HTTP.
- Comparar bytes recibidos contra Content-Length y reanudar si falta.
- Normalizar tildes en ambos lados de cualquier comparación de nombres.
- Fallar ruidosamente si falta una columna esperada. Nunca dejarla en nulo y seguir.
- Contar los valores distintos de una columna antes de usarla como llave.
- Normalizar marcadores de ausencia en la ingesta.
- Guardar hash, fecha de captura y URL de cada archivo que sostiene una cifra.
- Auditar los derivados propios con las mismas reglas que la fuente ajena.
Dos capas de auditoría, no una
Conviene separar dos preguntas que se confunden. Una es si el valor respeta su dominio: tipo, longitud, rango, juego de caracteres. La otra es si el conjunto respeta las reglas del negocio: que no haya una obra paralizada en quien no ejecuta ninguna, que un total por acreedor cuadre con el total por deudor.
Son preguntas distintas y fallan por motivos distintos. Un campo puede pasar todas las reglas de negocio y estar fuera de su dominio, y al revés.
Cómo verificar tu propia verificación
Este capítulo es el que más incomoda escribir, y es el más útil.
En este proyecto pusimos un control que aborta la publicación si queda algún marcador de plantilla sin sustituir en el texto. Funcionó: cazó dos. Semanas después se publicó un documento con un marcador impreso en crudo, en medio de un párrafo.
Pero lo peor vino después. Al verificar el documento publicado, la comprobación reutilizó el mismo patrón. Es decir: se comprobó con la misma regla que estaba mal, y la verificación confirmó el error en lugar de encontrarlo.
Una comprobación posterior no vale nada si reutiliza la lógica que está comprobando.
- Verifica con una implementación distinta de la que produce el resultado. Idealmente, mirando el producto final y no el código que lo generó.
- Prueba tu control contra un caso que sepas que debe fallar. Un control que nunca ha dicho que no, no sabes si funciona.
- Desconfía de los controles que siempre pasan. En datos públicos, algo siempre está mal; si tu auditoría nunca encuentra nada, está mirando el lugar equivocado.
Lo que sí funciona
Sería injusto cerrar solo con trampas. De todo lo que probamos, esto es lo que resultó sólido y merece decirse.
Los conjuntos de datos publicados como descarga directa, con archivo versionado y fecha, se comportan bien: se pueden verificar por hash, se pueden reproducir y no dependen de que un servicio esté vivo. Las fichas consultadas en tiempo real dan el dato más fresco y son la única forma responsable de afirmar algo sobre una empresa concreta.
Y una observación que suele sorprender: varias entidades resolvieron internamente la interoperabilidad que el dato abierto no ofrece. Un mismo organismo publica un archivo masivo enmascarado, sin identificadores cruzables, y a la vez expone una consulta individual que devuelve todo el grafo sin enmascarar. Lo que no se puede hacer en bloque a veces sí se puede hacer de a uno.
Nada de esto convierte a los datos abiertos peruanos en un mal recurso. Son un recurso enorme, gratuito y muchas veces mejor que el de países más ricos. Solo hay que leerlos sabiendo que no fueron construidos para ser leídos por una máquina, y actuar en consecuencia.
Todas las cifras de este documento se midieron trabajando con fuentes públicas peruanas entre julio y agosto de 2026. Cada trampa está cableada en un conector y documentada con su corrección. Elaboración de Cautela.
Comparte la guía