# Elexon Imbalance Prices - GB System Price Export & Revisions (`thatmike1/elexon-imbalance-prices`) Actor

GB electricity imbalance prices from Elexon BMRS: system sell and buy price, net imbalance volume per settlement period, DST-exact UTC times, and re-publication diffs.

- **URL**: https://apify.com/thatmike1/elexon-imbalance-prices.md
- **Developed by:** [Michal Pšenčík](https://apify.com/thatmike1) (community)
- **Categories:** Business, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.005 / actor start

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Elexon Imbalance Prices - GB System Price Export & Revisions

Export Great Britain's electricity imbalance settlement prices from [Elexon BMRS](https://data.elexon.co.uk/): system sell and buy price, net imbalance volume, and every specified DISEBSP field. Backfill dates for a battery optimisation backtest, then schedule a recent-date watch to capture value revisions and their previous values. No API key or proxy is required.

### Input

An empty input `{}` exports the last three complete settlement days, calculated at run time in `Europe/London`. It produces data on weekends too. To backfill a month:

```json
{
    "startDate": "2026-09-01",
    "endDate": "2026-09-30",
    "mode": "all",
    "watchName": "battery-backtest",
    "maxRows": 20000,
    "maxDays": 400
}
```

| Input       | Default               | Meaning                                                                                                                   |
| ----------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `startDate` | UK today minus 3 days | Inclusive settlement date, `YYYY-MM-DD`.                                                                                  |
| `endDate`   | UK yesterday          | Inclusive settlement date. Future dates are clipped to UK today with a summary note.                                      |
| `mode`      | `all`                 | `all` exports all periods; `changes` exports only new or value-revised periods compared with this watch.                  |
| `watchName` | `default`             | Persistent watch name, 1–128 characters. Reuse it to compare later runs.                                                  |
| `maxRows`   | `20000`               | Delivered row cap; `0` means unlimited.                                                                                   |
| `maxDays`   | `400`                 | Reject ranges longer than this many inclusive requested days, before clipping. Can be reduced, never increased above 400. |

Dates must be valid and `startDate` must be on or before `endDate`. Omit both dates to keep a scheduled task's window moving automatically; fixed dates stay fixed.

### Output

One billable default-dataset row represents one settlement period. JSON, CSV and Excel exports are available through Apify. All dates and period numbers refer to the UK settlement day; all timestamps are exact UTC ISO strings. A normal day has 48 half-hour periods, clocks forward has 46 and clocks back has 50. A BST day's UTC start is 23:00 the preceding date.

These selected fields are from a real 14 October 2026 response (full exports contain all fields listed below):

```json
{
    "settlementDate": "2026-10-14",
    "settlementPeriod": 1,
    "startTime": "2026-10-13T23:00:00.000Z",
    "endTime": "2026-10-13T23:30:00.000Z",
    "periodsInDay": 48,
    "createdDateTime": "2026-10-14T23:44:36.000Z",
    "systemSellPrice": 161.7,
    "systemBuyPrice": 161.7,
    "netImbalanceVolume": -32.18260232638889,
    "publication": "republished",
    "changeType": "new",
    "changedFields": [],
    "previous": null
}
```

Every row includes:

- Identifiers and timing: `settlementDate`, `settlementPeriod`, `startTime`, `endTime`, `periodsInDay`, `createdDateTime`, `retrievedAt`.
- Prices and volume: `systemSellPrice`, `systemBuyPrice`, `netImbalanceVolume`, `reserveScarcityPrice`, `sellPriceAdjustment`, `buyPriceAdjustment`, `replacementPrice`, `replacementPriceReferenceVolume`.
- Source flags: `bsadDefaulted`, `priceDerivationCode`.
- Accepted and adjustment volumes: `totalAcceptedOfferVolume`, `totalAcceptedBidVolume`, `totalAdjustmentSellVolume`, `totalAdjustmentBuyVolume`, `totalSystemTaggedAcceptedOfferVolume`, `totalSystemTaggedAcceptedBidVolume`, `totalSystemTaggedAdjustmentSellVolume`, `totalSystemTaggedAdjustmentBuyVolume`.
- Revision information: `publication`, `changeType`, `changedFields`, `previous`.

Source nulls remain null and numeric precision is preserved. Prices are GBP/MWh; volumes are MWh. Unknown additional API fields are accepted but not exported. Invalid envelopes, missing fields, wrong types, duplicate periods or incorrect UTC starts make that date an error rather than delivering questionable rows.

### Re-publication watches

Elexon initially publishes a period roughly 48 minutes after it starts and re-publishes it roughly a day later. The endpoint returns only the latest publication. `publication` is derived from timing: `republished` means `createdDateTime` is **more than 12 hours after `endTime`**; otherwise it is `first`. This is a heuristic, not a flag supplied by Elexon.

`changeType` compares every exported DISEBSP value field (prices, volumes, flags and derivation code) with the watch snapshot. Settlement identifiers and UTC bounds are validated separately, and are not value fields:

- `new`: the watch has never delivered this period.
- `revised`: any value differs, even if `createdDateTime` is unchanged. `changedFields` lists those field names.
- `republished`: all values are equal, but `createdDateTime` differs. `changedFields` is empty.
- `unchanged`: values and timestamp match. `changedFields` is empty and `previous` is null.

For `revised` and `republished` rows, `previous` contains the old `createdDateTime`, `systemSellPrice`, `systemBuyPrice`, `netImbalanceVolume` and the old value of every field in `changedFields`. It is null for `new` and `unchanged` rows.

The real 15 October 2026 period-21 pair illustrates why this matters: the first publication timestamp was `2026-10-15T09:48:33Z` and the re-publication timestamp was `2026-10-16T09:44:26Z`, with every other source field identical. This is `republished`, not `revised`.

The first `changes` run delivers every available period as `new`. **This baseline is billed per row.** Later runs deliver and bill only `new` and `revised` periods. Timestamp-only re-publications deliver no row: their new timestamps refresh the watch state for free and are counted in the free summary. `all` delivers every period, computes all four change types against existing state and updates snapshots for delivered rows. Repeated `all` runs bill every delivered row again.

For a nightly recent-day watch, schedule this input:

```json
{ "mode": "changes", "watchName": "battery-nightly" }
```

State is stored in a persistent named key-value store, `elexon-watch-` plus the first 32 hex characters of the SHA-256 of the exact watch name. `SUMMARY.watchStore` identifies it. The version-2 state stores every exported DISEBSP value field plus `createdDateTime`, retaining only the latest 60 distinct seen settlement dates. Older version-1 state is treated as absent, so its next `changes` run delivers a fresh billed baseline. Re-reading older discarded dates starts a new baseline for those dates. Use one run per watch at a time; concurrent runs can overwrite each other's state. To reset, choose a new watch name or delete its `STATE` record. Each Apify account owns its stores.

New or revised values advance state only after their rows are confirmed stored. `maxRows`, a spending cap or failed delivery leaves unreported new/revised periods eligible for the next run. In `changes` mode, equal-value timestamp refreshes can advance state without a row, including when delivery is capped, because no value change needs reporting. If data storage succeeds but state persistence fails, a retry may deliver and bill those rows again. Missing publications between runs cannot be recovered because Elexon serves only its latest snapshot.

### Free coverage summary and errors

The default key-value store's `SUMMARY` record is free and never appears in the default dataset. It contains the requested and effective date range, one entry per fetched date with expected/received periods, rows delivered, `rowsCutByMaxRows`, `rowsCutBySpendingLimit`, `countsByChangeType` for delivered rows, `observedCountsByChangeType` for all fetched periods before caps or filtering, notes, attribution and the licence link. Thus a timestamp-only re-publication in `changes` mode increments `observedCountsByChangeType.republished` but has no delivered row or item charge.

Date status is `complete`, `partial` (today, or a closed day with missing periods), `empty` (no published data), or `error` with a reason. Future dates beyond the clipped range are not fetched. A request that exhausts retries, HTTP 400 or malformed body affects only that date; the actor continues and fails after saving the summary only if every fetched date errored. Storage and state failures abort the run. A wholly future range succeeds with zero rows and a clipping note.

There is one sequential request per settlement date, at least 500 ms apart. HTTP 429, 5xx and transport failures get up to four retries, exponential backoff and `Retry-After`. Today's coverage is provisional and yesterday can still be re-published. Repeat recent days rather than assuming a closed settlement day is final.

### Pricing

**$0.001 per delivered settlement-period row**, plus **$0.005 per actor start**. Both are Apify's built-in events: `apify-default-dataset-item` and `apify-actor-start`. The platform bills the start event even when a run delivers no rows. A typical three-day default run delivers 144 rows: $0.149 including the start. Clock-change windows differ by two rows. The free summary has no dataset-item charge. Console monetization settings must be configured before publishing; the repository pricing file alone does not configure billing.

### Licence and attribution

Contains BMRS data © Elexon Limited copyright and database right 2026.

[BMRS data copyright licence](https://www.elexon.co.uk/bsc/data/balancing-mechanism-reporting-agent/copyright-licence-bmrs-data/). Commercial use is allowed. **Keep this attribution and licence link whenever you republish the data**, including in sub-licences and any further sub-licences. Do not imply Elexon endorses you or this actor. Each run's summary includes the attribution with the current year and the licence URL.

For support, open an issue on the actor's Apify Store page with the run ID, dates and affected periods. Do not post credentials. Public task and Store listing are pending publishing; current implementation and verification details are in the repository.

# Actor input Schema

## `startDate` (type: `string`):

Inclusive UK settlement date, YYYY-MM-DD. Omit for three days before today in Europe/London.

## `endDate` (type: `string`):

Inclusive UK settlement date, YYYY-MM-DD. Omit for yesterday in Europe/London. Future dates are clipped to today.

## `mode` (type: `string`):

All delivers every period. Changes bills only new or value-revised periods; timestamp-only re-publications refresh state for free. Its first run delivers a billed baseline.

## `watchName` (type: `string`):

Names persistent revision state. Reuse this name for later runs; use a different name to start a new baseline. Run one watch at a time.

## `maxRows` (type: `integer`):

Safety cap on delivered rows. 0 means unlimited. Unstored rows remain unseen for later changes runs.

## `maxDays` (type: `integer`):

Reject an inclusive requested range longer than this many days, before future clipping. Maximum supported value is 400.

## Actor input object example

```json
{
  "mode": "all",
  "watchName": "default",
  "maxRows": 20000,
  "maxDays": 400
}
```

# Actor output Schema

## `rows` (type: `string`):

No description

## `summary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("thatmike1/elexon-imbalance-prices").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("thatmike1/elexon-imbalance-prices").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call thatmike1/elexon-imbalance-prices --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,thatmike1/elexon-imbalance-prices"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/TB89gv8Kr7Fv56Xv0/builds/6Ud6yioeCM1e39Zma/openapi.json
