Skip to content

feat(seeds): OECD SDMX — parked pending concrete consumer #3025

@koala73

Description

@koala73

Background

OpenEcon exposes the full OECD SDMX 3.0 API (no key required). WorldMonitor currently has zero OECD coverage. OECD publishes governance, labor, tax, education, and health dimensions across 38 OECD members.

Rescope (2026-04-13): hold for a concrete consumer — do NOT seed yet

Review across issues #3025#3028 placed OECD lowest priority. Rationale:

  • Coverage ceiling (38 members) conflicts with WorldMonitor's "world coverage, never subset" principle — any OECD-only data point would leave every non-OECD country showing a blank tile or (worse) force a scorer to gate a dimension on OECD-only inputs.
  • Most indicators duplicate existing seeds: GDP / CPI / unemployment / fiscal balances are already covered by IMF WEO (expanded further in feat(seeds): expand IMF WEO (labor, growth, external) — priority #1, all consumers exist #3027). Tax revenue %GDP is already seeded via IMF GGR_G01_GDP_PT.
  • No current consumer in the codebase: the resilience dimension registry (server/worldmonitor/resilience/v1/_indicator-registry.ts) has no slot waiting on OECD education/R&D/health %GDP inputs; no panel today renders those fields.

This issue stays OPEN as a parking lot for future OECD use-cases that aren't yet scoped. When a concrete consumer emerges, re-evaluate.

Candidate future use-cases

Track these here rather than close. Each needs a concrete panel or scoring input before seeding:

OECD dataflow Future consumer trigger
OECD.SDD.TPS / DSD_PDB@DF_PDB_LV (labor productivity levels) Only OECD source with direct substitute; revisit if a "competitiveness" dimension lands in resilience
OECD.STI.PIE (R&D % GDP) Revisit if an "innovation capacity" dimension is added to resilience scoring
OECD.SDD.EDSTAT (education spending % GDP) Revisit if a social-investment dimension is added
OECD.SDD.TPS / DSD_SHA (health expenditure) Revisit if healthPublicService expands beyond current WHO/FSI inputs
OECD.ELS.SAE / DSD_HW@DF_AVG_ANN_HRS_WKD (average hours worked) Only OECD source; revisit if a labor-market tile lands for OECD-only overlay
Governance indicators (product market regulation, rule-of-law index) V-Dem / WGI already cover this more broadly

When scoping any of the above: include explicit behavior for non-OECD countries (hide the tile, not blank it), and pair with a world-coverage alternative wherever possible.

Technical notes (kept for future reference)

  • Base URL: https://sdmx.oecd.org/public/rest
  • Endpoint: /data/{agency},{dsd_id}@{dataflow_id},{version}/{filter_key}?dimensionAtObservation=AllDimensions&startPeriod=2019&endPeriod=2025
  • Accept header: application/vnd.sdmx.data+json; version=2.0.0
  • Country format: ISO3 (USA, DEU, GBR). Aggregates: OECD, EA19, EU27_2020, G7, G20
  • Filter key: dot-separated dimension indices (e.g., .USA..........). Position varies per DSD — must introspect the DSD first via /datastructure/{agency}/{dsd_id}/{version} with Accept: application/vnd.sdmx.structure+json; version=2.0.0
  • Rate limit: ~60 req/hr per IP. Circuit breaker + exponential backoff (3 attempts: 3s, 6s, 12s with jitter). Timeout 50s per request (OECD is slow).
  • No auth required

Sample working URL

curl -H "Accept: application/vnd.sdmx.data+json; version=2.0.0" \
  "https://sdmx.oecd.org/public/rest/data/OECD.SDD.NAD,DSD_NAMAIN10@DF_TABLE1_EXPENDITURE,1.0/.USA..........?dimensionAtObservation=AllDimensions&startPeriod=2020&endPeriod=2025"

Response parse pattern

const observations = json.data.dataSets[0].observations;
const dims = json.data.structures[0].dimensions.observation;
const timeValues = dims.find(d => d.id === 'TIME_PERIOD').values;
for (const [obsKey, obsVal] of Object.entries(observations)) {
  const indices = obsKey.split(':').map(Number);
  const timePeriod = timeValues[indices.at(-1)].id;
  const value = obsVal[0];
}

Gotchas

  • OECD does not reliably populate the position field on dimensions — map by ID, not index alone
  • DSDs vary per dataflow — do not assume a shared filter-key shape

Acceptance criteria (for future activation)

When this issue is un-parked:

  • A specific resilience dimension or panel tile must be named as the consumer BEFORE any seeder lands
  • Non-OECD country rendering path is explicit (hidden vs blank) and doesn't degrade existing panels
  • Pair with a world-coverage fallback source where possible

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: APIBackend API, sidecar, keysenhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions