# UK Carbon Intensity Scraper: Grid Mix & Emissions (`arman-bd/uk-carbon-intensity-scraper`) Actor

Scrape the UK National Grid carbon intensity API: actual and forecast gCO2/kWh, generation mix by fuel type, and regional breakdowns. No login, no proxy, no browser.

- **URL**: https://apify.com/arman-bd/uk-carbon-intensity-scraper.md
- **Developed by:** [Arman Hossain](https://apify.com/arman-bd) (community)
- **Categories:** Business, Developer tools, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.37 / 1,000 intensity scrapeds

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## UK Carbon Intensity Scraper: Grid Mix & Emissions

![UK Carbon Intensity: Half-hourly carbon intensity and generation mix for the GB grid, national or by any of 18 regions](https://api.apify.com/v2/key-value-stores/ZQOcNAOHrIgTacAmy/records/uk-carbon-intensity-scraper.jpg)

**UK Carbon Intensity Scraper** exports half-hourly carbon intensity for the GB electricity grid, actual and forecast gCO₂/kWh, the intensity index, and the generation mix broken down by fuel, nationally or for any of the 18 grid regions.

It reads the **National Grid ESO Carbon Intensity API** directly: **no proxy setup, no browser, no credentials to manage.** Payloads are tiny, which suits the half-hourly cadence of the underlying settlement periods.

**Agent skill: [SKILL.md](https://api.apify.com/v2/key-value-stores/t7YoTxpZEJOWvw4Ug/records/uk-carbon-intensity-scraper.md)**

```
https://api.apify.com/v2/key-value-stores/t7YoTxpZEJOWvw4Ug/records/uk-carbon-intensity-scraper.md
```

### What you get

| Output field | Meaning |
|---|---|
| `from`, `to` | The half-hour settlement period, ISO 8601 UTC |
| `intensityForecast` | Forecast carbon intensity in gCO₂/kWh |
| `intensityActual` | Settled actual intensity, national only, `null` for future periods and for regions |
| `intensityIndex` | `very low`, `low`, `moderate`, `high`, `very high` |
| `region` | Region ID (1-18), or `null` for a national reading |
| `regionName` | `National`, or the region short name such as `London` |
| `generationMix` | Raw source array: `[{ fuel, perc }, …]` |
| `fuelShares` | The same data flattened to `{ biomass: 7.9, coal: 0, … }` so it survives a CSV export |
| `postcode` | The outward code you queried, when you queried by postcode |
| `scrapedAt` | Run timestamp |

A `RUN_SUMMARY` record in the key-value store holds per-run counts, the filters used, and any request that failed.

### Four modes

| Mode | What it returns | Needs |
|---|---|---|
| `current` | The settlement period happening right now, nationally | - |
| `forecast` | The next 48 hours, 97 half-hourly periods | - |
| `historical` | Every half hour in your window, nationally | `fromDate` |
| `regional` | Per-region readings, snapshot or time series | optional `regions`, optional `fromDate` |

### Common use cases

- **Carbon-aware compute scheduling.** Run batch jobs when `intensityForecast` dips, using the 48-hour forecast.
- **Scope 2 and ESG reporting.** Pull a full reporting year of `intensityActual` and multiply by metered consumption.
- **Smart charging and demand response.** Shift EV or heat-pump load into `very low` periods for a specific postcode.
- **Renewables analysis.** Track wind and solar share over time from `fuelShares`.
- **Regional comparison.** North Scotland and London differ by an order of magnitude at the same moment.

### Quick start

Right now, nationally:

```json
{
 "mode": "current"
}
```

The next 48 hours, for a load-shifting scheduler:

```json
{
 "mode": "forecast"
}
```

A full month of settled national data for reporting:

```json
{
 "mode": "historical",
 "fromDate": "2026-07-01",
 "toDate": "2026-08-01"
}
```

Two postcodes as a week-long time series:

```json
{
 "mode": "regional",
 "regions": ["SW1A 1AA", "RG10"],
 "fromDate": "2026-08-01",
 "toDate": "2026-08-08"
}
```

All 18 regions right now, one request:

```json
{
 "mode": "regional",
 "regions": []
}
```

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `mode` | string | `current` | `current`, `forecast`, `historical` or `regional`. |
| `fromDate` | string | - | `YYYY-MM-DD` or ISO 8601. **Required for `historical`.** In `regional` mode it turns a snapshot into a time series. |
| `toDate` | string | now | `YYYY-MM-DD` or ISO 8601. Windows over 14 days are split into 14-day requests automatically. |
| `regions` | array | `[]` | Region IDs `1`-`18`, or UK postcodes. Empty in `regional` mode returns all 18 in one request. |
| `includeGenerationMix` | boolean | `true` | Include per-fuel shares. Turn off for a smaller, faster dataset. |
| `maxResults` | integer | `0` | Cap on readings saved. `0` = no limit. |

`fromDate` is ignored in `current` mode, and `regions` is ignored in every mode except `regional`.

### Output example

```json
{
 "from": "2026-08-06T11:00Z",
 "to": "2026-08-06T11:30Z",
 "intensityForecast": 62,
 "intensityActual": 61,
 "intensityIndex": "low",
 "region": null,
 "regionName": "National",
 "generationMix": [
 { "fuel": "biomass", "perc": 7.9 },
 { "fuel": "coal", "perc": 0 },
 { "fuel": "imports", "perc": 17.2 },
 { "fuel": "gas", "perc": 7.5 },
 { "fuel": "nuclear", "perc": 12.7 },
 { "fuel": "other", "perc": 0 },
 { "fuel": "hydro", "perc": 0 },
 { "fuel": "solar", "perc": 24.5 },
 { "fuel": "wind", "perc": 30.2 }
 ],
 "fuelShares": {
 "biomass": 7.9, "coal": 0, "imports": 17.2, "gas": 7.5,
 "nuclear": 12.7, "other": 0, "hydro": 0, "solar": 24.5, "wind": 30.2
 },
 "postcode": null,
 "scrapedAt": "2026-08-06T12:00:00.000Z"
}
```

### API example

```bash
curl -X POST "https://api.apify.com/v2/acts/arman-bd~uk-carbon-intensity-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
 "mode": "historical",
 "fromDate": "2026-08-01",
 "toDate": "2026-08-02"
 }'
```

### JavaScript example

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

const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('arman-bd/uk-carbon-intensity-scraper').call({
 mode: 'forecast',
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
const greenest = items.reduce((a, b) => (b.intensityForecast < a.intensityForecast ? b : a));
console.log(`Cleanest window: ${greenest.from} at ${greenest.intensityForecast} gCO2/kWh`);
```

### Notes

- **Regional readings have no `intensityActual`.** The source publishes a settled actual for the national grid only; regional data is forecast-based. The field is present and `null` rather than missing, so your schema stays stable.
- **Forecast periods also have a `null` actual.** Anything in the future has not settled yet. That is expected, not a gap.
- **Forecast mode has no generation mix.** The `/generation` endpoint accepts settled windows only; there is no forward variant, so `generationMix` and `fuelShares` are `null` in `forecast` mode. Regional forecasts do carry a mix, because it is embedded in the regional response.
- **Long windows are split automatically.** The API documents a 14-day ceiling per historical request, so a year-long window becomes 27 sequential requests. Each is a separate entry in `RUN_SUMMARY.failures` if it fails, and one failure never discards the others.
- **The national generation mix comes from a second endpoint.** `/intensity` and `/generation` are keyed on the same settlement periods, so the two are joined on `from`. If the mix call fails, the intensity record is still saved with `generationMix: null`.
- **`fuelShares` exists because arrays flatten badly.** `generationMix` is the source array; `fuelShares` is the same numbers as named columns, which is what you want in CSV or a spreadsheet.
- **Postcodes are trimmed to the outward code.** `SW1A 1AA` becomes `SW1A`, because the API rejects anything longer.
- **Transient errors are retried.** 429, 5xx and network errors get four attempts with exponential backoff (0.5 s → 1 s → 2 s). A rejected region or malformed window fails immediately rather than burning retries.
- **Public data only.** No authentication, no personal data, no access-control bypass.

### FAQ

**Do I need a proxy?** No. Proxy configuration is not required to run this Actor.

**Do I need an account on the source?** No, and there is no credentials to manage.

**What happens if a request fails?** It is reported in `RUN_SUMMARY.failures` and the run continues. The Actor only errors out if every request failed.

**Can I schedule it?** Yes, it is designed for scheduled runs. A half-hourly `current` run mirrors the grid's own settlement cadence.

**Does it cover Northern Ireland?** No. The API covers Great Britain; Northern Ireland runs on a separate grid.

**What are the region IDs?** 1-14 are the DNO regions, running roughly north to south: 1 North Scotland, 2 South Scotland, 3 North West England, 13 London, 14 South East England. Then 15 England, 16 Scotland, 17 Wales and 18 GB. Run `regional` with an empty `regions` list to see all of them with their names. Note that the API's own error message wrongly says the range is 1-17; `18` works.

**Why does `intensityForecast` differ from `intensityActual`?** The forecast is published ahead of time; the actual is settled afterwards from metered generation. Comparing the two is a legitimate use of this data.

**How far back does history go?** To May 2017, when the API began publishing.

**Can I integrate it with something else?** Yes, Apify API, client libraries, webhooks, scheduled runs, dataset exports (JSON/CSV/Excel) or MCP. Output is structured JSON.

# Actor input Schema

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

What to fetch. 'current' is the settlement period happening now, 'forecast' is the next 48 hours, 'historical' needs a date window, and 'regional' breaks the grid down by DNO region or postcode.

## `fromDate` (type: `string`):

Window start as YYYY-MM-DD or an ISO 8601 timestamp. Required for 'historical'. In 'regional' mode it turns a snapshot into a time series; in 'forecast' mode it anchors the 48-hour horizon.

## `toDate` (type: `string`):

Window end as YYYY-MM-DD or an ISO 8601 timestamp. Defaults to now. Windows longer than 14 days are split into 14-day requests automatically.

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

Region IDs (1-18) or UK postcodes. 1-14 are the DNO regions, then 15 England, 16 Scotland, 17 Wales, 18 GB. Full postcodes are trimmed to the outward code, so 'SW1A 1AA' and 'SW1A' behave identically. Leave empty in 'regional' mode to get all 18 in one request.

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

Include the per-fuel generation shares alongside each reading. National mode fetches these from a second endpoint; regional responses already carry them. Disable for a smaller, faster dataset.

## `maxResults` (type: `integer`):

Cap the total number of readings saved. Set 0 for no limit. A year of half-hourly national data is about 17,500 readings.

## Actor input object example

```json
{
  "mode": "historical",
  "fromDate": "2026-08-01",
  "toDate": "2026-08-07",
  "regions": [
    "3",
    "RG10",
    "SW1A 1AA"
  ],
  "includeGenerationMix": true,
  "maxResults": 0
}
```

# Actor output Schema

## `items` (type: `string`):

Every record the run produced.

## `runsummary` (type: `string`):

The RUN\_SUMMARY record from the run's key-value store.

# 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 = {
    "mode": "current",
    "regions": [
        "13",
        "SW1A"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("arman-bd/uk-carbon-intensity-scraper").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 = {
    "mode": "current",
    "regions": [
        "13",
        "SW1A",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("arman-bd/uk-carbon-intensity-scraper").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 '{
  "mode": "current",
  "regions": [
    "13",
    "SW1A"
  ]
}' |
apify call arman-bd/uk-carbon-intensity-scraper --silent --output-dataset

```

## MCP server setup

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

```

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/dzfRKseGQWTeaba4b/builds/oVOb1UwusxUEKJamM/openapi.json
