Saltar al contenido principal

Catálogos — CUI/NIT, RTU y omisos

POST /catalogos/cui-nit
POST /catalogos/rtu
POST /catalogos/omisos

Tres consultas de contribuyente que comparten el mismo catálogo de errores y el mismo límite por minuto. Se habilitan por separado en tu integración: tener una activa no habilita las otras dos.

En esta página, «la fuente» es el servicio de datos que Tributax consulta por ti para resolver estos tres catálogos; la información se origina en los registros de SAT. Cuando decimos «la fuente no lo informó» queremos decir que el dato no vino en esa respuesta, sin distinguir si SAT no lo tiene o si no se publicó. No necesitas contratar nada con ese servicio ni conocerlo: tu contrato es con Tributax, y la forma de la respuesta no cambia si cambiamos de fuente.

Requisitos previos

  1. Credenciales de integración válidas. Estas tres rutas no usan x-user-nit: no se opera por cuenta de ningún contribuyente, se consulta un registro. Si tu cliente lo envía de todos modos, no cambia nada: estas rutas se resuelven antes de que exista la noción de contribuyente.
  2. La bandera de cada catálogo, activa en tu integración. La activa un administrador de Tributax; no es algo que puedas activar tú. Sin ella, 403.
  3. Saldo disponible en tu integración. Cada consulta que se resuelve bien descuenta de un saldo prepago. Si se agota, las tres consultas responden 402 hasta que se recargue — ver Saldo y cobro.
ConsultaEndpointQué resuelve
CUI/NITPOST /catalogos/cui-nitLa correspondencia entre el CUI y el NIT de un contribuyente
RTUPOST /catalogos/rtuEl registro del contribuyente en el Registro Tributario Unificado
OmisosPOST /catalogos/omisosSi el contribuyente está listado como omiso (declaraciones pendientes)

:::info No es el mismo endpoint que /rtu/nits POST /catalogos/rtu es una consulta distinta de RTU por NIT. Coinciden en el nombre, no en el contrato:

POST /rtu/nitsPOST /catalogos/rtu
Cuántos por peticiónhasta 50 NITuno
Identificadorsolo NITNIT o CUI
Forma de la respuestacomo la publica la fuentecontrato propio de Tributax
Clave de errorerrormessage
Habilitaciónis_rtu_enabledis_rtu_v2_enabled
Límite50 por minuto, propio30 por minuto, compartido con CUI/NIT y Omisos

Cuál usar: si necesitas resolver muchos NIT de una pasada, POST /rtu/nits. Si necesitas el registro completo de un contribuyente —régimen, afiliaciones, obligaciones— con una forma de respuesta estable, POST /catalogos/rtu.

Cuando pidas la habilitación, nombra la ruta exacta, no «RTU»: son dos banderas distintas y activan cosas distintas. El v2 de is_rtu_v2_enabled es un nombre interno heredado; no significa que exista una versión 2 de esta API, que no lleva versión. :::

Petición

curl -sS -X POST 'https://dev.api.tributax.app/catalogos/cui-nit' \
-H 'content-type: application/json' \
-H 'x-integracion-login: <NOMBRE_INTEGRACION>' \
-H 'x-integracion-token: <TOKEN_INTEGRACION>' \
-d '{"nit":"<NIT>"}'

Los tres catálogos usan las mismas credenciales y los mismos identificadores; solo cambia la URL de la tabla de arriba. date_of_birth es la única diferencia: solo lo lee /catalogos/rtu.

CampoTipo¿Obligatorio?Notas
nitstringuno de los dosNo vacío.
cuistringuno de los dosNo vacío.
actualizadobooleannofalse por defecto. Ver Datos al momento.
date_of_birthstring YYYY-MM-DDno, y solo en /catalogos/rtuFecha de nacimiento de la persona individual, o de constitución de la jurídica. La fuente la pide solo cuando no tiene copia del registro. Los otros dos catálogos la descartan. Tributax no valida su formato: la reenvía tal como llega.

Envía nit, cui, o ambos — con que uno llegue como texto no vacío basta. Si ninguno cumple: 400 {"message":"Envia un NIT o un CUI para consultar."}.

Si envías los dos, Tributax no comprueba que correspondan a la misma persona: los reenvía a la fuente tal como llegaron y es ella la que decide qué hacer. Envía uno solo si no te consta que sean del mismo contribuyente.

Datos al momento: actualizado

