npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@uk-energy/elexon

v0.3.0

Published

Typed client for Elexon Insights (BMRS): bid-offer acceptances, bid-offer data, physical notifications, generation by fuel, BMU reference, system prices. Part of uk-energy-sdk.

Readme

@uk-energy/elexon

Typed client for Elexon Insights (BMRS): bid-offer acceptances and prices, physical notifications, generation by fuel, BM unit reference data and system prices. No API key. Types are generated from Elexon's OpenAPI document, so field names and nullability are the API's own.

Contains BMRS data © Elexon Limited copyright and database right 2026. Keep this line wherever you show the data. The BMRS data licence allows copying, adapting and selling derived works; it does not imply endorsement.

Python mirror: uk_energy.elexon in the uk-energy distribution.

pnpm add @uk-energy/elexon

Usage

import { ElexonClient } from "@uk-energy/elexon";

const ex = new ElexonClient();

// Bid-offer acceptances for one hour, every unit. soFlag=true bids on wind units are curtailment.
const boalf = await ex.datasets.boalf({ from: "2026-10-15T00:00Z", to: "2026-10-15T01:00Z" });
const curtailed = boalf.filter((r) => r.soFlag && r.bmUnit?.endsWith("W-1"));

// Or by settlement period: from/to are then dates, not instants.
await ex.datasets.boalf({
  from: "2026-10-15", to: "2026-10-15",
  settlementPeriodFrom: 1, settlementPeriodTo: 4,
  bmUnit: ["T_ARCHW-1"],
});

// Submitted bid/offer prices (BOD). Wind units bid negative: they are paid to turn down.
const bod = await ex.datasets.bod({ from: "2026-10-15T00:00Z", to: "2026-10-15T01:00Z", bmUnit: ["T_ARCHW-1"] });

// Generation by fuel type, 5-minute resolution
const fuel = await ex.datasets.fuelinst({ publishDateTimeFrom: "2026-10-15T00:00Z", publishDateTimeTo: "2026-10-15T00:30Z" });

// Every BM unit, with fuel type and capacity
const units = await ex.reference.bmUnits();
const wind = units.filter((u) => u.fuelType === "WIND");           // 287 on 20 Oct 2026

// System prices for a settlement day or one period
const prices = await ex.balancing.systemPrices("2026-10-15");       // 48 rows
const sp1 = await ex.balancing.systemPrices("2026-10-15", 1);

// Any other /datasets/{code}/stream endpoint, typed by code
const mels = await ex.datasets.stream("MELS", { from: "2026-10-15T00:00Z", to: "2026-10-15T01:00Z" });

Curtailment

// Every WIND unit (287), one settlement day: the reference list + 3 requests per 200 units (BOAV, EBOCF, BOALF)
const day = await ex.curtailment.forDay("2026-10-15");
day.total;            // { curtailedMwh, soFlaggedMwh, cashflowGbp, units, acceptances }
day.units[0];         // biggest curtailed unit: { bmUnit, leadPartyName, gspGroupId, capacityMw, curtailedMwh, cashflowGbp, … }
day.periods;          // one row per (unit, settlement period) with volume, £ and SO flag counts

// A few units, a few periods
await ex.curtailment.forDay("2026-10-15", { bmUnit: ["T_AKGLW-3"], settlementPeriodFrom: 1, settlementPeriodTo: 8 });

// The feeds behind it
await ex.balancing.acceptanceVolumes("bid", "2026-10-15", { bmUnit: ["T_AKGLW-3"] }); // BOAV, MWh per acceptance per period
await ex.balancing.cashflows("bid", "2026-10-15", { bmUnit: ["T_AKGLW-3"] });         // EBOCF, £ per unit per period
await ex.reference.units({ fuelType: ["WIND"], gspGroupId: ["_P", "_N"] });           // Scottish wind units

Octopus's "Wasted Wind" figure

const day = await ex.curtailment.wastedWind("2026-09-30");
day.total; // { curtailedMwh: 124226, curtailmentCostGbp: 4040737, turnUpMwh, turnUpCostGbp: 25986249, totalCostGbp: 30026986 }
day.periods[19]; // SP20 (09:30 BST): 2,415.70 MWh, £72,256.84 to switch off, £506,067.61 to replace

wastedwind.energy's definition, reproduced from the settlement bid and offer stacks (balancing.settlementStack): SO-flagged wind bids priced at their bid price, plus the curtailed volume re-bought from the cheapest non-SO offers in the same period. Two requests per settlement period. It is a definition, not settlement truth — see examples/ for a month reproduced to 0.2% and a comparison with the settled view below.

