Manual de integración
Descargar los XML de sus facturas recibidas y enviadas de la DIAN
API REST para consultar de forma masiva las facturas de compra y de venta registradas en el catálogo de la DIAN, y descargar sus XML —con NIT del emisor, totales e IVA discriminado— desde su propio sistema.
Qué hace este servicio
El servicio consulta el catálogo de la DIAN y devuelve los documentos electrónicos de un rango de fechas, con toda su información estructurada y, si se solicita, el XML original firmado de cada documento.
Se resuelve en dos etapas, que la API encadena automáticamente:
- Listado. Obtiene del catálogo los CUFE de todos los documentos del rango, con su emisor, folio, fecha, estado y valor.
- Consulta. Por cada CUFE pregunta a la DIAN y devuelve el XML original del documento.
La segunda etapa ya no exige certificado digital. El endpoint
/api/xml-sin-certificado encadena las dos con el solo enlace AuthToken de la
empresa, y entrega además las líneas de detalle y los impuestos por línea. La vía anterior,
que sí pide el .p12, sigue vigente y sin cambios.
La información proviene directamente de la DIAN en cada consulta. El servicio no mantiene una copia de los documentos: actúa como pasarela, no como repositorio. Vea la sección Almacenamiento.
Antes de empezar
Para consumir la API necesita tener a mano lo siguiente:
| Elemento | Para qué sirve | Quién lo entrega |
|---|---|---|
| URL base | Dirección del servicio | Ágil Factura |
API KeyX-Api-Key | Autenticar los endpoints de listado y de XML por CUFE | Ágil Factura |
Token de empresaAuthorization: Bearer | Autenticar el flujo completo usando el certificado ya configurado | Ágil Factura |
| Enlace AuthToken de la DIAN | Autorizar la consulta del catálogo. Cambia en cada extracción | La DIAN, por correo |
Certificado .p12 y su contraseña | Solo si usará el endpoint de XML por CUFE enviando el certificado | Su entidad certificadora |
La API Key y el token de empresa son credenciales: consúmalas desde su servidor, nunca desde el navegador ni desde una aplicación móvil, y no las publique en repositorios. Si sospecha que se expusieron, solicite su rotación.
El token de la DIAN
Es el punto que más dudas genera, así que conviene entenderlo bien. La DIAN envía por correo un enlace de autorización con esta forma:
https://catalogo-vpfe.dian.gov.co/User/AuthToken?pk=10910094|53031455&rk=901234567&token=6b84e2c2-9530-4e02-8bc0-647da438d804
Ese enlace completo es lo que se envía en el campo url de cada petición.
Tres reglas que debe respetar
| Regla | Detalle |
|---|---|
| Caduca | Tiene vigencia limitada. Genere uno nuevo justo antes de cada extracción; no lo guarde para reutilizarlo al día siguiente. |
| Debe ser del mismo ambiente | Un token de habilitación no sirve para consultar documentos de producción, y viceversa. |
| Va completo | Cópielo íntegro, incluidos pk, rk y token. Si recorta algún parámetro, la consulta falla. |
El enlace de la DIAN contiene una barra vertical en el parámetro pk, que no es
un carácter válido en una URL. El servicio la codifica automáticamente, así que puede pegar
el enlace tal como lo recibió. Si su cliente HTTP valida URLs por su cuenta, envíela
como %7C.
Autenticación
Hay dos formas, según el endpoint que use.
A · API Key para volumen
X-Api-Key: <api_key>
Es la que usan los dos endpoints del flujo principal. Una sola clave sirve para todas las empresas que usted maneje: el certificado de cada una viaja dentro de la petición, así que no hay nada que registrar previamente.
Como el certificado va en el cuerpo, use siempre HTTPS y consuma la API desde su servidor, nunca desde el navegador.
La clave es suya y personal: identifica a su organización y es la que atribuye el consumo que se le factura (ver Consumo). No la comparta con terceros.
B · Token de empresa
Authorization: Bearer <token_de_empresa>
Alternativa para los endpoints de extracción completa. El servicio firma con el certificado que la empresa ya tiene configurado, de modo que usted no lo envía — pero requiere dar de alta cada empresa con su certificado, lo que no resulta práctico cuando son muchas.
Compras y ventas
El campo tipo determina qué documentos se extraen:
| Valor | Qué trae | Consideraciones |
|---|---|---|
Received |
Compras — documentos que le emitieron a su empresa | Se valida que su empresa sea el adquiriente. Son los únicos sobre los que pueden registrarse eventos RADIAN. |
Sent |
Ventas — documentos que su empresa emitió | Solo para consulta y reportes. No aplican eventos RADIAN. |
Para obtener ambos debe hacer dos extracciones separadas, una por cada valor. El resto de los parámetros es idéntico.
El listado incluye todo lo que la DIAN registre en el rango: facturas electrónicas, notas
crédito y débito, documentos equivalentes POS y títulos valor. Filtre por el campo
tipoDoc si solo le interesan algunos.
Endpoints
Si lo que quiere es extraer XML de forma masiva, el primero le basta: resuelve listado y XML en una sola llamada y no necesita certificado digital. Los dos siguientes son la vía anterior, que sigue vigente y sí lo pide. Los tres usan la misma API Key y no requieren registrar ninguna empresa. Los demás son alternativas para casos puntuales.
Recorre el catálogo en un rango de fechas y, por cada documento, descarga además su XML firmado y lo interpreta. No requiere certificado digital: le basta el enlace AuthToken de la empresa. Devuelve lo del listado más las líneas de detalle y los impuestos por línea, que el listado no tiene.
{
"url": "https://catalogo-vpfe.dian.gov.co/User/AuthToken?...",
"tipo": "Received", // Received compras · Sent ventas
"fechaDesde": "2026-08-01",
"fechaHasta": "2026-08-31",
"limite": 10 // opcional: acota cuántos se descargan
}
{
"success": true,
"total": 161,
"ok": 155, // facturas con XML interpretado
"no_aplica": 6, // acuses RADIAN u otros: NO son errores
"errores": 0,
"parcial": false,
"conteo": {
"pedidos": 161, "entregados": 161, "de_cache": 0,
"reintentos": 1, "bloqueos": 1, "sin_xml": 0, "fallidos": 0
},
"data": [{
"TipoDocumento": "Invoice",
"Numero de Factura": "AFE8052025",
"CUFE": "09acc105d8dd1e61...",
"NIT del Emisor": "860013771",
"Sub Total": 553800,
"Impuestos Cabecera": 105222,
"Total a Pagar": 659022,
"TaxBreakdown": [{ "code": "01", "name": "IVA", "percent": 19,
"taxable_amount": 553800, "amount": 105222 }],
"LineItems": [{
"Nombre del Producto": "Domo Circular resq Negro - CJ x 50 un",
"Referencia": "D15251099", "Cantidad": 5,
"Precio Unitario": 83000, "Total": 415000,
"taxes": [{ "code": "01", "percent": 19, "amount": 78850 }]
}],
"Estado extracción": "ok"
}]
}
conteo es el parte de la corrida y conviene revisarlo: de_cache
son los que ya estaban guardados y no se volvieron a pedir, bloqueos las
veces que la DIAN frenó el ritmo y reintentos las que se recuperaron.
Si fallidos no es cero, la extracción está incompleta aunque
success sea true: cada documento trae su propio
Estado extracción.
Por encima de 400 documentos responde 422 sin haber descargado
nada —no se gasta cupo del portal— y devuelve en rangos_sugeridos los
tramos de fechas en que conviene partirlo, calculados con los documentos que hay
realmente en cada día. Ver la sección 09.
Devuelve únicamente el listado del catálogo, sin consultar cada documento. Es mucho más rápido y sirve para saber qué hay antes de decidir qué extraer.
{
"success": true,
"total": 6,
"data": [{
"cufe": "1ac814c2df59984f...",
"tipoDoc": "01",
"folio": "DPOS73771",
"fechaEmision": "26-07-2026",
"nitEmisor": "901772356",
"nombreEmisor": "GRUPO RC S.A.S.",
"estado": "Aprobado",
"EstadoRadian": "No Aplica",
"valor": "$ 246.850",
"valorNumerico": 246850
}]
}
valor viene con formato de moneda para mostrarlo tal cual;
valorNumerico trae el mismo importe como número, para que no
tenga que interpretar el texto al hacer cálculos o comparaciones.
Puede llegar con decimales.
Entrega el XML original firmado de una lista de CUFE, en base64. Hasta 500 documentos por llamada. No requiere que su empresa esté registrada en el servicio: el certificado viaja en la petición.
{
"certificate": "<contenido del .p12 en base64>",
"password": "<contraseña del certificado>",
"ambiente": 1, // 1 producción · 2 habilitación
"cufes": [ "1669c097...", "18a20b3d..." ]
}
{
"success": true, "total": 2, "ok": 2,
"data": [{
"cufe": "1669c097...",
"success": true,
"xmlBase64": "PD94bWwgdmVyc2lvbj0iMS4wIi..."
}]
}
Alternativas
Estas tres usan token de empresa, lo que exige que cada empresa esté registrada en el servicio con su certificado. Sirven cuando se opera una sola empresa, no cuando son muchas.
Inicia una extracción completa. Responde de inmediato con un identificador de trabajo, sin esperar a que termine el proceso. Es el endpoint principal.
{
"tipo": "Received",
"fechaDesde": "2026-07-01",
"fechaHasta": "2026-07-31",
"url": "https://catalogo-vpfe.dian.gov.co/User/AuthToken?..."
}
{
"success": true,
"jobId": "19e1f865-8d68-4b93-8f1a-b162eb27ca08",
"statusUrl": "https://.../api/extractor/company/jobs/19e1f865-..."
}
Consulta el avance y, al finalizar, entrega el resultado. Repita la llamada cada 15–20
segundos hasta que status sea done.
{
"status": "running", // running · done · error
"stage": "enriching",
"total": 6,
"processed": 5,
"log": [ "..." ],
"data": [ /* documentos, al terminar */ ]
}
Hace todo el proceso en una sola llamada y responde al terminar. no recomendado
Mantiene la conexión abierta durante todo el proceso, por lo que el servidor web la corta
con un error 504 en cuanto hay unos pocos documentos. Úselo solo para pruebas
rápidas con rangos mínimos; para todo lo demás use el flujo asíncrono.
El XML que recibe
El campo xmlBase64 contiene el documento UBL 2.1 firmado, tal como lo tiene
la DIAN. No es un extracto ni una conversión: es el archivo original, con validez legal, que
usted procesa y conserva.
Qué trae
- La firma digital XAdES del emisor y la validación de la DIAN.
- Las extensiones DIAN (
sts:DianExtensions) con el CUFE, la resolución de numeración y el rango autorizado. - Emisor y adquiriente completos, con identificación, régimen y ubicación.
- Totales, desglose de impuestos, retenciones y el detalle de todos los renglones.
- Notas, medios de pago, descuentos y cargos.
Cómo obtenerlo
Decodifique el base64 y guarde el resultado como archivo .xml:
# PHP
file_put_contents("$cufe.xml", base64_decode($doc['xmlBase64']));
# Python
open(f"{cufe}.xml", "wb").write(base64.b64decode(doc["xmlBase64"]))
El contenido decodificado empieza siempre por
<?xml version="1.0"...><Invoice ...> (o CreditNote /
DebitNote según el tipo de documento).
Junto al XML, la respuesta incluye algunos campos ya extraídos (CUFE, emisor, totales) que sirven para identificar el documento sin abrirlo. Si su sistema procesa el XML directamente, puede ignorarlos.
Flujo recomendado
Una llamada por empresa y período, sin certificado, sin trabajos asíncronos y sin consultas de avance.
- Obtener un enlace AuthToken nuevo de la DIAN para esa empresa.
POST /api/xml-sin-certificadocon el rango de fechas → devuelve cada documento con su XML ya interpretado.- Revisar
conteo: sifallidosno es cero, reintentar ese período. - Guardar lo recibido y causarlo en su sistema.
- Repetir con
tipo: "Sent"si también necesita las ventas.
# todo en una llamada, sin certificado
curl -X POST https://<url-base>/api/xml-sin-certificado \
-H "X-Api-Key: <api_key>" -H "Content-Type: application/json" \
-d '{"tipo":"Received","fechaDesde":"2026-08-01","fechaHasta":"2026-08-31","url":"<authtoken>"}'
El tope es de 400 documentos por llamada. Un mes por empresa casi siempre cabe, es fácil de reintentar y encaja con el cierre contable. Si un mes se pasa, la respuesta le dice en qué tramos partirlo.
La vía anterior, con certificado
Sigue vigente y sin cambios. Son dos llamadas, y la segunda exige el .p12 de la
empresa. Tiene sentido cuando ya dispone de los CUFEs por otro medio, o cuando necesita un
caudal que no dependa del ritmo del portal de la DIAN.
# 1 · listar
curl -X POST https://<url-base>/api/cufes \
-H "X-Api-Key: <api_key>" -H "Content-Type: application/json" \
-d '{"tipo":"Received","fechaDesde":"2026-07-01","fechaHasta":"2026-07-31","url":"<authtoken>"}'
# 2 · traer los XML (en lotes)
curl -X POST https://<url-base>/api/ubl2.1/xml/by-cufes \
-H "X-Api-Key: <api_key>" -H "Content-Type: application/json" \
-d '{"certificate":"<p12_base64>","password":"<clave>","ambiente":1,"cufes":["..."]}'
Cuando son muchas empresas
El esquema escala sin configuración adicional, y esa es su principal ventaja:
- Una sola API Key para toda la operación, sin importar cuántas empresas maneje.
- Por la vía sin certificado no hay certificados que gestionar, que es lo que más pesa cuando son muchas empresas: ni pedirlos, ni custodiarlos, ni renovarlos al vencer.
- Si usa la vía con certificado, los
.p12se quedan de su lado: no hay que cargarlos ni registrarlos en el servicio, viajan en cada petición. - Lo único imprescindible por empresa es su enlace AuthToken.
El enlace AuthToken es uno por empresa y caduca. Con muchas empresas, conseguir los enlaces vigentes es la parte más pesada del proceso — conviene planificarla antes de automatizar el resto.
Límites y rendimiento
| Aspecto | Límite | Qué significa |
|---|---|---|
| Documentos por llamada | 400 | Tope de /api/xml-sin-certificado. Si el rango los supera, se rechaza antes de descargar nada y se sugiere cómo partirlo. |
| CUFEs por llamada | 500 | Tope de /api/ubl2.1/xml/by-cufes. Es el máximo que acepta, no el recomendado — vea abajo. |
| Llamadas totales | sin tope | Puede paginar tantos lotes como necesite. |
| Rango de fechas | sin tope | Un rango amplio simplemente tarda más en listar. |
El límite real es el tiempo, no la cantidad
El servicio consulta la DIAN una vez por cada documento, de forma secuencial. Un lote de 500 CUFEs son 500 consultas encadenadas, y puede tardar más de lo que el servidor web permite mantener abierta una petición.
Use lotes de 100 a 150 CUFEs. Para afinarlo a su servidor, envíe un lote de 10 y mida
cuánto tarda: divida ese tiempo entre 10 y aplique
lote = 300 s ÷ segundos_por_documento. Lotes más pequeños además son más fáciles
de reintentar cuando algo falla.
La vía sin certificado va al ritmo del portal
/api/xml-sin-certificado no consulta el servicio web de la DIAN sino su
portal, y ese portal limita por dirección IP. Medido sobre 300 documentos:
| Pausa entre peticiones | Acierto | Documentos/min |
|---|---|---|
| sin pausa | 63 % | — |
| 600 ms | 95 % | 59 |
| 1.000 ms | 100 % | 44 |
Ir más deprisa no entrega más documentos: las peticiones rechazadas también gastan cupo. El servicio mantiene el paso de una por segundo y reintenta lo que la DIAN frene, así que no hay nada que ajustar del lado del cliente.
Ese límite lo comparten todas las empresas que salgan por la misma IP. Extraer varias en paralelo no multiplica el caudal, solo lo reparte: el techo de un servidor son unos 2.600 documentos por hora en total. Para subirlo hay que repartir las extracciones entre varias direcciones de salida.
Tiempos de referencia
| Operación | Tiempo | Por qué |
|---|---|---|
| Listar un período | ~1 min | Casi todo es abrir el navegador y autenticarse contra el portal; el listado en sí se resuelve en pocas consultas. |
| Un XML por CUFE | ~2 s | Consulta directa al servicio web, sin navegador. |
| Listado y XML, sin certificado | ~1,4 s/doc | Ritmo impuesto por el portal. Un mes de 160 documentos son unos 4 minutos; 400, unos 9. |
| Repetir un rango ya extraído | inmediato | Los XML descargados quedan guardados y no se vuelven a pedir. |
El listado puede rozar o superar el minuto, así que el servidor web debe permitirlo. Con los
valores por defecto (60 s) la petición se corta con un error 504. Solicite a su
proveedor que los tiempos de espera estén en 600 segundos: sobra margen a propósito,
porque un rango amplio o un día lento del portal de la DIAN pueden alargar la consulta.
Recomendaciones
- Extraiga por períodos mensuales y por empresa. Es más fácil de reintentar que un rango de un año.
- Programe el proceso en tareas de fondo o nocturnas, nunca dentro de una petición de usuario.
- No vuelva a pedir lo que ya tiene. Guarde por CUFE y consulte solo lo nuevo.
- Compare
okcontratotalen cada respuesta y reintente solo los CUFEs que fallaron. - Procese los lotes en serie, no en paralelo, para no saturar el servicio ni la DIAN.
Almacenamiento de los XML
El servicio no archiva los documentos. Los consulta a la DIAN, se los entrega en la respuesta y no conserva copia. Si necesita los XML para efectos contables o de auditoría, guardarlos es responsabilidad de su sistema.
Al recibir un xmlBase64, decodifíquelo y persístalo. Una convención simple:
almacenamiento/xml/<NIT>/<AAAA-MM>/<CUFE>.xml
Guarde también el CUFE en su base de datos como identificador único del documento: le sirve para no volver a descargarlo y para cualquier consulta posterior.
Que el servicio no almacene nada significa que su información no queda duplicada en un tercero, y que siempre recibe el documento tal como lo tiene la DIAN en ese momento.
Errores
| Código | Significado | Qué hacer |
|---|---|---|
401 | Credencial ausente o incorrecta | Revise el encabezado Authorization o X-Api-Key. |
422 | Datos inválidos, o empresa sin certificado configurado | Verifique el formato de fechas (AAAA-MM-DD) y que el enlace de la DIAN esté completo. |
422con rangos_sugeridos | El rango supera los 400 documentos de /api/xml-sin-certificado | No es un fallo y no se descargó nada. Parta el rango por los tramos que devuelve la respuesta, o use limite. |
404 | Trabajo inexistente o vencido | Pasaron más de 2 horas. Inicie la extracción de nuevo. |
502 | No hay comunicación con el componente de extracción | Reintente en unos minutos; si persiste, avise a Ágil Factura. |
504 | La petición excedió el tiempo permitido | Ocurre al usar /stream. Cambie al flujo asíncrono. |
Errores por documento
Una extracción puede terminar correctamente y aun así tener documentos individuales con error. Revise siempre los contadores del resultado:
- Procesados — cuántos se intentaron.
- OK — cuántos se obtuvieron completos.
- No coincide — el adquiriente del documento no corresponde a su NIT.
- Error — la DIAN no devolvió el documento. Reintente esos CUFE por separado.
Consumo
El servicio se factura por lo que se usa, y usted puede consultarlo cuando quiera con su misma API Key. La idea es que vea exactamente la misma cifra que vemos nosotros, sin tener que pedirla.
Consultas — cada llamada a /api/cufes, sin importar cuántos CUFE
devuelva el listado. Documentos — cada XML solicitado a
/api/ubl2.1/xml/by-cufes. Consultar su consumo no se cobra.
Sin parámetros devuelve el mes en curso. Acepta desde y
hasta en formato AAAA-MM-DD para consultar otro período.
{
"success": true,
"cliente": "Su Empresa S.A.S.",
"periodo": { "desde": "2026-08-01", "hasta": "2026-08-31" },
"consumo": { "consultas": 50, "documentos": 400 },
"importe": {
"cuota_mensual": 300000, // fija, se suma al consumo
"precio_consulta": 280,
"subtotal_consultas": 14000,
"tramo_documento": "1 – 500",
"precio_documento": 150,
"subtotal_documentos": 60000,
"consumido": 74000,
"total": 374000,
"moneda": "COP"
},
"detalle": [
{ "endpoint": "cufes", "unidad": "consulta",
"total": 50, "llamadas": 50 },
{ "endpoint": "xml/by-cufes", "unidad": "documento",
"total": 400, "llamadas": 4 }
]
}
El total es la cuota fija mensual más el consumo. Las tarifas del
ejemplo son las de referencia; las suyas son las de su contrato y el endpoint siempre
devuelve las que se le aplican a usted.
El precio del documento depende del volumen del mes
El tramo_documento lo define el total del mes y se aplica a
todos los documentos, incluidos los primeros — no es un cálculo por escalones.
Por eso, mientras el mes no cierre, el importe es una estimación: si el consumo
sigue creciendo y cambia de tramo, el precio unitario baja y el total puede
resultar menor que el que vio a mitad de mes.
Checklist de integración
Antes de dar por terminada la integración, verifique que su sistema:
- Solicita un enlace AuthToken nuevo por empresa en cada extracción, sin reutilizar el anterior.
- Usa
/api/cufesy/api/ubl2.1/xml/by-cufescomo flujo principal. - Divide los CUFEs en lotes de 100 a 150, ajustados a la medición de su servidor.
- Procesa los lotes en serie, no en paralelo.
- Almacena los XML por su cuenta, indexados por CUFE, y no vuelve a pedir lo ya descargado.
- Compara
okcontratotaly reintenta solo los CUFEs fallidos. - Extrae compras y ventas por separado (
ReceivedySent). - Corre en un proceso de fondo o tarea programada, no dentro de una petición de usuario.
- Guarda las credenciales y los certificados fuera del código, y los consume solo desde el servidor.
- Reintenta con espera progresiva cuando la DIAN no responde.
- Confirmó con su proveedor que los tiempos de espera del servidor están en 600 segundos.
- Revisa
/api/mi-consumoperiódicamente y vigila el conteo de consultas y documentos.