Observatory · health economics

Salus

Cost, burden, systems, financing

Health economist-first observatory: cost-effectiveness analysis, disease burden and costs, health systems performance, and health financing. Four engines built on WHO, Our World in Data, World Bank, IHME, Gavi, and Global Fund data. Multi-entity warehouse, structured panels, ~195 countries. Default port 8004.

53
Collectors
4
Engines
~195
Countries
8004
Default port

What this observatory tracks

CEA engine

Cost-effectiveness ratios, intervention cost data, DALYs averted per dollar, threshold analyses. Structured for cross-country and cross-intervention comparison in resource allocation research.

Burden and cost engine

Disease-specific mortality, morbidity, DALY estimates from IHME and WHO. Direct and indirect cost of illness, catastrophic health expenditure, out-of-pocket spending shares.

Systems engine

Health workforce density, hospital bed ratios, UHC service coverage index, essential medicine availability, quality-of-care indicators. The capacity and performance layer.

Financing engine

Total health expenditure as share of GDP, government versus private splits, external health aid flows (Gavi, Global Fund, bilateral), pooling and risk protection metrics.

4 engines, 53 collectors

Collectors pull from WHO GHO, Our World in Data, World Bank WDI, IHME GBD, Gavi, and Global Fund APIs on staggered UTC schedules. Excel-format sources are handled via openpyxl. All data lands in a single SQLite warehouse with consistent schema and topic tags.

CEA
burden / cost
systems
financing

Production surface

FastAPI and Starlette middleware: gzip, optional CORS, security headers, X-Request-ID on every response, optional JSON log lines via LOG_JSON. Readiness runs SELECT 1 against SQLite. Optional GET /metrics exposes salus_http_* counters and latency histograms.

  • Warehouse: WAL SQLite, tuned page cache and mmap, optimize on shutdown.
  • Ingestion: httpx async client, APScheduler for staggered UTC collection, openpyxl for Excel sources.
  • OpenAPI: /docs and /redoc on the running service.

Route map (read-only data)

All under /api/data/. Query parameters support filtering, year windows, pagination, and multi-series panels. Admin routes under /api/admin/ require X-Admin-Key when configured.

GET /api/data/sources: filter, sort, paginate registered sources.
GET /api/data/series/{code}: observations for one series; optional country and year range.
GET /api/data/series/{code}/summary: means, extremes, latest values.
GET /api/data/series/{code}/persistence: AR(1), half-life, jump ratio for one country.
GET /api/data/series/{code}/features: YoY growth, rolling vol, shock flags.
GET /api/data/series/{code}/comparators: cross-country ranking for the series.
GET /api/data/country/{iso3}: all series for one economy; optional topic or year.
GET /api/data/country/{iso3}/topics: coverage by topic.
GET /api/data/panel: long-format multi-series extract with balancedness diagnostics.

What operators should know

Fifty-three collectors across four engines. Some sources (IHME GBD, WHO GHO bulk) deliver large payloads. The client spaces requests to respect rate limits. For production, set a real admin secret, decide whether metrics scrape is internal-only, and back up the SQLite file with a quiet window or the SQLite backup API.

The exhaustive manual (schema DDL, every query parameter, troubleshooting, deployment notes) lives in the repo:

salus/README.md

Start the observatory

From the salus/ directory:

make setup && make collect-all && make serve

Integration tests against live APIs:

SALUS_LIVE_API=1 uv run pytest tests/integration -q

Then open http://127.0.0.1:8004/docs or call /api/data/sources.