Validador de RFC

Facturación por Jonathan Fonseca
100%nivel de servicio (30 d)
1,784 mslatencia promedio
2,817llamadas (30 d)

Verifica ante el SAT si un RFC está registrado y puede recibir facturas, y coteja nombre y código postal.

Empieza a consumir

Todas las peticiones van al gateway de idoo.dev con tu API key:

curl "https://idoo.dev/v1/valida-rfc/{ruta}" \ -H "Authorization: Bearer {tu_api_key}"
Docs para IA (.md)
Pégalo en Claude Code, Cursor, Lovable, Copilot o el asistente que uses: lee la documentación e integra la API por ti.
Planes
Gratis
Gratis
  • 50 llamadas/mes
  • 2 peticiones/min
Inicial
$299.00/mes
  • 1,000 llamadas/mes
  • 30 peticiones/min
Negocio
$999.00/mes
  • 5,000 llamadas/mes
  • 60 peticiones/min
Documentación

Validador de RFC

API para verificar ante el SAT si un RFC está registrado y es susceptible de recibir facturas, y para cotejar que el nombre o razón social y el código postal coincidan con lo que el SAT tiene registrado. Cada consulta se hace en vivo contra el servicio oficial del SAT: no hay padrones cacheados ni copias locales.

Además, permite consultar si un RFC aparece en las listas públicas del SAT de los artículos 69 y 69-B del Código Fiscal de la Federación: EFOS (empresas que facturan operaciones simuladas), contribuyentes no localizados, créditos fiscales firmes o exigibles, condonaciones, sellos digitales cancelados y las demás. Esas listas se descargan del SAT a diario y se consultan localmente, así que responden en milisegundos.

URL base y autenticación

Todas las peticiones van al gateway de idoo.dev con tu API key:

https://idoo.dev/v1/valida-rfc/{accion}
curl -X POST "https://idoo.dev/v1/valida-rfc/validar" \
  -H "Authorization: Bearer {tu_api_key}" \
  -H "Content-Type: application/json" \
  -d '{"rfc": "FCA130603EX5"}'

También se acepta el header X-API-KEY: {tu_api_key}. Las keys se crean en tu panel de idoo.dev.

Ambientes: sandbox vs producción

El ambiente lo determina tu API key, no un parámetro:

Key Ambiente Comportamiento
idoo_sk_test_... Sandbox Devuelve una respuesta simulada al instante, sin consultar al SAT. Gratis e ilimitado: no consume la cuota de tu plan. La forma de la respuesta es idéntica a producción y trae "simulado": true.
idoo_sk_live_... Producción Consulta real al SAT. Cada consulta descuenta 1 de tu plan.

Sandbox siempre responde que el RFC es válido, sin importar cuál mandes: sirve para integrar el contrato de la API, no para probar casos de negocio. Los formatos inválidos (422) sí se validan en sandbox.

En listas y efos, sandbox no consulta ninguna lista: un RFC que empiece por EFO (por ejemplo EFO010101AB1) responde como DEFINITIVO en el 69-B y cualquier otro responde que no aparece, para que pruebes las dos ramas de tu integración.

validar, listas y efos descuentan cuota. listas/estado y salud son libres (aplica únicamente al rate limit de tu plan). El header X-Idoo-Cuota-Restante de cada respuesta cobrable te dice cuántas consultas te quedan en el periodo.

Formato de respuesta

Todas las respuestas (éxito o error) usan el mismo sobre JSON:

{
  "valid": true,
  "status": 200,
  "message": "RFC válido, y susceptible de recibir facturas",
  "data": { "...": "..." },
  "timestamp": "2026-09-09 07:26:07"
}

En los errores, valid es false y se agrega el campo error con un código estable, pensado para que lo compares en tu código — a diferencia de message, que es texto legible y puede cambiar.

status: 200 no significa "RFC válido". Significa que la consulta se realizó con éxito. El veredicto está en data.valido.

Tiempos de respuesta

Tarda típicamente entre 2 y 4 segundos, y puede llegar a 20 cuando se necesitan varios intentos. Configura el timeout de tu cliente en al menos 30 segundos y haz la llamada en segundo plano si tu interfaz no puede esperar. En sandbox la respuesta es inmediata.