A curtailment is a bid acceptance: NESO accepts the unit's (negative-priced) bid to reduce output, so the unit is paid to turn down and cashflowGbp is positive. soFlaggedMwh is the share from acceptances BOALF marks soFlag: true — system (constraint) actions rather than energy balancing. On 15 Oct 2026, AKGLW-3 was curtailed in 42 of 48 periods for 1,019 MWh and £49.5k, all SO-flagged. aggregateCurtailment() is exported for callers with their own rows.

Every method is async and returns the API's rows unwrapped: /stream endpoints are bare arrays, /datasets/{code} and /balancing/* endpoints come back as { data, metadata } and only data is returned. A response of the wrong shape throws UkEnergyError; an HTTP error throws HttpError with the API's RFC 9110 problem details in body.

Surface

| Namespace | Methods | | --- | --- | | datasets | stream(code, query), boalf, bod, fuelinst, pn(date, sp, filter), pnStream, mels, mils | | reference | bmUnits(), fuelTypes(), units({ fuelType?, gspGroupId?, leadParty? }) | | balancing | systemPrices(date, sp?), marketIndex, acceptances(date, sp, filter), bidOffers(date, sp, filter), physical(dataset, date, sp, filter), acceptanceVolumes(bidOffer, date, filter), cashflows(bidOffer, date, filter), settlementStack(bidOffer, date, sp) | | curtailment | forDay(date, { bmUnit?, fuelType?, settlementPeriodFrom?, settlementPeriodTo? }), wastedWind(date, { settlementPeriodFrom?, settlementPeriodTo?, fuelType?, concurrency? }) |

Row types are exported: BoalfRow, BodRow, FuelinstRow, PnRow, MelsRow, BmUnit, SystemPriceRow, MarketIndexRow, AcceptanceRow, BidOfferRow, PhysicalRow, plus the SettlementWindow / UnitFilter / FuelinstQuery argument types; the full generated paths/components are under @uk-energy/elexon as well for anything not wrapped.

Things to know about the API

  • Settlement time is local. A settlement day starts at Europe/London midnight; in BST, SP1 of 15 Oct starts at 2026-10-14T23:00Z. Days have 48 periods, 46 on the spring clock change and 50 on the autumn one. @uk-energy/core exports settlementPeriodAt, settlementPeriodStart and friends for the conversion.
  • from/to change meaning on BOALF/BOD: instants normally, but calendar dates when settlementPeriodFrom/To are given.
  • BOALF is a volume of levels, not a list of curtailment events. One hour across all units is ~1,650 rows; one settlement period of acceptances is ~730. Filter by bmUnit where you can.
  • reference/bmunits/all is ~3,100 units with capacities as strings ("114.000"), and gspGroupId is null for 235 of the 287 wind units (transmission-connected), so GSP group cannot place most wind farms on a constraint boundary. 48 wind units have no elexonBmUnit; unitId() falls back to nationalGridBmUnit, which the API accepts as a filter too. The list also has duplicates (T_WLNYO-4 twice); curtailment.forDay de-duplicates ids.
  • Unit filters can be long. The API accepted all 287 wind ids in one 4.9 KB URL; the client splits lists at MAX_UNITS_PER_REQUEST (200) anyway. Unfiltered daily BOAV is ~9 MB.
  • Rate limits: none published and none observed (310 requests at ~120 rps on 20 Oct 2026, all 200). The client defaults to 10 rps / burst 20 per host, shared across clients in the process; pass rateLimit to change it.
  • Errors are RFC 9110 problem details with a 400 for a from later than to or a bad dataset code.

Options

new ElexonClient({
  rateLimit: { requestsPerSecond: 10, burst: 20 }, // default
  cache: new MemoryCache(),                          // optional; TTL defaults to 5 minutes
  cacheTtlMs: 5 * 60 * 1000,
  retry: { retries: 3 }, timeoutMs: 30_000, fetch,   // forwarded to @uk-energy/core HttpClient
});

Regenerating the types

openapi/insights-v1.json is a snapshot of https://data.elexon.co.uk/swagger/v1/swagger.json. Replace it and run pnpm --filter @uk-energy/elexon generate (TypeScript) and uv run python scripts/gen_elexon_types.py from python/ (Python TypedDicts).