v0.2.0 — i18n, UI, API keys, admin y métricas
Consolidación grande sobre v0.1.0. La API ya no es solo "ofrece datos" — ahora tiene gestión de keys, métricas por usuario, documentación bilingüe y un security tab completamente limpio.
🟢 En producción
- API: https://tasa-bcv-api-production.up.railway.app
- Docs (auto-detect español/inglés): https://tasa-bcv-api-production.up.railway.app/docs
✨ Lo nuevo
Documentación bilingüe
/docs— español por defecto/docs/en— inglés- Auto-detect por
Accept-Language(si tu navegador prefiere inglés, redirige automáticamente) /openapi.json(ES) y/openapi-en.json(EN), este último traducido en runtime- Override manual con
?lang=eso?lang=en
API Keys (tier Free)
POST /v1/keys/register— auto-registro público, instantáneo (email + nombre + propósito opcional)GET /v1/keys/me— metadata de mi keyGET /v1/keys/me/usage?days=30— histograma diario de uso (ideal para dashboards)DELETE /v1/keys/me— auto-revoke- 300 req/min con key vs 30 req/min anonymous
Admin
GET /v1/admin/keys— listar todas las keys (activas + revocadas)POST /v1/admin/keys/{id}/revoke— soft-delete por idPOST /v1/admin/trigger-ingest— disparar ingest manual (ya existía)
Shapes JSON limpios
La respuesta default es ultra-plana:
{ "date": "2026-05-14", "usd": 510.7873, "eur": 598.12171255 }Cuando hay propagación, aparece propagated_currencies: ["USD", "EUR"]. Campos internos (source_file, fetched_at, currency_pair) ya no se exponen al consumidor.
Code samples embebidos
Cada endpoint trae snippets listos en curl, JavaScript (fetch), TypeScript, Python (requests), PHP (cURL), Go (net/http) — visibles como tabs en /docs.
UI de Scalar pulida
- Theme purple con tipografías Inter + JetBrains Mono
- Tags reorganizados (rates → keys → system → admin)
- Ejemplos múltiples por endpoint (día normal, fin de semana, propagación parcial, pre-redenominación)
- Server URL declarado en el spec (try-it desde docs funciona)
Errores unificados
Antes los errores de validación Zod devolvían un shape distinto a los AppError. Ahora todo usa el mismo envelope:
{
"error": "Entrada inválida",
"code": "VALIDATION_ERROR",
"details": { "issues": [{ "path": "date", "message": "...", "code": "custom" }] }
}🛡️ Seguridad
- 0 alertas abiertas en Security tab (code scanning, secret scanning, dependabot)
- CVE crítico de Drizzle (SQL injection en identifiers) parcheado: 0.36 → 0.45
- Headers HTTP endurecidos: HSTS 1 año + preload, CSP estricto, Permissions-Policy denegando 8 features
- Dependabot + GitHub Actions CodeQL + Secret Scanning + Push Protection: todo activado
- Branch protection en
maincon required status checks
🧪 Tests
- 93 tests pasando, 0 skipped (subimos de 71 + 8 skipped en v0.1.0)
- Tests de DB migrados de pg-mem a
@testcontainers/postgresqlcon Postgres real - Cada PR debe pasar el job
typecheck + tests + buildantes de mergear
📦 Datos
- Total filas en DB: ~6,000+ (rates reales + propagaciones)
- USD desde 2016-01-04 (2,500 registros reales)
- EUR desde 2020-03-27 (~1,400 registros reales)
🗺️ Próximos pasos (planeados)
- Dominio propio cuando haya presupuesto
- Tier Pro con monetización (Stripe/Plisio/PayPal, por decidir)
- Endpoint
/metricspara observabilidad - Más ejemplos de SDK en el README
📄 Licencia
No estamos afiliados al Banco Central de Venezuela.