Códigos de estado

Código Significado
200 Consulta realizada (revisa data.valido para el veredicto).
400 El cuerpo no es un JSON válido.
401 No se envió API key, o alguien llamó al backend directo sin pasar por el gateway.
402 Cuota de consultas de tu plan agotada (solo producción).
403 API key inválida o sin suscripción activa a esta API.
404 La ruta no existe.
405 Método HTTP no permitido.
422 Los datos enviados no son válidos: falta el RFC, o su formato (o el del CP) es incorrecto.
429 Excediste el rate limit por minuto de tu plan (Retry-After indica cuándo reintentar).
502 El SAT no respondió, o devolvió algo que no se pudo interpretar.
503 No se pudo obtener tras varios intentos, o las listas del SAT no están disponibles.
504 La consulta al SAT excedió el tiempo máximo.

Catálogo de endpoints

Método Endpoint Descripción ¿Descuenta cuota?
POST validar Valida un RFC ante el SAT; opcionalmente coteja nombre y CP. Sí
POST listas Busca un RFC en todas las listas públicas del SAT (Art. 69, 69-B y 69-B Bis). Sí
POST efos Busca un RFC solo en las listas de EFOS (Art. 69-B y 69-B Bis). Sí
GET listas/estado Cuántos registros tiene cada lista y cuándo se actualizó. No
GET salud Estado del servicio, para monitoreo. No

POST validar

Consulta un RFC ante el SAT.

Body:

{
  "rfc": "FON2010129P5",
  "nombre": "FONDO DE CULTURA ECONOMICA",
  "cp": "14738"
}
Campo Tipo Obligatorio Descripción
rfc string Sí RFC a validar: 12 caracteres para persona moral, 13 para persona física. No distingue mayúsculas.
nombre string No Nombre, denominación o razón social a cotejar.
cp string No Código postal del domicilio fiscal, 5 dígitos.

Enviar nombre o cp activa la validación cruzada del SAT: el RFC puede existir y aun así responder que los datos no coinciden. El cotejo del nombre es literal — el SAT compara contra la razón social exacta que tiene registrada, sin régimen de capital (SA DE CV) salvo que así esté inscrita.

Respuesta 200 — solo RFC:

{
  "valid": true,
  "status": 200,
  "message": "RFC válido, y susceptible de recibir facturas",
  "data": {
    "rfc": "FCA130603EX5",
    "nombre": null,
    "cp": null,
    "resultado": "RFC válido, y susceptible de recibir facturas",
    "valido": true,
    "simulado": false,
    "idoo": { "consumidor_id": 4242, "plan": "Pro", "ambiente": "produccion" }
  },
  "timestamp": "2026-09-09 07:26:07"
}

Respuesta 200 — con nombre y CP que no coinciden:

{
  "data": {
    "rfc": "FCA130603EX5",
    "nombre": "FONDO DE CULTURA ECONOMICA",
    "cp": "14738",
    "resultado": "EL Nombre o Razón Social y CP no coinciden con lo registrado en el RFC",
    "valido": false,
    "simulado": false
  },
  "message": "EL Nombre o Razón Social y CP no coinciden con lo registrado en el RFC"
}

Campos de data:

Campo Tipo Descripción
rfc string RFC consultado, normalizado a mayúsculas.
nombre string | null Nombre cotejado, o null si no lo enviaste.
cp string | null Código postal cotejado, o null si no lo enviaste.
resultado string Veredicto textual del SAT, tal cual lo devuelve.
valido boolean true solo si el SAT respondió que el RFC es válido. Si enviaste nombre o cp y no coinciden, es false.
simulado boolean true cuando la respuesta viene de sandbox y no del SAT.
idoo object Contexto que el gateway asoció a tu key: consumidor_id, plan y ambiente. Útil para confirmar con qué key estás llamando.

Errores específicos:

Código error Cuándo ocurre
422 rfc_requerido No enviaste el campo rfc.
422 rfc_formato_invalido El RFC no tiene forma de RFC (12 o 13 caracteres, con la estructura oficial).
422 cp_formato_invalido El código postal no tiene 5 dígitos.
400 json_invalido El cuerpo no es JSON válido.
502 sat_inalcanzable El SAT no respondió. Transitorio: reintenta.
502 respuesta_inesperada El SAT respondió algo que no se pudo interpretar. Transitorio.
503 no_resuelto No hay respuesta del SAT. Transitorio: reintenta.
504 tiempo_agotado La consulta excedió el tiempo máximo. Transitorio.

POST listas

Busca un RFC en todas las listas públicas del SAT. No consulta al SAT en vivo: lee una copia local que se descarga del SAT cada día, así que responde en milisegundos y no depende de que el SAT esté arriba.

Body:

{ "rfc": "AAA080808HL8" }
Campo Tipo Obligatorio Descripción
rfc string Sí RFC a buscar: 12 caracteres para persona moral, 13 para persona física. No distingue mayúsculas.

Respuesta 200 — RFC encontrado en una lista:

{
  "valid": true,
  "status": 200,
  "message": "El RFC aparece en 1 lista(s): 69b",
  "data": {
    "rfc": "AAA080808HL8",
    "encontrado": true,
    "razon_social": "ASESORES EN AVALÚOS Y ACTIVOS, S.A. DE C.V.",
    "en_listas": ["69b"],
    "listas": {
      "69b": {
        "nombre": "EFOS: operaciones presuntamente inexistentes (Art. 69-B CFF)",
        "articulo": "69-B",
        "actualizado": "2026-10-23",
        "encontrado": true,
        "registros": [
          {
            "rfc": "AAA080808HL8",
            "razon_social": "ASESORES EN AVALÚOS Y ACTIVOS, S.A. DE C.V.",
            "situacion": "SENTENCIA FAVORABLE",
            "oficio_presuncion_sat": "500-05-2018-16632 de fecha 01 de junio de 2018",
            "pub_sat_presuntos": "01/06/2018",
            "oficio_definitivos_sat": "500-05-2018-27105 de fecha 27 de septiembre de 2018",
            "pub_sat_definitivos": "28/09/2018",
            "oficio_sentencia_sat": "500-05-2019-7305 de fecha 5 de marzo de 2019",
            "pub_sat_sentencia": "05/03/2019"
          }
        ]
      },
      "69b_bis": { "nombre": "…", "articulo": "69-B Bis", "actualizado": "2026-10-23", "encontrado": false, "registros": [] },
      "cancelados": { "nombre": "…", "articulo": "69", "actualizado": "2026-10-23", "encontrado": false, "registros": [] },
      "…": "una entrada por cada lista del catálogo"
    },
    "simulado": false,
    "idoo": { "consumidor_id": 4242, "plan": "Pro", "ambiente": "produccion" }
  },
  "timestamp": "2026-10-23 14:02:11"
}

Campos de data:

Campo Tipo Descripción
rfc string RFC consultado, normalizado a mayúsculas.
encontrado boolean true si el RFC aparece en al menos una lista.
razon_social string | null Nombre o razón social que el SAT publica para ese RFC, o null si no aparece.
en_listas string[] Claves de las listas en las que aparece. Vacío si no aparece en ninguna.
listas object Una entrada por lista, con la clave como nombre. Siempre vienen todas, aparezca el RFC o no.
listas.*.nombre string Nombre legible de la lista.
listas.*.articulo string 69, 69-B o 69-B Bis.
listas.*.actualizado string | null Fecha de la última descarga exitosa de esa lista. Un encontrado: false solo vale a esa fecha. null si la lista nunca se ha cargado.
listas.*.encontrado boolean Si el RFC aparece en esa lista.
listas.*.registros array Los registros tal como los publica el SAT; las columnas dependen de la lista (ver abajo). Un RFC puede traer varios en una misma lista (varios créditos, varias fechas).

Listas y sus columnas:

Clave Artículo Qué es Columnas de cada registro
69b 69-B EFOS: contribuyentes con operaciones presuntamente inexistentes situacion (PRESUNTO, DESVIRTUADO, DEFINITIVO, SENTENCIA FAVORABLE) y, por fase, oficio_*_sat, pub_sat_*, oficio_*_dof, pub_dof_*
69b_bis 69-B Bis Transmisión indebida de pérdidas fiscales situacion (DEFINITIVO, SENTENCIA FAVORABLE), oficio_definitivo_*, pub_*_definitivo, oficio_sentencia_*, pub_*_sentencia
cancelados 69 Créditos fiscales cancelados tipo_persona, supuesto, fecha_cancelacion, monto, fecha_publicacion, entidad_federativa
exigibles 69 Créditos fiscales exigibles tipo_persona, supuesto, fecha_primera_publicacion, entidad_federativa
firmes 69 Créditos fiscales firmes igual que exigibles
no_localizados 69 Contribuyentes no localizados igual que exigibles
sentencias 69 Sentencias condenatorias por delito fiscal igual que exigibles
reduccion_multas 69 Reducción de multas (Art. 74) tipo_persona, supuesto, fecha_autorizacion, monto, fecha_publicacion, entidad_federativa
reduccion_recargos 69 Reducción de recargos (Art. 21) tipo_persona, supuesto, fecha_primera_publicacion, monto, fecha_publicacion_monto, entidad_federativa
condonados_concurso 69 Condonados por concurso mercantil (Art. 146-B) igual que reduccion_recargos
condonados_decreto 69 Condonados por decreto de 2015 igual que reduccion_recargos
retorno_inversiones 69 Retorno de inversiones igual que reduccion_recargos
csd_sin_efectos 69 Certificados de sello digital dejados sin efectos supuesto_cancelacion, fecha_cancelacion, administracion_responsable, fecha_publicacion
entes_publicos 69 Entes públicos y de gobierno omisos supuesto, ejercicio, periodo, fecha_primera_publicacion
condonados_2007_2015 69 Condonados de 2007 a 2015 (histórico) anio, tipo_persona, monto, motivo, entidad_federativa
cancelados_146a 69 Cancelados Art. 146-A de 2007 a 2015 (histórico) igual que condonados_2007_2015

Todos los registros traen además rfc y razon_social. Las fechas van como YYYY-MM-DD, salvo las de publicación del 69-B (pub_*), que se conservan como las publica el SAT (DD/MM/YYYY). tipo_persona es M (moral) o F (física); en las listas históricas viene como texto (Persona moral).

Errores específicos:

Código error Cuándo ocurre
422 rfc_requerido No enviaste el campo rfc.
422 rfc_formato_invalido El RFC no tiene forma de RFC.
400 json_invalido El cuerpo no es JSON válido.
405 metodo_no_permitido Llamaste con GET en vez de POST.
503 listas_no_disponibles La copia local de las listas no está disponible. Transitorio: reintenta en unos minutos.

En sandbox no se consulta ninguna lista: cualquier RFC que empiece por EFO (por ejemplo EFO010101AB1) responde como DEFINITIVO en la lista 69b, y cualquier otro responde que no aparece en ninguna. Así puedes probar las dos ramas de tu integración.

POST efos

Igual que listas, pero solo busca en las listas del artículo 69-B (69b) y 69-B Bis (69b_bis): es la consulta típica antes de aceptar una factura de un proveedor. La respuesta tiene la misma forma, con solo esas dos entradas en listas.

curl -X POST "https://idoo.dev/v1/valida-rfc/efos" \
  -H "Authorization: Bearer {tu_api_key}" \
  -H "Content-Type: application/json" \
  -d '{"rfc": "AAA080808HL8"}'

El message resume el veredicto: El RFC aparece en el artículo 69-B con situación DEFINITIVO o El RFC no aparece en las listas del artículo 69-B. Para decidir en código usa data.encontrado y data.listas["69b"].registros[0].situacion.

encontrado: true no siempre es malo. Que un RFC aparezca en el 69-B no significa que sea un EFOS confirmado; situacion dice en qué fase está:

situacion Qué significa ¿Afecta las facturas que emitió?
PRESUNTO El SAT presume que factura operaciones inexistentes; el contribuyente está a tiempo de desvirtuarlo. Todavía no, pero es señal de riesgo.
DESVIRTUADO El contribuyente demostró que sus operaciones son reales. No.
DEFINITIVO Confirmado: sus facturas no tienen efectos fiscales. Sí.
SENTENCIA FAVORABLE Un tribunal le dio la razón después de haber sido definitivo. No.

Si el RFC tiene varios registros en 69b, cada uno es un procedimiento distinto con su propia situación: un mismo RFC puede estar DEFINITIVO por un oficio y con SENTENCIA FAVORABLE por otro.

GET listas/estado

Dice cuántos registros activos tiene cada lista y cuándo se actualizó por última vez. Sirve para que tu sistema sepa qué tan fresca es la información antes de confiar en un encontrado: false. No descuenta cuota.

curl "https://idoo.dev/v1/valida-rfc/listas/estado" \
  -H "Authorization: Bearer {tu_api_key}"

Respuesta 200:

{
  "valid": true,
  "status": 200,
  "message": "Estado de las listas",
  "data": {
    "listas": {
      "69b": {
        "nombre": "EFOS: operaciones presuntamente inexistentes (Art. 69-B CFF)",
        "articulo": "69-B",
        "registros": 13874,
        "actualizado": "2026-10-23",
        "fecha_corte": "2026-09-30",
        "ultima_importacion": "2026-10-23 03:31:08",
        "estatus": "OK"
      },
      "…": "una entrada por lista"
    },
    "simulado": false,
    "idoo": { "consumidor_id": 4242, "plan": "Pro", "ambiente": "produccion" }
  },
  "timestamp": "2026-10-23 14:02:11"
}

actualizado es la fecha en que se descargó el archivo del SAT; fecha_corte es la fecha que el propio SAT declara dentro del archivo (solo la traen las listas del 69-B) y suele ir unas semanas atrás.

GET salud

Comprueba que el servicio responde. Útil para monitoreo; no consulta al SAT ni descuenta cuota.

curl "https://idoo.dev/v1/valida-rfc/salud" \
  -H "Authorization: Bearer {tu_api_key}"

Respuesta 200:

{
  "valid": true,
  "status": 200,
  "message": "Servicio operativo",
  "data": {
    "servicio": "valida-rfc",
    "idoo": { "consumidor_id": 4242, "plan": "Pro", "ambiente": "produccion" }
  },
  "timestamp": "2026-09-09 07:25:36"
}

Flujo recomendado de integración

  1. Integra en sandbox con tu key idoo_sk_test_... — es gratis e ilimitado. Ahí armas el manejo del sobre JSON y de los errores 422 sin gastar consultas.
  2. Valida el formato del RFC de tu lado antes de llamar. Una consulta con formato inválido te devuelve 422 y no descuenta cuota, pero te cuesta un viaje de red; validarlo en tu formulario es mejor experiencia.
  3. Llama a validar con el RFC solo, o con nombre y cp si necesitas el cotejo completo (por ejemplo, para dar de alta a un cliente que va a recibir facturas).
  4. Lee el veredicto en data.valido, no en el status. Un 200 con "valido": false es una respuesta correcta del SAT diciendo que ese RFC no sirve para facturar.
  5. Maneja 502/503/504 con reintentos acotados y espera entre ellos: el SAT se cae seguido. Recuerda que cada reintento descuenta cuota.
  6. Como cada consulta tarda segundos, guarda el resultado en tu sistema en vez de volver a consultar el mismo RFC en cada pantalla. Los datos del padrón cambian con poca frecuencia.
  7. Cuando estés listo, crea tu key de producción y elige el plan según tu volumen de consultas.

Si además usas las listas del SAT:

  • Llama a efos antes de aceptar la factura de un proveedor y decide según situacion. Usa listas cuando necesites el panorama completo, por ejemplo al dar de alta un cliente o en una auditoría: cuesta lo mismo, pero devuelve las 16 listas.
  • Guarda actualizado junto con el resultado: un "no aparece" vale hasta esa fecha, y las listas cambian una vez al día.
  • Vigila listas/estado en tu monitoreo (no descuenta cuota). Si actualizado se queda atrás varios días, tus "no aparece" van perdiendo valor.

Ejercicios guiados

Todos corren en sandbox y no consumen cuota. Sustituye {tu_api_key} por tu key idoo_sk_test_....

Ejercicio 1 — Validar un RFC

