# UK Carbon Intensity & Low-Carbon Windows (`cliqtomedia/uk-carbon-intensity`) Actor

Half-hourly carbon intensity (gCO2/kWh) for Great Britain, its grid regions and UK outward postcodes from the NESO Carbon Intensity API: forecast, actual, index and generation mix, plus the lowest-carbon time windows for EV charging, heat pumps and other flexible loads.

- **URL**: https://apify.com/cliqtomedia/uk-carbon-intensity.md
- **Developed by:** [Cliqto Media](https://apify.com/cliqtomedia) (community)
- **Categories:** Developer tools, Business
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.70 / 1,000 result rows

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

Get the half-hourly carbon intensity of electricity (gCO2/kWh) for Great Britain, its grid regions and UK postcodes from the Carbon Intensity API of the National Energy System Operator (NESO). Each half-hour has a forecast, an actual value (national only), an index and the generation mix. The Actor also finds the **lowest-carbon time windows** for EV charging, heat pumps, batch jobs and other flexible loads.

No login or API key is needed. The default run (Great Britain, London and Scotland for the next 24 hours) takes about 5 seconds, returns 153 rows (144 half-hours plus 9 ranked windows) and costs about $0.16 on the Free plan.

![UK Carbon Intensity & Low-Carbon Windows: choose areas and time, get every half-hour of grid CO2 with fuel mix, and get the best low-carbon windows](https://api.apify.com/v2/key-value-stores/wFqo0WUJF7KVBMQWn/records/hero-20261012b.png)

### Table of contents

- [What it does](#what-it-does)
- [Quick start](#quick-start)
- [Use cases](#use-cases)
- [Input](#input)
- [Output](#output)
- [Pricing and limits](#pricing-and-limits)
- [API, export and integrations](#api-export-and-integrations)
- [Troubleshooting](#troubleshooting)
- [FAQ](#faq)
- [Source and licence](#source-and-licence)
- [Support](#support)

### What it does

The Actor reads the public Carbon Intensity API run by the National Energy System Operator (NESO). It does three things:

1. It gets every half-hour in your time range for each area you choose: Great Britain, any of the 17 NESO regions, and up to 5 postcodes.
2. It turns each half-hour into one clean row with UTC times, forecast and actual intensity, the index and the fuel mix.
3. It finds the best non-overlapping windows of the length you choose (for example 3 windows of 2 hours), ranked by the average forecast intensity.

#### Good fit

- You plan EV charging, heat pumps, batteries or batch jobs and need the greenest hours in the next day or two.
- You report on grid carbon intensity for a past day or week (ESG, energy dashboards).
- You compare regions or sites by postcode for the same time range.

#### Not a fit / limits

- It does not calculate the carbon footprint of your business and does not issue certificates or guarantees of origin.
- Great Britain only. Northern Ireland is not covered by the source.
- Regions and postcodes have forecasts only. The source has no regional actual values.
- Forecasts reach about 48 hours ahead. History starts in 2017–2018.

### Quick start

1. Open the **Input** tab.
2. Keep the prefilled input: Great Britain, regions 13 (London) and 16 (Scotland), from `now` to `+24h`.
3. Optionally add a postcode such as `M1` or `EH1 1YZ`.
4. Click **Start**.
5. In the Output tab, open **Low-carbon windows only** for the ranked windows, or **All rows** for every half-hour (the `recordType` column says `period`, `window` or `area_status`).

Expected result: one row per half-hour per area, plus the ranked windows for each area.

### Use cases

| Buyer job | Input path | Output entity | Key fields | Next integration |
| --- | --- | --- | --- | --- |
| Schedule EV charging or a heat pump | `postcodes`, `windowDurationHours`, `windowCount` | window | `from`, `to`, `averageForecastIntensity`, `windowRank` | Your scheduler reads the rank 1 window |
| Daily ESG or energy report | `startTime: "yesterday"`, `includeNational` | period | `actualIntensity`, `forecastIntensity`, `generationMix` | Export CSV/Excel or Google Sheets |
| Compare regions | `regions` | period | `regionName`, `forecastIntensity`, `intensityIndex` | Dashboard or BI tool |

### Input

#### Input fields

| Field | Type | Default / prefill | Allowed values | Effect |
| --- | --- | --- | --- | --- |
| `includeNational` | boolean | `true` | — | Great Britain series. This is the only series with actual values. |
| `regions` | array of strings | prefill `["13", "16"]` | Region IDs `"1"`–`"17"` only | 1–14 are distribution network regions (13 London, 1 North Scotland, …), 15 England, 16 Scotland, 17 Wales. |
| `postcodes` | array of strings | — | Up to 5 UK postcodes | Only the outward part is used and kept in the output (`SW1A 1AA` → `SW1A`). |
| `startTime` | string | `now` | `now`, `today`, `yesterday`, `tomorrow`, `YYYY-MM-DD` (a UK day), or an ISO date-time | ISO without an offset is read as UTC. The start is rounded down to the half-hour. |
| `endTime` | string | empty | `+Nh`, `YYYY-MM-DD`, or an ISO date-time | Empty = 24 hours after the start (or the end of the UK day for a day value). A date covers that whole UK day. Max range 7 days (168 hours). |
| `includeHalfHourPeriods` | boolean | `true` | — | Output one row per half-hour. |
| `includeGenerationMix` | boolean | `true` | — | Add the fuel mix to each half-hour (national and regional). |
| `findLowCarbonWindows` | boolean | `true` | — | Output ranked window rows. |
| `windowDurationHours` | number | `2` | 0.5–24, in steps of 0.5 | Length of each window. It must fit inside the range. |
| `windowCount` | integer | `3` | 1–10 | Windows per area. Fewer are returned if the range is too short. |
| `windowsFromNowOnly` | boolean | `true` | — | Only search windows from the current half-hour onward. Turn it off to rank windows in a past range. |

At least one area is required. Duplicate regions and postcodes with the same outward code are merged. If both `includeHalfHourPeriods` and `findLowCarbonWindows` are off, the input is rejected.

#### Interacting settings

| When | Effective behavior |
| --- | --- |
| `startTime` is a day and `endTime` is empty | The range is that whole UK calendar day: 48 half-hours, or 46/50 on clock-change days. |
| The range is in the past and `windowsFromNowOnly` is `true` | No windows are found. Set `windowsFromNowOnly` to `false`. |
| A window has already started | It can still be ranked; its `isUpcoming` is `false`. |

#### Input example

```json
{
  "includeNational": true,
  "regions": ["13", "16"],
  "postcodes": ["M1 1AE"],
  "startTime": "now",
  "endTime": "+24h",
  "includeGenerationMix": true,
  "findLowCarbonWindows": true,
  "windowDurationHours": 2,
  "windowCount": 3
}
```

Expected output: 4 areas × 48 half-hours = 192 period rows plus up to 12 window rows. `M1 1AE` is reported as `M1`.

### Output

Each Dataset row has a `recordType`:

- `period`: one half-hour of one area;
- `window`: one ranked low-carbon window of one area;
- `area_status`: an area with no rows (`not_found`, `no_data` or `error`). These rows are free.

#### Output fields

| Field | Meaning | Can be empty? |
| --- | --- | --- |
| `recordType` | `period`, `window` or `area_status` | no |
| `area`, `areaType` | Area label and type (`national`, `region`, `postcode`) | no |
| `regionId`, `regionName`, `dnoRegion` | NESO region; `regionId` is null for Great Britain | yes |
| `postcode` | Outward postcode of the lookup | yes |
| `from`, `to` | Start and end, UTC ISO 8601 | null on status rows |
| `forecastIntensity` | Forecast, gCO2/kWh | yes |
| `actualIntensity` | Actual, gCO2/kWh. National past half-hours only; otherwise `null` (not 0) | yes |
| `intensityIndex` | `very low`, `low`, `moderate`, `high`, `very high` | yes |
| `unit` | Always `gCO2/kWh` | no |
| `generationMix` | Percent by fuel: biomass, coal, gas, imports, nuclear, hydro, solar, wind, other | yes |
| `windowRank` | 1 = lowest average forecast | window rows only |
| `windowDurationHours`, `averageForecastIntensity`, `minForecastIntensity`, `maxForecastIntensity` | Window length and its forecast (average rounded to 0.1) | window rows only |
| `isUpcoming` | `true` if the window starts after the run started | window rows only |
| `status`, `message` | `ok`, or the reason for a status row | `message` yes |
| `provider`, `sourceUrl`, `fetchedAt` | Source, exact API request and fetch time (UTC) | no |

The key-value store record `WINDOWS` holds only the window rows (a JSON array). The record `RUN_SUMMARY` has the run status, counts per area, the best windows, request statistics, warnings and the attribution text.

The Dataset has two table views, **Overview** and **Fuel mix**. Both show all rows; filter by `recordType` if you need one kind. To get only windows in the Dataset, turn off `includeHalfHourPeriods`.

#### Output example

A real row from the default run on 2026-10-12, trimmed:

```json
{
  "recordType": "period",
  "area": "GB (national)",
  "areaType": "national",
  "from": "2026-10-12T13:30:00Z",
  "to": "2026-10-12T14:00:00Z",
  "forecastIntensity": 73,
  "actualIntensity": null,
  "intensityIndex": "low",
  "unit": "gCO2/kWh",
  "generationMix": { "wind": 49.5, "gas": 13, "nuclear": 11.5, "imports": 10.7, "biomass": 8.7, "solar": 6.6, "hydro": 0, "coal": 0, "other": 0 },
  "status": "ok",
  "provider": "Carbon Intensity API (National Energy System Operator)",
  "sourceUrl": "https://api.carbonintensity.org.uk/intensity/2026-10-12T13:30Z/2026-10-13T13:30Z",
  "fetchedAt": "2026-10-12T13:52:42Z"
}
```

And a window row from the same run:

```json
{
  "recordType": "window",
  "area": "GB (national)",
  "from": "2026-10-13T11:30:00Z",
  "to": "2026-10-13T13:30:00Z",
  "windowRank": 1,
  "windowDurationHours": 2,
  "averageForecastIntensity": 36.5,
  "minForecastIntensity": 35,
  "maxForecastIntensity": 38,
  "isUpcoming": true
}
```

#### Output surfaces

| Situation | What you get | What to do |
| --- | --- | --- |
| Normal run | `period` and `window` rows, run status `COMPLETE` | Use the Overview view or the `WINDOWS` record |
| Unknown postcode | One `area_status` row with `not_found` | Check the postcode; Northern Ireland is not covered |
| Range with no data (too far ahead or before 2017) | `area_status` row with `no_data`, run status `NO_RESULTS` | Choose a range within the next ~48 hours or the past |
| Some areas failed | Rows for the others, run status `PARTIAL` | Read `RUN_SUMMARY.areas` and retry later |
| Invalid input | Run fails with a clear message, no rows | Fix the field named in the message |

### Pricing and limits

#### Pricing unit

This Actor uses pay-per-event pricing:

| Event | Price |
| --- | --- |
| Actor start (one per run at the default 256 MB memory) | $0.005 |
| Result row (one `period` or `window` row) | $0.001 (Free plan), $0.00095 (Bronze), $0.0009 (Silver), $0.00085 (Gold and higher) |

`area_status` rows are free. A run with no data (for example a range too far ahead) costs only the start event. If the source fails, no rows are charged. Platform usage is included in the price.

#### Cost examples (Free plan prices)

| Scenario | Input scope | Rows | Cost |
| --- | --- | ---: | ---: |
| Default run | GB + 2 regions, 24 h, 3 windows each | 153 | $0.158 |
| One window only | 1 postcode, periods off, 1 window | 1 | $0.006 |
| Two days, six areas | GB + 5 regions, 48 h, 3 windows each | 594 | $0.599 |
| Largest tested | GB + 17 regions + 5 postcodes, 7 days, windows on | 7,958 | $7.96 |

The largest tested run finished in 41 seconds. You can set a maximum total charge for a run; the Actor stops saving rows when it is reached (run status `CAPPED`).

### API, export and integrations

- **API:** start the Actor with the [Apify API](https://docs.apify.com/api/v2) and read the Dataset items, or the `WINDOWS` key-value record for the ranked windows only.
- **Export:** download the Dataset as JSON, CSV, Excel, XML or HTML.
- **Scheduling:** run it every day (for example at 18:00 UK time) with Apify schedules to plan the next day's flexible loads.
- **Webhooks / no-code:** send results to Make, Zapier, Google Sheets or your own webhook with Apify integrations.

#### Use with AI agents (MCP)

- MCP server URL with only this Actor: `https://mcp.apify.com?tools=cliqtomedia/uk-carbon-intensity`.

- Smallest useful input:

  ```json
  { "includeNational": true, "startTime": "now", "endTime": "+24h", "includeHalfHourPeriods": false, "windowCount": 3 }
  ```

- Key output fields: `from`, `to`, `averageForecastIntensity`, `windowRank` on window rows; `forecastIntensity`, `intensityIndex` on period rows.

- A typical call takes about 5 seconds and costs $0.008 for 3 window rows. Limits: 7 days per run, up to 5 postcodes, region IDs `"1"`–`"17"`.

### Troubleshooting

#### Input is rejected for a region name

Symptom: the run does not start and says the region value is not allowed.
Reason: the input form accepts region IDs only.
Action: use `"13"` for London, `"16"` for Scotland, and so on (see the Input table).

#### No windows in the output

Symptom: period rows exist, but there are no window rows.
Reason: the range is in the past and `windowsFromNowOnly` is `true`, or the range is shorter than `windowDurationHours`.
Action: set `windowsFromNowOnly` to `false` for past ranges, or make the window shorter.

#### Scotland shows 0 gCO2/kWh

Symptom: many half-hours for Scotland (16), North Scotland (1) or Scottish postcodes have forecast 0, and several windows have an average of 0.
Reason: this is the value the source publishes for these areas. The Actor keeps it as is and does not invent a ranking. When every forecast in the range is the same, `RUN_SUMMARY.warnings` says that the windows are ties ordered by start time.
Action: none needed; use another region if you need a ranking.

#### A postcode returns `not_found`

Symptom: one `area_status` row with `not_found`.
Reason: the source does not know this outward postcode, or it is in Northern Ireland.
Action: check the postcode. Other areas in the same run are not affected.

### FAQ

#### Do I need an account or API key for the source?

No. The source is a free public API. You only need an Apify account to run the Actor.

#### Why is `actualIntensity` null?

The source publishes actual values only for Great Britain as a whole and only for half-hours that have passed. Regions and postcodes have forecasts only. A null means "no value", never 0.

#### How fresh is the data?

Each run requests the source live. `fetchedAt` shows when. Forecasts change during the day, so a later run can show different forecasts for the same half-hour.

#### Why are times in UTC?

The source uses UTC. A UK calendar day (`today`, `2026-10-25`) is converted for you, so clock-change days have 46 or 50 half-hours.

### Source and licence

Contains data from the [Carbon Intensity API](https://carbonintensity.org.uk) by National Energy System Operator, licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). The Actor reformats it (UTC periods, window ranking). This Actor is not affiliated with or endorsed by NESO. Every row keeps `provider`, `sourceUrl` and `fetchedAt`.

### Support

For bugs, questions or feature requests, open an issue on the Actor's **Issues** tab in Apify Store. Include the run ID and your input. Do not send API tokens or other secrets.

# Changelog

This Actor's version history is a separate document: https://apify.com/cliqtomedia/uk-carbon-intensity/changelog.md

# Actor input Schema

## `includeNational` (type: `boolean`):

Include the national (GB) series. Only the national series has actual (measured) intensity; regional series are forecast only.

## `regions` (type: `array`):

Region IDs: 1–14 are the distribution network (DNO) regions, 15 England, 16 Scotland, 17 Wales. Unknown values stop the run with an error.

## `postcodes` (type: `array`):

Up to 5 UK postcodes. Only the outward part is used ("SW1A 1AA" → SW1A). A postcode the source does not know is reported as not_found, not as an error.

## `startTime` (type: `string`):

now (current half-hour), today, yesterday, tomorrow, a UK calendar day YYYY-MM-DD, or an ISO date-time (without an offset it is UTC). Times are rounded down to the half-hour.

## `endTime` (type: `string`):

Empty = 24 hours after the start (or the end of the UK day when the start is a day). Also accepts +Nh (for example +48h), a UK day YYYY-MM-DD (to its end) or an ISO date-time. Maximum range 7 days; forecasts reach about 48 hours ahead.

## `includeHalfHourPeriods` (type: `boolean`):

Output one row per area and half-hour. Turn off to get only the low-carbon windows.

## `includeGenerationMix` (type: `boolean`):

Add the fuel mix (percent of generation: wind, solar, gas, nuclear, imports and others) to each half-hour row. For the national series this is one extra source request.

## `findLowCarbonWindows` (type: `boolean`):

Find the time windows with the lowest average forecast intensity, per area. Windows do not overlap.

## `windowDurationHours` (type: `number`):

Length of each window, 0.5–24 hours, in steps of 0.5.

## `windowCount` (type: `integer`):

How many best windows per area (1–10). Fewer are returned when the range does not have room.

## `windowsFromNowOnly` (type: `boolean`):

Search windows only from the current half-hour onward, so they can still be scheduled. Turn off to rank past windows too.

## Actor input object example

```json
{
  "includeNational": true,
  "regions": [
    "13",
    "16"
  ],
  "startTime": "now",
  "endTime": "+24h",
  "includeHalfHourPeriods": true,
  "includeGenerationMix": true,
  "findLowCarbonWindows": true,
  "windowDurationHours": 2,
  "windowCount": 3,
  "windowsFromNowOnly": true
}
```

# Actor output Schema

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

Half-hour periods, low-carbon windows and area status rows; the recordType field says which.

## `windows` (type: `string`):

JSON array of the ranked window rows (the same rows as recordType = window in the Dataset).

## `runSummary` (type: `string`):

Status, counts per area, best windows, request statistics and source attribution.

# 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 = {
    "includeNational": true,
    "regions": [
        "13",
        "16"
    ],
    "startTime": "now",
    "endTime": "+24h",
    "includeGenerationMix": true,
    "findLowCarbonWindows": true,
    "windowDurationHours": 2,
    "windowCount": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("cliqtomedia/uk-carbon-intensity").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 = {
    "includeNational": True,
    "regions": [
        "13",
        "16",
    ],
    "startTime": "now",
    "endTime": "+24h",
    "includeGenerationMix": True,
    "findLowCarbonWindows": True,
    "windowDurationHours": 2,
    "windowCount": 3,
}

# Run the Actor and wait for it to finish
run = client.actor("cliqtomedia/uk-carbon-intensity").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 '{
  "includeNational": true,
  "regions": [
    "13",
    "16"
  ],
  "startTime": "now",
  "endTime": "+24h",
  "includeGenerationMix": true,
  "findLowCarbonWindows": true,
  "windowDurationHours": 2,
  "windowCount": 3
}' |
apify call cliqtomedia/uk-carbon-intensity --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,cliqtomedia/uk-carbon-intensity"
        }
    }
}
```

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/LZMHoPRMszkVdnVDV/builds/OfvVN4Ml7za0w0ups/openapi.json
