DecayStats
DecayStats · API Reference v1

API Reference

Read-only JSON API serving the DecayStats Erosion Index, Decay Index, and per-stratum decomposition for 52 geographies and 10 household archetypes. URL-versioned at /api/v1/. No authentication. Open CORS. Per-IP rate-limited.

Endpoints
17
Geographies
56
Archetypes
10
Rate limit
120/min
5,000/day per IP

Overview

  • Base URL. https://decaystats.com/api/v1
  • Protocol. HTTPS only. All endpoints GET.
  • Content type. application/json; charset=utf-8
  • Versioning. URL-prefixed (/v1/). Backward-incompatible changes get a /v2/ prefix.
  • CORS. Open (*) for all /api/* paths.
  • Provenance. Every response includes methodology_version, basket_version, and generated_at.

Getting started

No API key required. No auth header required. All endpoints are public read-only.

Hello-world request
curl https://decaystats.com/api/v1/health

Authentication

None. Every endpoint is public read-only. No Authorization header, query token, or API key is read by the application. If future write endpoints are added, they will be introduced under a separate path with auth documented inline; the read API will remain unauthenticated.

Rate limits

Global per-IP limits enforced by Flask-Limiter:

  • 120 requests per minute per client IP
  • 5,000 requests per day per client IP

Exceeding either limit returns HTTP 429 with the error body documented in Error format. The retry_after_seconds field indicates suggested back-off (default 60). Higher tiers are not offered; clients requiring sustained throughput should cache locally — responses are stable until the next ingest run.

Response conventions

Every successful response is wrapped in a provenance envelope:

{
  "methodology_version": "2.0.4",
  "basket_version": "1.0.12",
  "generated_at": "2026-05-27T14:22:03.119482+00:00",
  "data": <endpoint-specific payload>
}

Some endpoints add sibling keys alongside data (for example coverage_months on series endpoints).

Numeric types. All index levels and rates are JSON numbers. Internal computation uses Decimal; serialization converts to float at the response boundary.

Period strings. Monthly periods are YYYY-MM. Annual periods are YYYY. Both order lexicographically.

Common query parameters

Accepted on all v1.1 endpoints (archetype, state, series). Ignored on v1.0 endpoints (/index/*, /basket, /methodology).

ParameterTypeDefaultBehavior
basket_version string active version Pin response to a specific locked basket version. Stable downstream consumers should pin.
as_of YYYY-MM-DD none Reserved for v1.1.x point-in-time queries. Currently accepted but not applied — returns same data as omitting the parameter.

Error format

Errors return a JSON body and a matching HTTP status code.

{
  "error": "not_found",
  "message": "Unknown geography 'ZZ'. Valid: 'US' or a USPS 2-letter state code."
}
HTTPerrorTrigger
400(Flask default)Required query parameter missing.
404not_foundUnknown path, archetype, or geography; wrong-level geography for path.
429rate_limitedPer-IP rate limit exceeded. Body includes retry_after_seconds.
500internal_errorUnhandled server-side exception. Body never includes a stack trace.

Endpoints — Health & Status

GET /api/v1/health

Lightweight liveness probe. Reports overall app status, methodology and basket versions, and a single database-connectivity check. Intentionally cheap so it can be polled at high frequency — the per-source data-freshness scan lives at /api/v1/status.

Status values: ok or degraded. degraded when the database connectivity ping fails.

Curl
curl https://decaystats.com/api/v1/health
Example response
{
  "status": "ok",
  "methodology_version": "2.0.4",
  "basket_version": "1.0.12",
  "database": "ok"
}
GET /api/v1/status

Per-source data-freshness readiness report. Each registered source's latest period is compared against its publish-lag SLA and bucketed GREEN / YELLOW / RED. Heavier than /api/v1/health (one query per source); intended for dashboards and monitoring, not high-frequency probing.

Status values: ok or degraded. degraded when any source is blocking-stale or the freshness check itself errored.

Curl
curl https://decaystats.com/api/v1/status
Example response
{
  "methodology_version": "2.0.4",
  "basket_version": "1.0.12",
  "data": {
    "status": "ok",
    "data_freshness": {
      "summary": { "any_blocking": false, "any_warning": false, "checked": 11 },
      "sources": [ { "source": "eia", "status": "GREEN", "days_old": 6, "cadence": "monthly", "max_lag_days": 60 } ],
      "blocking_sources": []
    }
  }
}

Endpoints — Index (v1.0)

National-only surface. Retained for backward compatibility.

GET /api/v1/index/headline

National-Average headline numbers — the public-citation surface.

Fields: as_of_month, long_run_decay_pct, rolling_12_month_decay_pct, long_run_half_life_years, rolling_12_month_half_life_years, long_run_first_month, long_run_last_month, coverage_months, erosion_index.

curl https://decaystats.com/api/v1/index/headline
GET /api/v1/index/archetypes

Latest values for all 10 archetypes side-by-side. Returns an array; is_headline: true marks national_average.

curl https://decaystats.com/api/v1/index/archetypes
GET /api/v1/index/<archetype>/series

Full monthly time series for one archetype at the national level.

Path parameters. archetype: one of national_average, homeowner, renter, working_parent, retiree, income_lowest_quintile, income_second_quintile, income_middle_quintile, income_fourth_quintile, income_highest_quintile.

Observation shape. {period, erosion_index, decay_index_12m_pct, half_life_12m_years}.

curl https://decaystats.com/api/v1/index/retiree/series
GET /api/v1/index/strata

Per-stratum latest price-relative table — machine-readable form of the DecayStats-vs-CPI comparison.

Per row. {stratum, first_period, last_period, latest_relative, coverage_months} anchored at 2000-01 = 1.0.

curl https://decaystats.com/api/v1/index/strata

Endpoints — Geographies

GET /api/v1/geographies

Index of all 52 published geographies, grouped by level (national, states).

Top-level fields. national, states, total_count, published_series_total, level_construction_note.

curl https://decaystats.com/api/v1/geographies

Endpoints — Archetype (v1.1)

Geography- and archetype-first routing per the v1.1 contract.

GET /api/v1/archetype/<archetype>/headline

National headline for one archetype. Same fields as /index/headline plus archetype and geography: "US".

curl https://decaystats.com/api/v1/archetype/working_parent/headline
GET /api/v1/archetype/<archetype>/series

Full monthly time series for one archetype at the national level.

curl https://decaystats.com/api/v1/archetype/renter/series
GET /api/v1/archetype/<archetype>/decomposition

Per-stratum X-ray decomposition. Each row: {stratum, weight, rate_pct, contribution_pp, coverage_months}. Contributions sum approximately to the headline long-run Decay Index.

curl https://decaystats.com/api/v1/archetype/retiree/decomposition

Endpoints — State (v1.1)

USPS 2-letter codes (50 states + DC). National CEX weights × state-specific prices, with documented per-stratum fallback when state-level data is unavailable.

GET /api/v1/state/<code>/coverage

Per-state coverage map. For each stratum: which source band drives the value (state | national), when state-specific data begins, and what share of basket weight is genuinely state-specific.

This endpoint is the §5.8 disclosure surface — readers can see how much of a state headline is genuinely state-resolved versus national-pass-through.

curl https://decaystats.com/api/v1/state/CA/coverage
GET /api/v1/state/<code>/archetype/<archetype>/headline

State × archetype headline. Same shape as the archetype headline endpoint with geography set to the state code.

curl https://decaystats.com/api/v1/state/CA/archetype/retiree/headline
GET /api/v1/state/<code>/archetype/<archetype>/series

Full state × archetype monthly time series.

curl https://decaystats.com/api/v1/state/TX/archetype/working_parent/series
GET /api/v1/state/<code>/archetype/<archetype>/decomposition

Per-stratum X-ray for (state, archetype). Same row shape as the archetype decomposition.

curl https://decaystats.com/api/v1/state/NY/archetype/renter/decomposition

Endpoints — Generic series (v1.1)

GET /api/v1/series

Flexible time-series query. Two modes depending on whether stratum is supplied.

Required query params. archetype (one of 10 slugs).
Optional. geography (default US), stratum (if present, returns per-stratum price relatives), basket_version, as_of.

Archetype mode — full index for one (archetype, geography)
curl "https://decaystats.com/api/v1/series?archetype=retiree&geography=FL"
Stratum mode — price relative for one (stratum, geography)
curl "https://decaystats.com/api/v1/series?archetype=retiree&geography=FL&stratum=healthcare_premium"

Endpoints — Basket & Methodology

GET /api/v1/basket

Locked basket definition. Per-archetype stratum weights, headline-archetype identifier, canonical stratum list, per-stratum source map.

curl https://decaystats.com/api/v1/basket
GET /api/v1/methodology

Methodology version + a short summary of the load-bearing pillars, with links to the canonical documents.

curl https://decaystats.com/api/v1/methodology

Versioning policy

URL versioning. All endpoints live under /api/v1/. Backward-incompatible changes get a new prefix (/api/v2/). The /v1/ surface continues to serve until announced sunset.

What counts as breaking (requires a new prefix):

  • Removing an endpoint.
  • Removing or renaming a field in a successful response.
  • Changing the type of a field.
  • Tightening enum values for an existing parameter.
  • Changing the semantics of an existing field.

What is additive (same /v1/ prefix):

  • Adding a new endpoint.
  • Adding a new field to an existing response.
  • Adding a new accepted enum value (e.g., a new archetype or geography).
  • Adding a new optional query parameter.

Three independent version axes:

AxisFieldBumps on
API contract/v1/ URL prefixBreaking changes
Methodologymethodology_versionAny change to how the index is computed
Basketbasket_versionAny change to the locked basket (items, weights, source-precedence)