Por defecto la respuesta sale de una copia que mantiene la fuente. Con "actualizado": true la consulta va a la fuente en ese momento: el dato es el más reciente, pero la respuesta tarda sensiblemente más.

  • Úsalo cuando necesites confirmar un cambio reciente —un contribuyente que acaba de actualizar su RTU o de ponerse al día con SAT—, no como valor por defecto de tu integración.
  • Para saber qué tan vieja es la copia, mira fechaConsulta, que trae el momento que la fuente reporta para ese dato.
  • Toda consulta, con actualizado o sin él, tiene un tope de 30 segundos; pasado ese tiempo la respuesta es 502. Como con true la espera se acerca a ese tope, configura el timeout de tu cliente por encima de 30 segundos o vas a cortar la conexión antes que nosotros y no vas a ver el 502.
  • Envía un booleano JSON (true / false), no un texto: "true" entre comillas no es parte del contrato.

Respuestas

Los tres catálogos responden 200 con el registro dentro de data.

:::caution Un 200 no garantiza que haya datos Si la fuente responde que la consulta fue exitosa pero no trae ningún campo reconocible, la respuesta es un 200 con data presente y todos sus campos en null. No se distingue de un registro real cuyos atributos vinieran todos vacíos. Comprueba los campos que te importan en vez de deducirlos del status.

El 404, por su parte, significa que la fuente no reportó resultado para los datos enviados. :::

La forma de data es un contrato de Tributax, el mismo sin importar cómo publique sus datos la fuente, y sigue cuatro reglas:

  • Nombres en camelCase.
  • Todos los campos están siempre presentes. El que la fuente no informa sale null, nunca se omite. Una lista sin elementos sale [], nunca null.
  • La mayoría de los valores catalogados por SAT salen como { "codigo", "descripcion" }. Si SAT no informa el valor, los dos campos salen null y el objeto sigue ahí. codigo llega como número (1383) o como texto ("818") según el catálogo: normaliza con String(codigo) antes de comparar y no lo guardes en una columna numérica, porque los códigos con ceros a la izquierda se rompen. Hay excepciones a esta forma: actividadesEconomicas[] usa { ciiu, nombre, clasificacion }; marcas[] y caracteristicasEspeciales[] usan { codigo, nombre }; y marcas[].estado es un código numérico suelto, no un objeto. Genera tu modelo desde el esquema, no desde esta regla.
  • null no es false. Un booleano en null significa que la fuente no lo informó. En omisos en particular, no lo trates como «sin incumplimientos».

Un solo formato de fecha está garantizado por este contrato: fechaNacimiento, en YYYY-MM-DD — Tributax la convierte. Todas las demás, incluida fechaConsulta, se entregan tal como las publica la fuente: trátalas como texto, no las parsees ni las uses para calcular sin haber comprobado su formato en tu propia integración.

fechaConsulta te dice qué tan fresco es el dato, no cuándo llamaste: es el momento que la fuente reporta para ese dato. Con "actualizado": true es prácticamente el instante de tu petición; con el valor por defecto puede ser anterior. Hoy llega en ISO 8601 con zona UTC, y Guatemala es UTC−6 sin horario de verano: 2026-09-15T02:25:12Z es el 14 de septiembre en Guatemala, no el 15.

El esquema completo, campo por campo, está en la referencia de la API.

CUI/NIT

{
"data": {
"cui": "<CUI>",
"nit": "<NIT>",
"nombre": "<NOMBRES>, <APELLIDOS>",
"tipoPersona": "individual",
"fechaConsulta": "2026-09-15T15:06:28Z"
}
}
CampoTipoQué es
cuistring | nullCUI del contribuyente
nitstring | nullNIT del contribuyente
nombrestring | nullNombre completo en un solo campo, como lo registra la fuente. No lo partas para deducir nombres y apellidos: la puntuación la pone la fuente y no es parte de este contrato. Si los necesitas separados, usa RTU
tipoPersona"individual" | "juridica" | nullnull si la fuente no lo informa o informa un valor que no se reconoce: en ese caso no deduzcas ninguno de los dos
fechaConsultastring | nullMomento que la fuente reporta para el dato, con el formato que ella publica

Omisos

{
"data": {
"nit": "<NIT>",
"nombre": "<NOMBRES>, <APELLIDOS>",
"nombreComercial": null,
"cui": "<CUI>",
"versionDpi": "002",
"estadoContribuyente": null,
"tipoAfiliacion": null,
"regimen": null,
"fechaAfiliacion": null,
"domicilioFiscal": {
"departamento": null,
"municipio": null,
"direccion": null,
"telefono": null
},
"establecimientos": [],
"tieneIncumplimientos": true,
"tieneProcesoCoactivo": false,
"incumplimientos": ["DECLARACIONES OMITIDAS"],
"fechaConsulta": "2026-09-15T02:25:12Z"
}
}
CampoTipoQué es
tieneIncumplimientosboolean | nullSi SAT registra obligaciones sin presentar. Es el motivo de la consulta.
tieneProcesoCoactivoboolean | nullSi SAT registra un proceso de cobro coactivo
incumplimientosstring[]Detalle, como texto de SAT. [] cuando no hay
nit, nombre, nombreComercialstring | nullIdentificación del contribuyente. nombre viene en un solo campo; no lo partas por la coma
cuistring | nullTal como lo publica la fuente: puede venir con espacios entre grupos de dígitos. El cui de CUI/NIT no trae esa advertencia, así que si vas a cruzar los dos, quita los espacios de ambos lados antes de comparar
versionDpi, estadoContribuyente, tipoAfiliacion, regimenstring | nullDatos de afiliación, como texto de SAT
fechaAfiliacionstring | nullComo la publica SAT
domicilioFiscalobjetodepartamento, municipio, direccion, telefono; cada uno string | null. El objeto siempre está
establecimientosarraySin normalizar, ver Campos sin forma fija
fechaConsultastring | nullMomento que la fuente reporta para el dato, con el formato que ella publica

RTU

El ejemplo está recortado a lo representativo; la respuesta real trae todos los campos del esquema, con null donde no hay dato.

{
"data": {
"nit": "<NIT>",
"tipoContribuyente": { "codigo": "1", "descripcion": "PERSONA/NEGOCIO" },
"persona": {
"primerNombre": "<PRIMER_NOMBRE>",
"primerApellido": "<PRIMER_APELLIDO>",
"dpi": "<CUI>",
"fechaNacimiento": "1990-01-31",
"genero": { "codigo": 1383, "descripcion": "MASCULINO" },
"sectorEconomico": {
"codigo": 750,
"descripcion": "SERVICIOS",
"estado": { "codigo": null, "descripcion": null },
"fechaInicio": null
},
"actividadesEconomicas": [
{ "ciiu": "8411.40", "nombre": "ADMINISTRACION PUBLICA", "clasificacion": null }
]
},
"empresa": null,
"representantes": [],
"afiliaciones": {
"isr": null,
"iva": {
"impuesto": { "codigo": "11", "descripcion": "IMPUESTO AL VALOR AGREGADO" },
"regimen": { "codigo": "818", "descripcion": "PEQUEÑO CONTRIBUYENTE" },
"exento": false,
"obligaciones": [
{
"nombre": "IVA PEQUEÑO CONTRIBUYENTE",
"generaEtiquetaOmiso": true,
"estado": { "codigo": null, "descripcion": null }
}
]
}
},
"caracteristicasEspeciales": [
{
"codigo": 2432,
"nombre": "EMISOR DE FACTURA ELECTRÓNICA",
"estado": { "codigo": null, "descripcion": null }
}
],
"fechaUltimaActualizacion": null,
"fechaConsulta": "2026-09-15T15:19:56Z"
}
}
CampoTipoQué es
nitstring | nullNIT del contribuyente
tipoContribuyentecódigo SATTipo de contribuyente, según SAT
personaobjeto | nullViene lleno para personas individuales. Nombres y apellidos por separado, dpi, serieDpi, versionDpi, fechaNacimiento (YYYY-MM-DD), fechaVencimientoDpi, genero, estadoCivil, nacionalidad, tipoDocumento, estado, sectorEconomico, actividadesEconomicas[], marcas[], participacionGremial, participacionEmpresarial
empresaobjeto | nullViene lleno para personas jurídicas. Sin normalizar, ver Campos sin forma fija
representantesarraySin normalizar, ver Campos sin forma fija
afiliaciones.isr, afiliaciones.ivaobjeto | nullnull si el contribuyente no está afiliado a ese impuesto. Cada uno trae impuesto, regimen, tipoContribuyente, periodoImpositivo, formaCalculo, estatusAfiliacion, tipoRenta (solo ISR), tipoEstablecimiento (solo IVA), exento, fechaDesde y obligaciones[]
afiliaciones.*.regimencódigo SATEl régimen, con el mismo nombre en ISR y en IVA
afiliaciones.*.obligaciones[]arraynombre, periodo, formulario, generaEtiquetaOmiso (si incumplirla deja al contribuyente marcado como omiso), requerida, estado
caracteristicasEspecialesarrayPor ejemplo, emisor de factura electrónica: codigo, nombre, estado, fechaEstado, fechaDesde, fechaHasta
fechaUltimaActualizacionstring | nullÚltima actualización o ratificación de los datos ante SAT, como la publica SAT
fechaConsultastring | nullMomento que la fuente reporta para el dato, con el formato que ella publica

