Errores y límites
El contrato de errores es estable: códigos fijos, un formato único y una señal explícita de si conviene reintentar. Esta página es la tabla completa de la plataforma; cada servicio enlaza aquí y solo agrega la forma de sus códigos propios.
El formato
Todo error responde el mismo body. Respuesta real de un request sin token:
Respuesta real (2026-09-28).
{
"code": "UNAUTHORIZED",
"message": "missing bearer token",
"request_id": "c95a96e7-1636-40ac-b422-24c0f88030fc",
"retryable": false
}
Programe contra code, que es
contractual; message es descriptivo y puede mejorar con el tiempo.
retryable indica si el mismo request puede tener éxito al reintentarse sin
cambiar nada: true solo en RATE_LIMITED, DOWNLOAD_QUOTA, INTERNAL y
SERVICE_UNAVAILABLE; los tres con Retry-After dicen cuánto esperar.
Los códigos
La columna dominio marca los códigos que solo un servicio produce; el resto es de la plataforma y puede aparecer en cualquier endpoint.
code |
HTTP | dominio | cuándo ocurre |
|---|---|---|---|
INVALID_PARAMS |
400 | todos | Parámetro faltante o mal formado, from > to, rango mayor a 5 años, as_of_revision fuera de rango, RUC que no tiene 11 dígitos, o page_token corrupto o de otra consulta. |
UNAUTHORIZED |
401 | todos | Token ausente, inválido, revocado o expirado. El servicio nunca repite el token en la respuesta. |
FORBIDDEN |
403 | todos | El token no puede usar esa ruta: un token de colector en /v1; GET /v1/ruc/by-dni/{dni} mientras el prerrequisito legal E1 esté pendiente; la descarga de un artefacto del padrón con un token sin la marca descarga_artefactos (Consulta RUC). |
UNSUPPORTED_CURRENCY |
404 | tipo de cambio | Moneda que la API sabe que no sirve (ejemplo real: currency=EUR). |
UNSUPPORTED_SERIES |
404 | tipo de cambio | Serie distinta de publicado y cierre. |
NO_OBSERVATIONS |
404 | tipo de cambio, revisiones | Consulta válida pero sin observaciones: rango sin datos o serie aún sin cobertura (Tipo de cambio); en /v1/revisions, at anterior a la primera revisión. Distinto por diseño de UNSUPPORTED_CURRENCY: "no hay datos" no es "no sirvo esa moneda". |
NOT_FOUND |
404 | todos | Observación, evidencia o artefacto inexistente para el id pedido. |
NO_DATA_AT_REVISION |
404 | consulta RUC | La as_of_revision pedida es anterior a la primera publicación del dominio (Consulta RUC). Todo dominio con publicaciones lo hereda. |
RATE_LIMITED |
429 | todos | Límite de requests del token excedido. Trae Retry-After con los segundos a esperar. |
DOWNLOAD_QUOTA |
429 | todos | Cuota diaria de bytes de descarga del token agotada (/v1/evidence/{id}/descarga). Trae Retry-After. |
INTERNAL |
500 | todos | Error interno. El detalle queda en los registros del servicio (referenciable por request_id), nunca en el body. |
SERVICE_UNAVAILABLE |
503 | todos | La base de datos no está disponible (por ejemplo, durante un reinicio). Trae Retry-After. |
Un dominio nuevo que necesite un código propio lo declara en esta tabla antes de producirlo; el registro de cambios fecha cada alta.
Límites de uso
- 60 requests por minuto y 5 000 por día por token; cada token identifica una instalación.
- Ante
429, espere lo que indiqueRetry-After. - Su consumo del mes, por dominio, está en
GET /v1/usage(ver Empezar). - Servicio en desarrollo, sin acceso público por ahora: estos son los límites con los que está desplegado.
Cuota de descarga
Los bytes de evidencia (GET /v1/evidence/{id}/descarga) tienen una cuota
diaria por token, aparte del límite de requests:
| cuota | bytes por día |
|---|---|
| por token | 20 GB (propuesto a partir del egreso medido del archivo: 19,8 MB/s; equivale a unos 50 ZIP del padrón al día) |
| credencial de evaluación interna | 2 GB (unos cinco ZIP del padrón) |
La ventana es deslizante de 24 horas. Una captura pequeña (el
tipoCambio.txt) cuenta por sus bytes al servirse; un artefacto grande (el
ZIP del padrón) cuenta por su tamaño al emitirse el redirect, y se rechaza
con DOWNLOAD_QUOTA si no cabe entero en lo que queda. Los metadatos
(GET /v1/evidence/{id}) nunca cuentan. La descarga es parte del plan del
token, sin sobrecargo.
Paginación a revisión fija
Dos endpoints paginan, con el mismo mecanismo:
| endpoint | máximo por página | orden |
|---|---|---|
GET /v1/rates |
366 ítems | fecha ascendente |
GET /v1/ruc/{ruc}/history |
500 transiciones | ordinal ascendente |
Si hay más, viene next_page_token: un token opaco que lleva embebida la
revisión de la primera página y la consulta a la que pertenece; se pasa
en page_token.
- Recorrer todas las páginas entrega un snapshot consistente: aunque el dataset avance entre página y página, todas responden a la misma revisión.
- El token solo vale para la misma consulta (moneda/serie/rango; RUC);
usarlo en otra responde
INVALID_PARAMS. total(tasas) ytotal_transiciones(historia) son el total a esa revisión, no el tamaño de la página. En la historia,estado_inicialviaja solo en la primera página.
Límite adicional: el rango from–to de tasas no puede exceder 5 años por
consulta.
Caché
| respuesta | política |
|---|---|
Respuesta anclada a revisión (as_of_revision, page_token) del tipo de cambio |
immutable + ETag (SHA-256 del body) — cacheable indefinidamente. |
/v1/rates, /v1/rates/latest sin anclar |
no-store — el dataset puede avanzar; no la guarde. |
/v1/ruc/{ruc}, by-dni, /history, /v1/ruc-coverage y /v1/evidence/{sha256} (metadatos del padrón), ancladas o no |
private, no-store, sin ETag, Vary: Authorization salvo ruc-coverage (adenda a ESP-006 A.7, desde R0): cada respuesta lleva valores propios del pedido. If-None-Match: * sobre un 200 → 304 sin cuerpo y con los mismos encabezados; otro valor → 200; un error se responde aunque traiga la precondición. La estabilidad del contenido anclado es propiedad del dato, no de la caché. |
/v1/evidence/{id}/descarga de una captura del tipo de cambio |
immutable + ETag = SHA-256 de los bytes. |
/v1/evidence/{uuid} (metadatos de una captura) |
no-store — el enlace firmado cambia en cada emisión. |
/v1/observations/{id} |
no-store — la observación es inmutable, pero su representación incluye la cadena de supersesión y los eventos, que pueden crecer. |
Alertas y salud: la plataforma se vigila a sí misma
Estos endpoints valen para todo dominio y no tienen página de servicio:
GET /v1/alerts— incidentes operativos abiertos (open=true, por defecto) o todos: fuente no disponible, invariante fallida, discrepancia entre fuentes, plausibilidad baja, cambio de formato, verificación fallida del padrón. Cada alerta tiene ciclo de vida (abierta→reconocida→resuelta) y la misma condición no se duplica. Un consumidor que quiera reaccionar consulta aquí; el servicio no empuja notificaciones en v1.GET /v1/health/live— vivo o no; el único endpoint sin token junto con el calendario. No toca la base de datos.GET /v1/health/ready— listo para servir: responde{"status": "ready"}solo si la base de datos contesta.GET /v1/health/freshness— la frescura de todas las series del tipo de cambio, el conteo de alertas abiertas y el estado del reloj del servidor (ntp:sincronizado,no_sincronizadoono_verificado; el timestamp de las revisiones depende de ese reloj). La frescura del padrón RUC viaja en sus propias respuestas.GET /v1/annotations— anotaciones humanas (valor, actor, justificación, revisión), filtrables por moneda, serie y rango. Jamás forman parte de la vigencia de una serie oficial ni reemplazan una observación: son un registro paralelo, visible aquí y coninclude_annotations=trueen/v1/rates.