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": {
"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.