Skip to content

API Reference

The FastAPI service exposes a read-only REST API. Interactive documentation (Swagger UI) is available at /docs when the service is running.

Endpoints

Method Path Description
GET / Redirect to /ui/
GET /ui/ Built-in grid dashboard
GET /guide/ Project documentation (this site)
GET /health Service health and cache status
GET /api/current Latest snapshot across all areas
GET /api/snapshot Full dashboard payload: current + all series + gaps in one response
GET /api/{area} 30-day time series for one area
GET /api/gaps Detected data gaps per area/region
GET /docs Swagger UI
GET /redoc ReDoc UI

Valid {area} values: wind, solar, demand, interconnection, co2, frequency, snsp, generation.


GET /health

Returns service health and cache status.

{
  "status": "ok",
  "cached_at": "2026-03-10T14:00:21+00:00",
  "areas_loaded": ["wind", "solar", "demand", "interconnection", "co2", "frequency", "snsp", "generation"]
}
Field Description
status Always "ok" if the service is running
cached_at ISO-8601 UTC timestamp of last successful S3 refresh, or null if not yet refreshed
areas_loaded Areas with non-empty data in the cache

GET /api/current

Returns the most recent non-null reading for every metric. Intended for gauge widgets.

{
  "as_of": "2026-03-10T14:00:21",
  "wind_mw":            {"ALL": 2867.0, "NI": 536.0, "ROI": 2330.0},
  "wind_forecast_mw":   {"ALL": 767.0,  "NI": 107.0, "ROI": 660.0},
  "solar_mw":           {"ROI": 150.0},
  "solar_forecast_mw":  {"ROI": 160.0},
  "demand_mw":          {"ALL": 4513.0, "NI": 648.0, "ROI": 3866.0},
  "frequency_hz":       50.06,
  "snsp_pct":           65.73,
  "co2_t":              {"ROI": 496.0},
  "interconnection_mw": {"EWIC": 0.0, "GRNLK": 440.0, "MOYLE": 34.0, "NET": 474.0},
  "generation_mw":      {
    "FUEL_GAS": 24000.0,
    "FUEL_RENEW": 55000.0,
    "FUEL_COAL": 0.0,
    "FUEL_OTHER_FOSSIL": 3000.0,
    "FUEL_NET_IMPORT": 7000.0
  }
}
Field Unit Notes
as_of Timestamp of last cache refresh
wind_mw MW Latest actual wind by region
wind_forecast_mw MW Latest forecast wind by region
solar_mw MW Latest actual solar by region
solar_forecast_mw MW Latest forecast solar by region
demand_mw MW Latest system demand by region
frequency_hz Hz Latest system frequency (ROI only)
snsp_pct % Latest SNSP (all-island)
co2_t tonnes/hour Latest CO₂ emissions by region
interconnection_mw MW Latest flow per connector — positive = import into Ireland
generation_mw MWh (cumul.) Latest fuelMix snapshot values

Returns 503 if the cache has not yet been populated.


GET /api/gaps

Returns the current gap-detection summary. See Gap Detection for the full description of how gaps are computed.

{
  "generated_at": "2026-04-22T06:00:04Z",
  "window_days": 31,
  "trailing_exclusion_hours": 6,
  "areas": {
    "wind": {
      "ROI": {
        "gap_count": 1,
        "missing_points": 4,
        "ranges": [
          {"start": "2026-04-18T14:00", "end": "2026-04-18T14:45", "points": 4}
        ]
      }
    }
  },
  "backfills": {
    "in_flight": 3,
    "permanent_failure": 0
  }
}

An absent area key means no gaps were detected in that area during the window. An empty payload (no gaps at all) is the expected healthy response.


GET /api/{area}

Returns the full 30-day time series for one area.

{
  "generated_at": "2026-03-10T14:00:00Z",
  "window_days": 33,
  "series": {
    "ROI": [
      {"t": "2026-02-05T10:00:00", "actual": 2330.0, "forecast": 2400.0},
      {"t": "2026-02-05T10:15:00", "actual": 2290.0, "forecast": 2350.0}
    ],
    "NI":  [...],
    "ALL": [...]
  }
}
{
  "generated_at": "2026-03-10T14:00:00Z",
  "window_days": 33,
  "series": {
    "ROI": [
      {"t": "2026-02-05T10:00:00", "value": 3866.0},
      {"t": "2026-02-05T10:15:00", "value": 3901.0}
    ]
  }
}
{
  "generated_at": "2026-03-10T14:00:00Z",
  "window_days": 33,
  "series": {
    "EWIC":  [{"t": "2026-02-05T10:00:00", "value": 0.0}],
    "GRNLK": [{"t": "2026-02-05T10:00:00", "value": 440.0}],
    "MOYLE": [{"t": "2026-02-05T10:00:00", "value": 34.0}],
    "NET":   [{"t": "2026-02-05T10:00:00", "value": 474.0}]
  }
}
{
  "generated_at": "2026-03-10T14:00:00Z",
  "window_days": 33,
  "series": {
    "FUEL_GAS":          [{"t": "2026-03-10T11:00:00", "value": 24000.0}],
    "FUEL_RENEW":        [{"t": "2026-03-10T11:00:00", "value": 55000.0}],
    "FUEL_COAL":         [{"t": "2026-03-10T11:00:00", "value": 0.0}],
    "FUEL_OTHER_FOSSIL": [{"t": "2026-03-10T11:00:00", "value": 3000.0}],
    "FUEL_NET_IMPORT":   [{"t": "2026-03-10T11:00:00", "value": 7000.0}]
  }
}

Resolution notes

Area Resolution Notes
wind, solar, demand, co2, snsp, interconnection 15 min Raw EirGrid API cadence
frequency 1 hour Downsampled from 5-second source data in DuckDB
generation Daily One snapshot per pipeline run

Returns 503 if data for the area is not yet cached, 422 if the area name is invalid.


Error responses

Status Condition
422 Unprocessable Entity Unknown {area} value
503 Service Unavailable Cache not yet populated

Cache behaviour

sequenceDiagram
    participant C as Client
    participant A as FastAPI
    participant M as Cache (memory)
    participant S as S3

    Note over A,S: At startup and every CACHE_TTL_SECONDS
    A->>S: GET summary/{area}/latest.json (×8)
    S-->>A: JSON payloads
    A->>M: update _cache dict

    C->>A: GET /api/demand
    A->>M: read snapshot (lock)
    M-->>A: cached payload
    A-->>C: 200 JSON

The cache is refreshed in a background daemon thread. Read requests acquire a short-lived lock only to snapshot the cache dict reference — they never block on S3 I/O.