Dos campos de RTU se salen de la forma general: marcas[].estado es un código numérico suelto, no un objeto { codigo, descripcion } como los demás estado; y actividadesEconomicas[] usa { ciiu, nombre, clasificacion }.

Persona individual o jurídica

persona viene lleno para individuales y empresa para jurídicas. Pero la regla general de este contrato es que un campo que la fuente no informa sale null, así que persona: null no prueba por sí solo que sea jurídica: puede ser que el bloque no haya venido. Si tu lógica depende de la distinción, exige que el bloque contrario venga lleno (persona === null && empresa !== null ⇒ jurídica) y trata «los dos en null» como no determinado, no como un valor por defecto.

Campos sin forma fija

empresa y representantes en RTU, y establecimientos en omisos, se entregan tal como los publica la fuente, sin normalizar. Quedan fuera de las garantías del resto de este contrato: su forma todavía no está fijada y puede cambiar sin anuncio previo. Mientras tanto, guárdalos como JSON opaco en vez de mapearlos a columnas tipadas, y no ramifiques tu lógica de negocio por sus campos. Si necesitas un dato estable de ahí, escríbenos desde Contacto y lo incorporamos al contrato normalizado.

:::caution El sobre es distinto al de RENAP y RTU por NIT Estos tres catálogos responden {"data": …} y sus errores con la clave message, como el resto de la API. RENAP y RTU por NIT responden sus errores con la clave error y traen además success y responseCode dentro del cuerpo. Acá no existe responseCode: el status HTTP es tu única señal de éxito. Si reusas el parseo de esas dos consultas, vas a leer claves que no están. :::

Errores

SituaciónStatusCuerpo
Ni nit ni cui llegaron utilizables400{"message":"Envia un NIT o un CUI para consultar."}
La fuente rechazó los datos de la consulta400{"message":"Los datos de la consulta no son validos."}
Las credenciales de integración no corresponden a ninguna integración401{"message":"Integración no encontrada con estas credenciales."}
La fuente rechazó la autenticación de la consulta401{"message":"No fue posible autenticar la consulta. Escribe a soporte."}
El saldo de tu integración se agotó402{"message":"Tu integracion no tiene saldo disponible. Recarga para seguir consultando."}
El servicio de consultas no tiene saldo disponible402{"message":"El servicio de consultas no tiene saldo disponible. Escribe a soporte."}
Sin credenciales de integración403{"message":"Se requieren credenciales de integracion."}
Este catálogo no está habilitado en tu integración403{"message":"Esta consulta no esta habilitada para tu integracion."}
La fuente niega el acceso a esta consulta403{"message":"Esta consulta no esta habilitada. Escribe a soporte."}
No hay registro para los datos enviados404{"message":"No encontramos informacion para los datos enviados."}
Más de 30 consultas por minuto en tu integración429{"message":"Demasiadas consultas seguidas. Intenta en un minuto."}
La fuente está limitando las consultas429{"message":"Demasiadas consultas seguidas. Intenta de nuevo en unos minutos."}
Fallo no controlado de la API500El mensaje varía; no lo uses como condición
La fuente no respondió502{"message":"El servicio de consultas no esta disponible en este momento."}

Los mensajes van sin tildes tal como los emite la API — no es un error de esta página, cópialos tal cual si los vas a mostrar.

