ED.UNIT.RANGE
Generation time series for one production unit — average power (MW) or total energy (MWh).
Signature
ED.UNIT.RANGE(up, [start], [end], [agg], [headers], [noDate], [unit])Description
Time series of generation for a single production unit. ENTSO-E A73 publishes at native 15-min cadence. Aggregation is controlled by agg: 0=native, 5min/10min/15min, 1=hourly, 2=daily (default), 3=monthly, 4=quarterly, 5=semiannual, 6=annual, 7=total. agg=0/RAW returns the real 15-min grid (96 slots a day, reconstructed from the run-length document) and agg=7/T returns a single total value — same semantics as ED.RANGE.
**Unit (unit parameter):** by default each bucket holds the mean power in MW over the bucket, weighted by how long each published value was in force. That is the right number for capacity-factor and dispatch analysis but it is NOT the energy generated — "annual generation 800 MW" does not mean 800 MWh, it means 800 MW × 8760 h ≈ 7 TWh. Pass unit="MWh" for the energy delivered in each bucket: the integral of the same curve, so it agrees with the MW figure by construction. The unit_generation table is 100% MW-stored, so unit="MWh" always works — there is no already-MWh edge case (unlike prices or pre-MWh metrics in ED.RANGE).
**How the 15-min data is read (important).** ENTSO-E publishes A73 with curveType A03 — run-length encoded. A position missing from the document means *"the same value as the previous point"*, not zero and not "no data": a plant sitting at 985 MW all day may publish two points, not 96. EnergyData stores the document exactly as published and expands it at read time — every point is weighted by how long it was in force, so MW and MWh are two views of the same curve and can no longer contradict each other. Averaging the stored points as if they were equal samples over-reports MW by up to +777 % on a CCGT; the older per-hour-average-then-sum route under-reported MWh by up to 74 % because it dropped hours that published no point. If you pull per-plant data from another provider, check which of the three you get.
**coverage_pct.** The REST response carries a coverage_pct (0-1) for the whole window plus a coverage array per bucket: the share of each period actually backed by published data. A value is carried forward only to the end of its own UTC day's publication — never across midnight, never backwards, never past the point where that day's data stops — so an ingest gap stays a gap with a low coverage_pct instead of turning into a plausible-looking number, and a bucket with nothing behind it is omitted rather than filled. Expect 1.000 for recent data; about 0.958 between 2025-05-31 and 2026-04-12 (hour 23 UTC was not collected) and about 0.240 before 2025-05-31 (an upstream resolution bug compressed each day into its first ~6 hours). Windows spanning those periods are still affected — coverage_pct is how you spot it.
**Calendar-bucket edge.** With agg=2/D and coarser, a value in force across civil midnight is credited entirely to the bucket it started in (about 1 % of intervals). It is immaterial for plants that run around the clock and visible only for units that switch off overnight; agg=1/H and agg=0/RAW are unaffected. If you need calendar-day energy at settlement precision, aggregate the hourly series yourself.
A window with no data returns #N/A, never 0: a zero here would read as "the plant produced nothing", which is a different — and false — statement.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| up* | string | — | Unit code (e.g. ALZ1). |
| start | date | — | Start. Defaults: omit end for today (Madrid, with a small overnight cutoff), omit start for 30 days before end. |
| end | date | — | End. Omit for today. |
| agg | 0-7 | string | 2 (daily) | Aggregation is controlled by agg: 0=native, 5min/10min/15min, 1=hourly, 2=daily (default), 3=monthly, 4=quarterly, 5=semiannual, 6=annual, 7=total. 0/RAW returns the real 15-min grid, reconstructed from the run-length document (with unit="MWh" it returns the hourly MWh series — the smallest meaningful energy bucket); 7/T returns a single total value instead of a spill, or #N/A if the window holds no data. |
| headers | 0 | 1 | 0 | Set 1 for header row. |
| noDate | 0 | 1 | 0 | Set 1 to omit the period column. |
| unit | "MW" | "MWh" | "MW" | Output unit. "MW" returns the duration-weighted mean power in each bucket (default). "MWh" returns the energy delivered in each bucket — the integral of that same curve. |
* Required.
Returns
Spill array — [period, value] rows (agg=7/T returns a single value, or #N/A if the window holds no data). Buckets with no data behind them are omitted rather than returned as 0. Value unit depends on unit (MW or MWh).
Examples
=ED.UNIT.RANGE("VAL2", "2025-01-01",, 3)— Vandellós II monthly average power (MW)=ED.UNIT.RANGE("ALZ1", "2025-07-01", "2025-07-01", 1)— Almaraz I hourly for one day (MW)=ED.UNIT.RANGE("ALZ1", "2025-07-01", "2025-07-01", 0)— Almaraz I 15-min grid for one day (96 rows in MW, run-length expanded)=ED.UNIT.RANGE("BES5", "2020-01-01",, 6, 1)— Beselga 5 annual with headers (MW)=ED.UNIT.RANGE("VAN2", "2025-01-01",, 6, , , "MWh")— Vandellós II annual energy delivered (MWh)=ED.UNIT.RANGE("ALZ1", "2025-01-01", "2025-12-31", 3, , , "MWh")— Almaraz I monthly energy 2025 (MWh)=ED.UNIT.RANGE("ALZ1", "2025-01-01", "2025-12-31", 7, , , "MWh")— Almaraz I total 2025 generation (MWh) — single value, no spillNotes
- MW and MWh are two views of one curve: MW is Σ(value × time in force) ÷ Σ(time in force), MWh is Σ(value × time in force) ÷ 3600. They agree by construction — before this they could differ by ×2 to ×8 on the same series.
- The
unit_generationtable is 100% MW-stored, sounit="MWh"always works — no 400 error like for prices (EUR/MWh) or pre-MWh metrics inED.RANGE. - With
agg=0/RAWandunit="MWh"the route returns the hourly MWh series rather than per-15-min energy (mirrorsED.RANGE: per-sample energy deltas add cost without analytical gain). - Several ENTSO-E EICs can map to one
up_code. Sub-units that publish **at the same time** (some combined cycles report one EIC per turbine) are **added together**, so the plant reads as the plant. EICs that simply **replace one another** over time are stitched into a single timeline and averaged over it, so a hand-over does not halve the value. Residual: on a handful of days a plant EIC and its sub-unit EICs both publish, and those days count both. - The underlying REST endpoint (
/api/data/unit/range) also acceptstz=(madriddefault /cet/utc) with the same semantics as/api/data/range, and returnscoverage_pctplus a per-bucketcoveragearray; the Excel function does not expose atzargument yet.