Documentación de la API
Valida constancias de situación fiscal (CSF), consulta el Listado 69-B del SAT y obtén datos fiscales ya normalizados y listos para mapear a los nodos de un CFDI 4.0 (RFC, razón social, código postal, régimen fiscal). Integración típica: menos de 15 minutos.
https://TU-DOMINIO.com
Autenticación
Todos los endpoints de producto (los que consumen crédito) requieren tu API key
en el header Authorization, esquema Bearer. El catálogo
público (/v1/catalogo/*) no requiere autenticación.
Authorization: Bearer vf_live_TU_API_KEY_AQUI
Créditos y saldo
Cada cliente tiene un solo saldo (pool) de créditos. Cada llamada exitosa a un
endpoint de producto consume créditos de ese pool, distinto según el trabajo
real que hace cada uno (10 para /validaciones-fiscales,
5 para /revalidaciones-fiscales, 1 para
/lista-69b), lo verás reflejado en el
campo creditos_consumidos de la respuesta.
Si no tienes saldo, el endpoint responde 402 Payment Required
sin intentar procesar nada.
Probar en vivo
Cada endpoint de esta página también se puede probar de verdad, con peticiones HTTP reales contra tu servidor: subir el PDF, mandar el RFC, ver la respuesta JSON tal cual la regresa la API, sin escribir ningún código ni salir del navegador. Pega tu API key una sola vez con el botón Authorize y ya queda lista para cualquier endpoint que pruebes después.
POST /v1/mx/validaciones-fiscales
Sube el PDF de una Constancia de Situación Fiscal (CSF) del SAT. La API extrae
y valida en vivo contra el SAT: RFC, razón social, domicilio fiscal y régimen
fiscal -- este último ya mapeado a la clave de 3 dígitos c_RegimenFiscal
que un CFDI exige, no solo el texto libre que trae el PDF.
Request
| Campo | Tipo | Descripción |
|---|---|---|
archivo | multipart/form-data | El PDF de la CSF. Máximo 8 MB. |
Response 200
| Campo | Tipo | Descripción |
|---|---|---|
identificacion_fiscal | string | RFC extraído y validado contra el SAT. |
razon_social | string | null | |
codigo_postal | string | null | Código postal del domicilio fiscal. |
estado_texto | string | null | |
municipio_texto | string | null | |
colonia | string | null | |
calle | string | null | |
numero_exterior | string | null | |
numero_interior | string | null | |
codigo_regimen_texto | string | null | Texto libre del régimen tal cual lo trae la CSF (ej. "Régimen Simplificado de Confianza"). Se conserva para auditoría. |
regimen_fiscal_codigo | string | null | La clave de 3 dígitos que necesitas para un CFDI (ej. "626"). null si no se pudo mapear con confianza: en ese caso usa codigo_regimen_texto para resolverlo a mano. |
regimen_fiscal_descripcion | string | null | Descripción oficial del catálogo SAT correspondiente a regimen_fiscal_codigo. |
regimen_fiscal_coincidencia | boolean | false si codigo_regimen_texto vino con datos pero no hubo match en el catálogo (el SAT cambió la redacción, o el régimen ya no está vigente). |
token_validacion | string | null | Úsalo en /revalidaciones-fiscales para volver a consultar sin re-subir el PDF. |
creditos_consumidos | string[] | Claves de los créditos cobrados por esta llamada. |
curl -X POST https://TU-DOMINIO.com/v1/mx/validaciones-fiscales \
-H "Authorization: Bearer vf_live_TU_API_KEY_AQUI" \
-F "archivo=@/ruta/a/constancia.pdf"
import requests
with open("constancia.pdf", "rb") as archivo:
respuesta = requests.post(
"https://TU-DOMINIO.com/v1/mx/validaciones-fiscales",
headers={"Authorization": "Bearer vf_live_TU_API_KEY_AQUI"},
files={"archivo": archivo},
)
datos = respuesta.json()
print(datos["regimen_fiscal_codigo"]) # ej. "626"
const form = new FormData();
form.append("archivo", archivoPdf); // File o Blob
const respuesta = await fetch("https://TU-DOMINIO.com/v1/mx/validaciones-fiscales", {
method: "POST",
headers: { Authorization: "Bearer vf_live_TU_API_KEY_AQUI" },
body: form,
});
const datos = await respuesta.json();
console.log(datos.regimen_fiscal_codigo); // ej. "626"
Ejemplo de respuesta 200
{
"identificacion_fiscal": "AAA080808HL8",
"razon_social": "COMERCIALIZADORA EJEMPLO SA DE CV",
"codigo_postal": "06600",
"estado_texto": "CIUDAD DE MEXICO",
"municipio_texto": "CUAUHTEMOC",
"colonia": "JUAREZ",
"calle": "AV REFORMA",
"numero_exterior": "123",
"numero_interior": null,
"codigo_regimen_texto": "Régimen Simplificado de Confianza",
"regimen_fiscal_codigo": "626",
"regimen_fiscal_descripcion": "Régimen Simplificado de Confianza",
"regimen_fiscal_coincidencia": true,
"token_validacion": "abc123tokendelsat",
"creditos_consumidos": ["VALIDACION_CSF_MX"]
}
POST /v1/mx/revalidaciones-fiscales
Repite la consulta al SAT usando el token_validacion
de una validación anterior; no necesitas volver a subir el PDF. Ideal para
revalidar periódicamente (ej. cada 15-30 días) sin pedirle al cliente su CSF
de nuevo. Misma forma de respuesta que /validaciones-fiscales.
Request
| Campo | Tipo | Descripción |
|---|---|---|
token_validacion | string | Token regresado por una llamada previa a /validaciones-fiscales. |
curl -X POST https://TU-DOMINIO.com/v1/mx/revalidaciones-fiscales \
-H "Authorization: Bearer vf_live_TU_API_KEY_AQUI" \
-H "Content-Type: application/json" \
-d '{"token_validacion": "abc123tokendelsat"}'
import requests
respuesta = requests.post(
"https://TU-DOMINIO.com/v1/mx/revalidaciones-fiscales",
headers={"Authorization": "Bearer vf_live_TU_API_KEY_AQUI"},
json={"token_validacion": "abc123tokendelsat"},
)
print(respuesta.json())
const respuesta = await fetch("https://TU-DOMINIO.com/v1/mx/revalidaciones-fiscales", {
method: "POST",
headers: {
Authorization: "Bearer vf_live_TU_API_KEY_AQUI",
"Content-Type": "application/json",
},
body: JSON.stringify({ token_validacion: "abc123tokendelsat" }),
});
console.log(await respuesta.json());
regimen_fiscal_codigo y regimen_fiscal_descripcion.
POST /v1/mx/lista-69b
Consulta si un RFC aparece en el Listado 69-B del SAT (Empresas que Facturan Operaciones Simuladas, EFOS), en cualquiera de sus 4 etapas. Consulta contra una copia local que se actualiza periódicamente, no en vivo contra el SAT (el SAT no publica el CSV en un horario fijo).
Request
| Campo | Tipo | Descripción |
|---|---|---|
rfc | string | RFC a consultar (12-13 caracteres). Se normaliza a mayúsculas. |
Response 200
| Campo | Tipo | Descripción |
|---|---|---|
rfc | string | |
aparece_en_listado | boolean | Deliberadamente NO es un solo semáforo de riesgo: un mismo RFC puede tener historial en más de una situación. |
situaciones_encontradas | string[] | Alguno de: presunto, desvirtuado, definitivo, sentencia_favorable. |
detalle | object[] | Un registro por cada coincidencia: situacion, nombre_contribuyente, datos_originales (fila cruda del SAT). |
listado_actualizado_en | string | null | Fecha ISO 8601 de la última actualización exitosa de la copia local. |
creditos_consumidos | string[] |
curl -X POST https://TU-DOMINIO.com/v1/mx/lista-69b \
-H "Authorization: Bearer vf_live_TU_API_KEY_AQUI" \
-H "Content-Type: application/json" \
-d '{"rfc": "AAA080808HL8"}'
import requests
respuesta = requests.post(
"https://TU-DOMINIO.com/v1/mx/lista-69b",
headers={"Authorization": "Bearer vf_live_TU_API_KEY_AQUI"},
json={"rfc": "AAA080808HL8"},
)
datos = respuesta.json()
print(datos["aparece_en_listado"])
const respuesta = await fetch("https://TU-DOMINIO.com/v1/mx/lista-69b", {
method: "POST",
headers: {
Authorization: "Bearer vf_live_TU_API_KEY_AQUI",
"Content-Type": "application/json",
},
body: JSON.stringify({ rfc: "AAA080808HL8" }),
});
const datos = await respuesta.json();
console.log(datos.aparece_en_listado);
Ejemplo de respuesta 200
{
"rfc": "AAA080808HL8",
"aparece_en_listado": true,
"situaciones_encontradas": ["presunto", "desvirtuado"],
"detalle": [
{
"situacion": "desvirtuado",
"nombre_contribuyente": "EJEMPLO SA DE CV",
"datos_originales": { "No": "12345", "Oficio Global de Presunción": "500-05-2018-27105 de fecha 27 de septiembre de 2018" }
}
],
"listado_actualizado_en": "2026-08-27T06:52:57.421759+00:00",
"creditos_consumidos": ["VAL_LISTA_NEGRA_69B"]
}
GET /v1/catalogo/regimenes-fiscales
Catálogo oficial de regímenes fiscales del SAT (c_RegimenFiscal).
No requiere autenticación. Útil cuando regimen_fiscal_coincidencia
te regresó false y necesitas dejar que un humano elija el régimen
correcto de una lista.
curl https://TU-DOMINIO.com/v1/catalogo/regimenes-fiscales
import requests
respuesta = requests.get("https://TU-DOMINIO.com/v1/catalogo/regimenes-fiscales")
print(respuesta.json())
const respuesta = await fetch("https://TU-DOMINIO.com/v1/catalogo/regimenes-fiscales");
console.log(await respuesta.json());
Ejemplo de respuesta 200
[
{ "codigo": "601", "descripcion": "General de Ley Personas Morales" },
{ "codigo": "612", "descripcion": "Personas Físicas con Actividades Empresariales y Profesionales" },
{ "codigo": "626", "descripcion": "Régimen Simplificado de Confianza" }
]
POST /v1/mx/extracciones/curp
Sube la Constancia CURP (el PDF que se descarga en gob.mx/curp) o una foto de la CURP, y la API regresa los datos ya separados en campos individuales: la CURP, el nombre completo dividido en nombre(s) y apellidos, la fecha y entidad de nacimiento, y el sexo. Sirve para prellenar un formulario sin que la persona tenga que volver a teclear cada dato a mano.
Request
| Campo | Tipo | Descripción |
|---|---|---|
archivo | multipart/form-data | PDF de la Constancia CURP, o foto/imagen (JPG, PNG) de la CURP. Máximo 8 MB. |
incluir_recorte | boolean (query param, default false) | Actívalo solo si necesitas la imagen recortada del recuadro principal (por ejemplo, para armar un expediente de evidencia de una solicitud de visa). Cuesta lo mismo en créditos que dejarlo apagado. |
Response 200
| Campo | Tipo | Descripción |
|---|---|---|
curp | string | null | |
estructura_valida | boolean | La CURP tiene la forma correcta (18 caracteres, patrón esperado). |
digito_verificador_coincide | boolean | null | Informativo, nunca es motivo de rechazo por sí solo. null si no se pudo calcular. |
nombres | string | null | |
apellido_paterno | string | null | |
apellido_materno | string | null | |
anio_nacimiento | integer | null | |
mes_nacimiento | integer | null | |
dia_nacimiento | integer | null | |
sexo | string | null | |
entidad_nacimiento | string | null | |
texto_qr_original | string | null | Lo que traiga el código QR del documento, tal cual, sin interpretar. |
token_validacion | string | null | Presente cuando el QR resultó ser una URL de verificación. |
imagen_recuadro_base64 | string | null | Solo presente si mandaste incluir_recorte=true y se pudo aislar el recuadro con confianza. PNG codificado en base64. |
creditos_consumidos | string[] |
curl -X POST "https://TU-DOMINIO.com/v1/mx/extracciones/curp?incluir_recorte=true" \
-H "Authorization: Bearer vf_live_TU_API_KEY_AQUI" \
-F "archivo=@/ruta/a/constancia_curp.pdf"
import requests
with open("constancia_curp.pdf", "rb") as archivo:
respuesta = requests.post(
"https://TU-DOMINIO.com/v1/mx/extracciones/curp",
headers={"Authorization": "Bearer vf_live_TU_API_KEY_AQUI"},
params={"incluir_recorte": "true"},
files={"archivo": archivo},
)
datos = respuesta.json()
print(datos["curp"])
const form = new FormData();
form.append("archivo", archivoPdf); // File o Blob
const respuesta = await fetch(
"https://TU-DOMINIO.com/v1/mx/extracciones/curp?incluir_recorte=true",
{
method: "POST",
headers: { Authorization: "Bearer vf_live_TU_API_KEY_AQUI" },
body: form,
}
);
const datos = await respuesta.json();
console.log(datos.curp);
Ejemplo de respuesta 200
{
"curp": "XEXX010101HNEXTX04",
"estructura_valida": true,
"digito_verificador_coincide": true,
"nombres": "ANA",
"apellido_paterno": "PEREZ",
"apellido_materno": "GOMEZ",
"anio_nacimiento": 2001,
"mes_nacimiento": 1,
"dia_nacimiento": 1,
"sexo": "M",
"entidad_nacimiento": "NACIDO EN EL EXTRANJERO",
"texto_qr_original": "XEXX010101HNEXTX04|PEREZ|GOMEZ|ANA",
"token_validacion": null,
"imagen_recuadro_base64": "iVBORw0KGgoAAAANSUhEUgAA...(truncado)",
"creditos_consumidos": ["EXTRACCION_CURP"]
}
POST /v1/mx/extracciones/domicilio
Sube un comprobante de domicilio en PDF (por ejemplo, un recibo de CFE, agua o teléfono descargado del portal del proveedor) y la API separa calle, colonia, código postal, ciudad o municipio y estado en campos individuales, listos para usarse en un formulario.
confianza_extraccion: "baja"
con los campos estructurados en null.
Request
| Campo | Tipo | Descripción |
|---|---|---|
archivo | multipart/form-data | PDF del comprobante de domicilio, con texto seleccionable. Máximo 8 MB. |
Response 200
| Campo | Tipo | Descripción |
|---|---|---|
calle | string | null | |
numero_exterior | string | null | |
numero_interior | string | null | |
colonia | string | null | |
codigo_postal | string | null | |
ciudad_o_municipio | string | null | |
estado | string | null | |
confianza_extraccion | string | "alta" si se reconocieron al menos 2 de las etiquetas esperadas (CP, calle, colonia, estado); "baja" si no, y entonces conviene revisar texto_completo a mano. |
texto_completo | string | null | Primeros 3000 caracteres del texto del PDF, por si necesitas revisarlo cuando la confianza salió baja. |
creditos_consumidos | string[] |
curl -X POST https://TU-DOMINIO.com/v1/mx/extracciones/domicilio \
-H "Authorization: Bearer vf_live_TU_API_KEY_AQUI" \
-F "archivo=@/ruta/a/recibo_cfe.pdf"
import requests
with open("recibo_cfe.pdf", "rb") as archivo:
respuesta = requests.post(
"https://TU-DOMINIO.com/v1/mx/extracciones/domicilio",
headers={"Authorization": "Bearer vf_live_TU_API_KEY_AQUI"},
files={"archivo": archivo},
)
datos = respuesta.json()
print(datos["codigo_postal"])
const form = new FormData();
form.append("archivo", archivoPdf); // File o Blob
const respuesta = await fetch("https://TU-DOMINIO.com/v1/mx/extracciones/domicilio", {
method: "POST",
headers: { Authorization: "Bearer vf_live_TU_API_KEY_AQUI" },
body: form,
});
const datos = await respuesta.json();
console.log(datos.codigo_postal);
Ejemplo de respuesta 200
{
"calle": "AV REFORMA",
"numero_exterior": "123",
"numero_interior": null,
"colonia": "JUAREZ",
"codigo_postal": "06600",
"ciudad_o_municipio": "CUAUHTEMOC",
"estado": "CIUDAD DE MEXICO",
"confianza_extraccion": "alta",
"texto_completo": "CFE RECIBO... AV REFORMA 123... COL JUAREZ... C.P. 06600...",
"creditos_consumidos": ["EXTRACCION_DOMICILIO"]
}
POST /v1/mx/extracciones/pasaporte
Sube un pasaporte en PDF y la API lee la zona MRZ (las dos líneas de texto al pie de la página de datos) para extraer nombre, apellidos, fechas de nacimiento y vencimiento, y confirmar que los dígitos de control del documento cierran correctamente. Útil para detectar un pasaporte alterado o mal capturado antes de usarlo en un trámite.
POST /v1/mx/extracciones/pasaporte/mrz-texto
en su lugar: manda {"mrz_texto": "..."}
y no necesita subir ningún archivo.
Request
| Campo | Tipo | Descripción |
|---|---|---|
archivo | multipart/form-data | PDF del pasaporte, con la MRZ como texto seleccionable. Máximo 8 MB. |
Response 200
| Campo | Tipo | Descripción |
|---|---|---|
pais_emisor | string | null | Código de 3 letras del país que emitió el documento. |
nacionalidad | string | null | |
apellido_paterno | string | null | La MRZ trae los apellidos juntos; se separan con la misma heurística que usa la CURP. |
apellido_materno | string | null | |
nombres | string | null | |
numero_documento | string | null | |
sexo | string | null | |
anio_nacimiento | integer | null | |
mes_nacimiento | integer | null | |
dia_nacimiento | integer | null | |
anio_expiracion | integer | null | |
mes_expiracion | integer | null | |
dia_expiracion | integer | null | |
numero_personal | string | null | Campo opcional de la MRZ, no todos los países lo usan. |
digitos_verificadores_validos | object | Un booleano por cada dígito de control de la MRZ: numero_documento, fecha_nacimiento, fecha_expiracion, compuesto. |
todos_los_digitos_validos | boolean | true solo si los 4 dígitos de control cerraron correctamente. |
creditos_consumidos | string[] |
curl -X POST https://TU-DOMINIO.com/v1/mx/extracciones/pasaporte \
-H "Authorization: Bearer vf_live_TU_API_KEY_AQUI" \
-F "archivo=@/ruta/a/pasaporte.pdf"
import requests
with open("pasaporte.pdf", "rb") as archivo:
respuesta = requests.post(
"https://TU-DOMINIO.com/v1/mx/extracciones/pasaporte",
headers={"Authorization": "Bearer vf_live_TU_API_KEY_AQUI"},
files={"archivo": archivo},
)
datos = respuesta.json()
print(datos["todos_los_digitos_validos"])
const form = new FormData();
form.append("archivo", archivoPdf); // File o Blob
const respuesta = await fetch("https://TU-DOMINIO.com/v1/mx/extracciones/pasaporte", {
method: "POST",
headers: { Authorization: "Bearer vf_live_TU_API_KEY_AQUI" },
body: form,
});
const datos = await respuesta.json();
console.log(datos.todos_los_digitos_validos);
Ejemplo de respuesta 200
{
"pais_emisor": "UTO",
"nacionalidad": "UTO",
"apellido_paterno": "ERIKSSON",
"apellido_materno": null,
"nombres": "ANNA MARIA",
"numero_documento": "L898902C3",
"sexo": "M",
"anio_nacimiento": 1974,
"mes_nacimiento": 8,
"dia_nacimiento": 12,
"anio_expiracion": 2012,
"mes_expiracion": 4,
"dia_expiracion": 15,
"numero_personal": "ZE184226B",
"digitos_verificadores_validos": {
"numero_documento": true,
"fecha_nacimiento": true,
"fecha_expiracion": true,
"compuesto": true
},
"todos_los_digitos_validos": true,
"creditos_consumidos": ["EXTRACCION_PASAPORTE"]
}
POST /v1/mx/validaciones-cruzadas
Compara los datos que ya extrajiste de 2 a 8 documentos de la misma persona (CURP, domicilio, pasaporte, CSF, o los tuyos propios) y te dice si son consistentes entre sí. No procesa ningún archivo nuevo, solo compara los campos que ya tienes, así que sirve como una última revisión antes de usar esos datos en un formulario.
Request
| Campo | Tipo | Descripción |
|---|---|---|
documentos | object[] (2 a 8 elementos) | Cada elemento representa los datos ya extraídos de un documento. Ver campos abajo. |
Campo de documentos[i] | Tipo | Descripción |
|---|---|---|
tipo | string | Identificador libre del documento de origen, por ejemplo "csf", "curp", "pasaporte", "domicilio". |
nombre_completo | string | null | Un solo string con el nombre completo (no lo partas en nombres/apellidos): la comparación es por traslape de palabras, así que el orden entre documentos no importa. |
anio_nacimiento | integer | null | |
mes_nacimiento | integer | null | |
dia_nacimiento | integer | null | |
curp | string | null | |
rfc | string | null |
Response 200
| Campo | Tipo | Descripción |
|---|---|---|
consistente | boolean | true si no se encontró ninguna discrepancia. |
discrepancias | object[] | Un elemento por cada campo (curp, rfc, fecha_nacimiento o nombre_completo) donde los documentos no coinciden: campo (string) y valores (objeto, tipo de documento apuntando al valor visto ahí). |
creditos_consumidos | string[] |
curl -X POST https://TU-DOMINIO.com/v1/mx/validaciones-cruzadas \
-H "Authorization: Bearer vf_live_TU_API_KEY_AQUI" \
-H "Content-Type: application/json" \
-d '{
"documentos": [
{ "tipo": "csf", "nombre_completo": "JUAN PEREZ LOPEZ", "rfc": "PELJ800101ABC", "anio_nacimiento": 1980, "mes_nacimiento": 1, "dia_nacimiento": 1 },
{ "tipo": "curp", "nombre_completo": "JUAN PEREZ LOPEZ", "curp": "PELJ800101HDFRPN01", "anio_nacimiento": 1980, "mes_nacimiento": 1, "dia_nacimiento": 2 }
]
}'
import requests
respuesta = requests.post(
"https://TU-DOMINIO.com/v1/mx/validaciones-cruzadas",
headers={"Authorization": "Bearer vf_live_TU_API_KEY_AQUI"},
json={
"documentos": [
{"tipo": "csf", "nombre_completo": "JUAN PEREZ LOPEZ", "rfc": "PELJ800101ABC", "anio_nacimiento": 1980, "mes_nacimiento": 1, "dia_nacimiento": 1},
{"tipo": "curp", "nombre_completo": "JUAN PEREZ LOPEZ", "curp": "PELJ800101HDFRPN01", "anio_nacimiento": 1980, "mes_nacimiento": 1, "dia_nacimiento": 2},
]
},
)
datos = respuesta.json()
print(datos["consistente"])
const respuesta = await fetch("https://TU-DOMINIO.com/v1/mx/validaciones-cruzadas", {
method: "POST",
headers: {
Authorization: "Bearer vf_live_TU_API_KEY_AQUI",
"Content-Type": "application/json",
},
body: JSON.stringify({
documentos: [
{ tipo: "csf", nombre_completo: "JUAN PEREZ LOPEZ", rfc: "PELJ800101ABC", anio_nacimiento: 1980, mes_nacimiento: 1, dia_nacimiento: 1 },
{ tipo: "curp", nombre_completo: "JUAN PEREZ LOPEZ", curp: "PELJ800101HDFRPN01", anio_nacimiento: 1980, mes_nacimiento: 1, dia_nacimiento: 2 },
],
}),
});
const datos = await respuesta.json();
console.log(datos.consistente);
Ejemplo de respuesta 200
{
"consistente": false,
"discrepancias": [
{
"campo": "fecha_nacimiento",
"valores": { "csf": "1980-01-01", "curp": "1980-01-02" }
}
],
"creditos_consumidos": ["VALIDACION_CRUZADA"]
}
POST /v1/mx/extracciones/combinada
Procesa hasta 4 documentos de la misma persona (CSF, CURP, domicilio y pasaporte) en una sola llamada, con descuento sobre llamar cada endpoint por separado, y guarda el resultado en el historial auditable de un expediente. Si alguno de los documentos no se puede procesar, los demás que sí funcionaron se regresan igual: un solo documento fallido no cancela toda la llamada.
Request
| Campo | Tipo | Descripción |
|---|---|---|
csf | multipart/form-data (opcional) | Constancia de Situación Fiscal (PDF con QR del SAT). |
curp | multipart/form-data (opcional) | Constancia CURP (PDF) o foto/imagen de la CURP. |
domicilio | multipart/form-data (opcional) | Comprobante de domicilio (PDF con texto seleccionable). |
pasaporte | multipart/form-data (opcional) | Pasaporte (PDF con la MRZ como texto seleccionable). |
id_expediente | string, form field (opcional) | Expediente existente al que agregar estos documentos. Si no se manda, se crea uno nuevo. |
referencia_expediente | string, form field (opcional) | Solo aplica si no se manda id_expediente: referencia libre para el expediente nuevo. |
incluir_recorte_curp | boolean, form field (default false) | Igual que incluir_recorte en /extracciones/curp, pero para el campo curp de esta respuesta. |
errores.
Response 200
| Campo | Tipo | Descripción |
|---|---|---|
id_expediente | string | El expediente usado (el que mandaste, o uno nuevo si no mandaste id_expediente). |
fiscal | object | null | Mismo objeto que regresa /v1/mx/validaciones-fiscales. null si no mandaste csf. |
curp | object | null | Mismo objeto que regresa /v1/mx/extracciones/curp. null si no mandaste curp. |
domicilio | object | null | Mismo objeto que regresa /v1/mx/extracciones/domicilio. null si no mandaste domicilio. |
pasaporte | object | null | Mismo objeto que regresa /v1/mx/extracciones/pasaporte. null si no mandaste pasaporte. |
errores | object | Solo incluye los documentos que sí mandaste pero no se pudieron procesar: tipo de documento apuntando al motivo. |
creditos_consumidos | string[] | Una clave por cada documento procesado con éxito. |
curl -X POST https://TU-DOMINIO.com/v1/mx/extracciones/combinada \
-H "Authorization: Bearer vf_live_TU_API_KEY_AQUI" \
-F "referencia_expediente=Juan Perez - visa EB-2" \
-F "csf=@/ruta/a/constancia.pdf" \
-F "curp=@/ruta/a/constancia_curp.pdf" \
-F "pasaporte=@/ruta/a/pasaporte.pdf"
import requests
with open("constancia.pdf", "rb") as csf, open("constancia_curp.pdf", "rb") as curp, open("pasaporte.pdf", "rb") as pasaporte:
respuesta = requests.post(
"https://TU-DOMINIO.com/v1/mx/extracciones/combinada",
headers={"Authorization": "Bearer vf_live_TU_API_KEY_AQUI"},
data={"referencia_expediente": "Juan Perez - visa EB-2"},
files={"csf": csf, "curp": curp, "pasaporte": pasaporte},
)
datos = respuesta.json()
print(datos["id_expediente"])
const form = new FormData();
form.append("referencia_expediente", "Juan Perez - visa EB-2");
form.append("csf", archivoCsf);
form.append("curp", archivoCurp);
form.append("pasaporte", archivoPasaporte);
const respuesta = await fetch("https://TU-DOMINIO.com/v1/mx/extracciones/combinada", {
method: "POST",
headers: { Authorization: "Bearer vf_live_TU_API_KEY_AQUI" },
body: form,
});
const datos = await respuesta.json();
console.log(datos.id_expediente);
Ejemplo de respuesta 200
{
"id_expediente": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"fiscal": { "identificacion_fiscal": "AAA080808HL8", "razon_social": "COMERCIALIZADORA EJEMPLO SA DE CV", "...": "..." },
"curp": { "curp": "XEXX010101HNEXTX04", "nombres": "ANA", "...": "..." },
"domicilio": null,
"pasaporte": { "pais_emisor": "UTO", "nombres": "ANNA MARIA", "...": "..." },
"errores": {},
"creditos_consumidos": ["FISCAL_EN_COMBINADA", "CURP_EN_COMBINADA", "PASAPORTE_EN_COMBINADA"]
}
POST /v1/mx/expedientes
Crea un expediente vacío para agrupar las extracciones de una persona o trámite
bajo un mismo identificador (por ejemplo, todos los documentos de una solicitud de
visa). Después vas agregando documentos con /v1/mx/extracciones/combinada
usando el id_expediente que te regresa aquí.
Request
| Campo | Tipo | Descripción |
|---|---|---|
referencia | string | null (opcional) | Texto libre para reconocer el expediente en tu propio sistema, por ejemplo "Juan Pérez - visa EB-2". Nunca se interpreta ni se valida. |
Response 200
| Campo | Tipo | Descripción |
|---|---|---|
id_expediente | string | UUID del expediente. Guárdalo para usarlo en /extracciones/combinada y en /expedientes/{id_expediente}. |
referencia | string | null | |
creado_en | string (fecha ISO 8601) |
curl -X POST https://TU-DOMINIO.com/v1/mx/expedientes \
-H "Authorization: Bearer vf_live_TU_API_KEY_AQUI" \
-H "Content-Type: application/json" \
-d '{"referencia": "Juan Perez - visa EB-2"}'
import requests
respuesta = requests.post(
"https://TU-DOMINIO.com/v1/mx/expedientes",
headers={"Authorization": "Bearer vf_live_TU_API_KEY_AQUI"},
json={"referencia": "Juan Perez - visa EB-2"},
)
datos = respuesta.json()
print(datos["id_expediente"])
const respuesta = await fetch("https://TU-DOMINIO.com/v1/mx/expedientes", {
method: "POST",
headers: {
Authorization: "Bearer vf_live_TU_API_KEY_AQUI",
"Content-Type": "application/json",
},
body: JSON.stringify({ referencia: "Juan Perez - visa EB-2" }),
});
const datos = await respuesta.json();
console.log(datos.id_expediente);
Ejemplo de respuesta 200
{
"id_expediente": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"referencia": "Juan Perez - visa EB-2",
"creado_en": "2026-09-04T10:15:00.000000+00:00"
}
GET /v1/mx/expedientes/{id_expediente}
Consulta el historial completo de un expediente: cada documento que se procesó, el endpoint que se usó, si fue exitoso y el resultado completo tal como se le devolvió al cliente en ese momento. Es el registro auditable para poder mostrar de dónde salió cada dato más adelante, por ejemplo ante un revisor de la solicitud.
Request
| Campo | Tipo | Descripción |
|---|---|---|
id_expediente | string (parámetro de ruta) | UUID del expediente, regresado por POST /expedientes o por POST /extracciones/combinada. |
Response 200
| Campo | Tipo | Descripción |
|---|---|---|
id_expediente | string | |
referencia | string | null | |
creado_en | string (fecha ISO 8601) | |
documentos | object[] | Uno por cada documento procesado, en orden cronológico. Cada elemento trae tipo_documento (string), endpoint (string), exitoso (boolean), resultado (object, el JSON completo que se le devolvió al cliente para ese documento) y creado_en (fecha ISO 8601). |
404 si el expediente no existe, o si existe pero pertenece a otra cuenta.
curl https://TU-DOMINIO.com/v1/mx/expedientes/3fa85f64-5717-4562-b3fc-2c963f66afa6 \
-H "Authorization: Bearer vf_live_TU_API_KEY_AQUI"
import requests
respuesta = requests.get(
"https://TU-DOMINIO.com/v1/mx/expedientes/3fa85f64-5717-4562-b3fc-2c963f66afa6",
headers={"Authorization": "Bearer vf_live_TU_API_KEY_AQUI"},
)
datos = respuesta.json()
print(len(datos["documentos"]))
const respuesta = await fetch(
"https://TU-DOMINIO.com/v1/mx/expedientes/3fa85f64-5717-4562-b3fc-2c963f66afa6",
{ headers: { Authorization: "Bearer vf_live_TU_API_KEY_AQUI" } }
);
const datos = await respuesta.json();
console.log(datos.documentos.length);
Ejemplo de respuesta 200
{
"id_expediente": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"referencia": "Juan Perez - visa EB-2",
"creado_en": "2026-09-04T10:15:00.000000+00:00",
"documentos": [
{
"tipo_documento": "curp",
"endpoint": "/v1/mx/extracciones/combinada",
"exitoso": true,
"resultado": { "curp": "XEXX010101HNEXTX04", "nombres": "ANA", "...": "..." },
"creado_en": "2026-09-04T10:16:12.000000+00:00"
},
{
"tipo_documento": "domicilio",
"endpoint": "/v1/mx/extracciones/combinada",
"exitoso": false,
"resultado": { "error": "No se pudo leer texto del PDF (¿es un escaneo sin capa de texto?)." },
"creado_en": "2026-09-04T10:16:12.000000+00:00"
}
]
}
POST /v1/mx/consultas/cedula-profesional
Busca en el Registro Nacional de Profesionistas de la SEP por número de cédula o por nombre, y regresa el título, la institución y el tipo de registro encontrado. Sirve para confirmar que un profesionista (abogado, contador, médico, y demás) de verdad tiene la cédula que dice tener.
Request
| Campo | Tipo | Descripción |
|---|---|---|
numero_cedula | string | null | Manda este campo o nombre (al menos uno de los dos). |
nombre | string | null | Búsqueda por nombre si no se tiene el número de cédula. |
Response 200
| Campo | Tipo | Descripción |
|---|---|---|
total_encontrados | integer | |
resultados | object[] | Uno por cada coincidencia: numero_cedula, nombre_completo, titulo, institucion, tipo, anio_registro y datos_originales (objeto, el registro crudo tal como lo regresa la SEP). |
creditos_consumidos | string[] |
curl -X POST https://TU-DOMINIO.com/v1/mx/consultas/cedula-profesional \
-H "Authorization: Bearer vf_live_TU_API_KEY_AQUI" \
-H "Content-Type: application/json" \
-d '{"numero_cedula": "1234567"}'
import requests
respuesta = requests.post(
"https://TU-DOMINIO.com/v1/mx/consultas/cedula-profesional",
headers={"Authorization": "Bearer vf_live_TU_API_KEY_AQUI"},
json={"numero_cedula": "1234567"},
)
datos = respuesta.json()
print(datos["total_encontrados"])
const respuesta = await fetch("https://TU-DOMINIO.com/v1/mx/consultas/cedula-profesional", {
method: "POST",
headers: {
Authorization: "Bearer vf_live_TU_API_KEY_AQUI",
"Content-Type": "application/json",
},
body: JSON.stringify({ numero_cedula: "1234567" }),
});
const datos = await respuesta.json();
console.log(datos.total_encontrados);
Ejemplo de respuesta 200
{
"total_encontrados": 1,
"resultados": [
{
"numero_cedula": "1234567",
"nombre_completo": "JUAN PEREZ LOPEZ",
"titulo": "LICENCIADO EN DERECHO",
"institucion": "UNIVERSIDAD NACIONAL AUTONOMA DE MEXICO",
"tipo": "TITULO",
"anio_registro": "2005",
"datos_originales": {}
}
],
"creditos_consumidos": ["CONSULTA_CEDULA_PROFESIONAL"]
}
POST /v1/mx/validaciones/clabe
Valida la estructura y el dígito verificador de una CLABE interbancaria de 18 dígitos, e identifica el banco a partir de los primeros 3 dígitos. Sirve para detectar un error de captura antes de programar una transferencia o un pago.
Request
| Campo | Tipo | Descripción |
|---|---|---|
clabe | string (exactamente 18 caracteres) | La CLABE a validar. |
Response 200
| Campo | Tipo | Descripción |
|---|---|---|
clabe | string | |
estructura_valida | boolean | La CLABE tiene 18 dígitos con la forma esperada. |
digito_verificador_coincide | boolean | null | null si no se pudo calcular (por ejemplo, si estructura_valida es false). |
codigo_banco | string | null | Los primeros 3 dígitos de la CLABE. |
nombre_banco | string | null | null si el código de banco no se reconoce en el catálogo. |
codigo_plaza | string | null | |
creditos_consumidos | string[] |
curl -X POST https://TU-DOMINIO.com/v1/mx/validaciones/clabe \
-H "Authorization: Bearer vf_live_TU_API_KEY_AQUI" \
-H "Content-Type: application/json" \
-d '{"clabe": "002010077777777770"}'
import requests
respuesta = requests.post(
"https://TU-DOMINIO.com/v1/mx/validaciones/clabe",
headers={"Authorization": "Bearer vf_live_TU_API_KEY_AQUI"},
json={"clabe": "002010077777777770"},
)
datos = respuesta.json()
print(datos["nombre_banco"])
const respuesta = await fetch("https://TU-DOMINIO.com/v1/mx/validaciones/clabe", {
method: "POST",
headers: {
Authorization: "Bearer vf_live_TU_API_KEY_AQUI",
"Content-Type": "application/json",
},
body: JSON.stringify({ clabe: "002010077777777770" }),
});
const datos = await respuesta.json();
console.log(datos.nombre_banco);
Ejemplo de respuesta 200
{
"clabe": "002010077777777770",
"estructura_valida": true,
"digito_verificador_coincide": true,
"codigo_banco": "002",
"nombre_banco": "BANAMEX",
"codigo_plaza": "010",
"creditos_consumidos": ["VALIDACION_CLABE"]
}
GET /v1/datos-economicos/...
Consulta series de datos económicos y financieros (tipo de cambio, inflación, PIB, tasas de interés, población, entre otros) de 7 fuentes oficiales distintas: Banxico, INEGI, FRED, la OCDE, el FMI, la CEPAL y el Banco Mundial. Las 7 comparten la misma forma de respuesta, así que tu integración no cambia según la fuente que consultes.
Authorization: Bearer
que el resto del catálogo). Además, Banxico, INEGI y FRED requieren que el servidor
de ValidatosAPI tenga configurado un token gratuito propio de esa fuente (ver la
columna "Token propio" en la tabla más abajo); OCDE, FMI, CEPALSTAT y Banco Mundial
no necesitan ningún token adicional.
Ejemplo completo: Banxico
GET /v1/datos-economicos/banxico/{id_serie}: por ejemplo,
SF43718 es el tipo de cambio pesos por dólar (FIX) que publica Banxico.
| Campo | Tipo | Descripción |
|---|---|---|
id_serie | string (parámetro de ruta) | Identificador de la serie en el catálogo del SIE de Banxico. |
solo_ultimo | boolean (query param, default true) | true = solo el dato más reciente (1 crédito). false = serie completa (2 créditos). |
fecha_inicio | string | null (query param) | Formato YYYY-MM-DD. Solo aplica si solo_ultimo=false. |
fecha_fin | string | null (query param) | Formato YYYY-MM-DD. Solo aplica si solo_ultimo=false. |
Response 200 (misma forma para las 7 fuentes)
| Campo | Tipo | Descripción |
|---|---|---|
fuente | string | Por ejemplo "banxico", "inegi", "fred". |
identificador | string | El identificador consultado, tal cual lo mandaste en la ruta. |
titulo | string | null | Nombre descriptivo de la serie, si la fuente lo incluye en su respuesta. |
datos | object[] | Un elemento por observación: fecha (string, formato según la fuente) y valor (string, para no perder precisión ni formato de fuentes que traen texto, ej. "preliminar"). |
creditos_consumidos | string[] |
curl "https://TU-DOMINIO.com/v1/datos-economicos/banxico/SF43718?solo_ultimo=true" \
-H "Authorization: Bearer vf_live_TU_API_KEY_AQUI"
import requests
respuesta = requests.get(
"https://TU-DOMINIO.com/v1/datos-economicos/banxico/SF43718",
headers={"Authorization": "Bearer vf_live_TU_API_KEY_AQUI"},
params={"solo_ultimo": "true"},
)
datos = respuesta.json()
print(datos["datos"][0]["valor"])
const respuesta = await fetch(
"https://TU-DOMINIO.com/v1/datos-economicos/banxico/SF43718?solo_ultimo=true",
{ headers: { Authorization: "Bearer vf_live_TU_API_KEY_AQUI" } }
);
const datos = await respuesta.json();
console.log(datos.datos[0].valor);
Ejemplo de respuesta 200
{
"fuente": "banxico",
"identificador": "SF43718",
"titulo": "Tipo de cambio pesos por dólar E.U.A. FIX",
"datos": [
{ "fecha": "03/09/2026", "valor": "18.3541" }
],
"creditos_consumidos": ["DATO_ECONOMICO"]
}
Las 7 fuentes
| Fuente | Endpoint | Token propio | Ejemplos de identificador |
|---|---|---|---|
| Banxico | GET /v1/datos-economicos/banxico/{id_serie} |
Sí, gratis en banxico.org.mx/SieAPIRest/service/v1/token | SF43718 (tipo de cambio FIX) |
| INEGI | GET /v1/datos-economicos/inegi/{id_indicador} (query area_geografica, default "0700" nacional) |
Sí, gratis en inegi.org.mx/app/api/indicadores | 628194 (INPC) |
| FRED | GET /v1/datos-economicos/fred/{id_serie} (mismos query solo_ultimo/fecha_inicio/fecha_fin que Banxico) |
Sí, gratis en fred.stlouisfed.org/docs/api/api_key.html | GDP (PIB de EUA), UNRATE (desempleo de EUA) |
| OCDE | GET /v1/datos-economicos/ocde/{dataflow} (requiere query filtro_dimensiones) |
No, pública | dataflow OECD.SDD.TPS,DSD_PDB@DF_PDB_LV,1.0 con filtro_dimensiones=MEX.A.GDPHRS..USD_PPP_H.Q... |
| FMI | GET /v1/datos-economicos/fmi/{base_datos}/{frecuencia}/{paises}/{indicador} |
No, pública | IFS / M / MX / ENDA_XDC_USD_RATE (tipo de cambio) |
| CEPALSTAT | GET /v1/datos-economicos/cepalstat/{id_indicador} (query pais, anio, opcionales) |
No, pública | Ver catálogo en statistics.cepal.org/portal/databank |
| Banco Mundial | GET /v1/datos-economicos/banco-mundial/{codigo_pais}/{id_indicador} |
No, pública | MX / NY.GDP.MKTP.CD (PIB en dólares) |
Manejo de errores
Todos los errores regresan un cuerpo JSON con un solo campo detail
describiendo qué pasó.
| Código | Cuándo ocurre |
|---|---|
401 | Falta el header Authorization, o la API key es inválida/revocada/expirada. |
402 | Saldo de créditos insuficiente. No se cobra ni se procesa nada. |
403 | La cuenta del cliente no está activa. |
413 | El PDF excede el máximo permitido (8 MB). |
415 | El archivo enviado no es un PDF válido. |
422 | El documento no pudo procesarse o validarse (ver detail para la causa específica), o el body de la petición no cumple la validación esperada. |
500 | Error interno. Si persiste, contáctanos con el id de la respuesta si está disponible. |
{
"detail": "Falta la API key (header 'Authorization: Bearer ')."
}