Ramifica tu código por el status, no por el texto. El texto de un mensaje puede cambiar sin aviso previo (Versionado). En los tres pares de abajo el mensaje es hoy el único indicio de cuál de las dos causas ocurrió: úsalo para decidir a quién escribir o qué registrar en tu log, no como la condición que dispara un reintento automático.

  • Dos 401, con causas opuestas. Si el mensaje habla de la integración, el problema es tuyo: revisa tus credenciales y el entorno contra el que estás pegando. También sale así si envías solo uno de los dos headers. Si dice "No fue posible autenticar la consulta", tus credenciales están bien y falló la autenticación contra la fuente: escribe a soporte, no rotes nada.
  • Dos 402 de "sin saldo". Uno es el saldo prepago de tu integración, que se recarga escribiendo a soporte; el otro es del servicio de consultas, del lado de Tributax. En los dos casos la consulta no se ejecutó y no se te cobró.
  • Dos 403 de "no habilitada". Uno es de Tributax ("...para tu integracion." — tu integración no tiene la bandera de este catálogo) y otro es de la fuente ("...Escribe a soporte." — la fuente le negó el acceso a la consulta). El primero lo resuelve un administrador de Tributax activando tu bandera; el segundo requiere escribir a soporte.
  • Dos 429 de "demasiadas consultas". Uno es el límite de Tributax (30 por minuto, "Intenta en un minuto") y otro es el límite propio de la fuente ("Intenta de nuevo en unos minutos"). Ninguno trae Retry-After.

Qué hacer ante un 502

Un 502 quiere decir que la fuente no respondió o falló de su lado. Tu consulta no se resolvió, no se te cobró, y reintentar es seguro: no duplica nada.

  • Reintenta con retroceso exponencial, igual que con un 429 (Entornos y límites).
  • Cada intento gasta del límite de 30 por minuto compartido entre los tres catálogos, y del global de 100 por 15 minutos. Tres reintentos seguidos consumen cuatro de esos 30.
  • El tope antes del 502 es de 30 segundos. Pon el timeout de tu cliente por encima de ese valor, sobre todo si envías "actualizado": true.

Un 404 es distinto: ahí la fuente sí respondió. Cubre tanto que no haya registro para los datos enviados como que la fuente haya marcado la consulta como fallida, y los dos casos comparten status y mensaje, así que no se distinguen desde tu lado. Tratalo como «no tengo el dato ahora», no como «este contribuyente no existe»: si el identificador te consta bueno, reintenta más tarde antes de darlo por inexistente. Los datos rechazados por la fuente, en cambio, son 400.

Límite: 30 por minuto, compartido entre los tres

Los tres catálogos gastan del mismo contador por integración — no hay 30 para CUI/NIT más 30 para RTU más 30 para Omisos. Agotar el minuto consultando cui-nit deja sin cupo a rtu y a omisos hasta que la ventana se reinicie.

Este límite se suma al global de 100 por 15 minutos; ambos aplican a la vez.

La ventana se reinicia a más tardar 60 s después de la primera consulta del minuto. No hay cabecera Retry-After. El contador vive en memoria del proceso: el límite nominal no es una garantía de capacidad.

Al excederlo: 429 {"message":"Demasiadas consultas seguidas. Intenta en un minuto."}.

En estas rutas puedes recibir tres 429 distintos: el de los catálogos, el de la fuente y el global por IP, que responde con el cuerpo por defecto del limitador y no necesariamente es JSON. Comprueba el Content-Type antes de parsear.

Saldo y cobro

Cada consulta que se resuelve bien descuenta de un saldo prepago por integración, según la tarifa vigente de tu integración.

  • Solo se cobran las consultas que se resolvieron bien. Un 502, un 429, un 404 o un error de la fuente no descuentan saldo.
  • Si el saldo llega a cero, las tres consultas responden 402 con {"message":"Tu integracion no tiene saldo disponible. Recarga para seguir consultando."}, y la consulta ni siquiera llega a la fuente.
  • No hay endpoint para consultar tu saldo ni para recargarlo. Lo administra Tributax: escríbenos desde Contacto para recargar, o para acordar un aviso antes de que se agote.

Cada consulta queda registrada

De cada consulta que llega a la fuente se registra la integración, el catálogo consultado, el status, el tiempo de respuesta y el identificador que enviaste (NIT o CUI) — incluidas las que la fuente responde con error. Sirve para auditoría.

No queda registro de los rechazos anteriores a la consulta: 400 sin NIT ni CUI, credenciales ausentes o inválidas, catálogo no habilitado, saldo agotado y el límite de 30 por minuto.

Para habilitar cualquiera de estos tres catálogos en tu integración, escríbenos desde Contacto.

📄 Estado de verificación: leído del código del backend. No se ha ejercido contra el entorno desplegado en esta entrega.