El caso más simple: ¿este RFC puede recibir facturas?

curl -X POST "https://idoo.dev/v1/valida-rfc/validar" \
  -H "Authorization: Bearer {tu_api_key}" \
  -H "Content-Type: application/json" \
  -d '{"rfc": "FCA130603EX5"}'

En producción, data.valido será true si el SAT lo tiene registrado y activo para facturación.

Ejercicio 2 — Cotejar nombre y código postal

Al dar de alta un cliente conviene verificar que los tres datos que te dio coinciden entre sí; es lo que evita rechazos al timbrar después.

curl -X POST "https://idoo.dev/v1/valida-rfc/validar" \
  -H "Authorization: Bearer {tu_api_key}" \
  -H "Content-Type: application/json" \
  -d '{
        "rfc": "FCA130603EX5",
        "nombre": "FONDO DE CULTURA ECONOMICA",
        "cp": "14738"
      }'

Si el SAT responde que no coinciden, el status sigue siendo 200: la consulta funcionó. El problema está en data.valido: false y en data.resultado, que te dice qué falló.

Ejercicio 3 — Manejar un fallo transitorio

El SAT no siempre responde. Este es el patrón mínimo de reintento, en PHP:

$intentos = 0;
do {
    $respuesta = miCliente()->post('validar', ['rfc' => $rfc]);
    $codigo = $respuesta['status'];
    if ($codigo < 500) {
        break;                  // 200, 422, 400: no tiene caso reintentar
    }
    $intentos++;
    sleep(2 * $intentos);       // espera creciente
} while ($intentos < 3);

Tres intentos son suficientes: si el SAT sigue caído, avísale al usuario y guarda el RFC para reintentar más tarde. No lo pongas en un bucle infinito: cada vuelta descuenta cuota.

Ejercicio 4 — Revisar un proveedor en el 69-B

Antes de aceptar una factura, confirma que el emisor no es un EFOS definitivo. En sandbox, prueba con EFO010101AB1 (aparece) y con cualquier otro RFC (no aparece).

curl -X POST "https://idoo.dev/v1/valida-rfc/efos"   -H "Authorization: Bearer {tu_api_key}"   -H "Content-Type: application/json"   -d '{"rfc": "EFO010101AB1"}'

En PHP:

$r = miCliente()->post('efos', ['rfc' => $rfcEmisor]);
if (!$r['valid']) {
    throw new RuntimeException($r['error'] ?? 'error ' . $r['status']);
}
$situaciones = array_column($r['data']['listas']['69b']['registros'], 'situacion');
if (in_array('DEFINITIVO', $situaciones, true)) {
    // Rechazar: sus facturas no tienen efectos fiscales
} elseif (in_array('PRESUNTO', $situaciones, true)) {
    // Revisar a mano: está en riesgo
}

efos no va al SAT: responde en milisegundos, así que un timeout de 10 segundos es de sobra.


Errores frecuentes

  • Tratar el status como el veredicto. 200 significa "consulté al SAT y esto es lo que dijo". El veredicto está en data.valido.
  • Mandar la razón social con régimen de capital. Si el SAT tiene registrada FONDO DE CULTURA ECONOMICA y tú envías FONDO DE CULTURA ECONOMICA SA DE CV, el cotejo falla. Manda el nombre tal como está en la constancia de situación fiscal.
  • Timeouts cortos. Un cliente con timeout de 5 segundos va a cortar consultas que iban a responder bien. Usa 30 segundos.
  • Consultar el mismo RFC repetidamente. Cada llamada descuenta cuota y tarda segundos. Guarda el resultado.
  • Reintentar un 422. Ese error es de tus datos, no del SAT: reintentar da exactamente lo mismo.
  • Rechazar a todo el que aparece en el 69-B. DESVIRTUADO y SENTENCIA FAVORABLE son contribuyentes que ganaron; solo DEFINITIVO invalida sus facturas.
  • Confiar en un "no aparece" sin mirar la fecha. Vale a la fecha de actualizado de esa lista.
Reseñas

Esta API todavía no tiene reseñas.

Escribir una reseña

Inicia sesión y suscríbete para poder reseñar.

Iniciar sesión