UK Carbon Intensity Scraper: Grid Mix & Emissions avatar

UK Carbon Intensity Scraper: Grid Mix & Emissions

Pricing

from $0.37 / 1,000 intensity scrapeds

Go to Apify Store
UK Carbon Intensity Scraper: Grid Mix & Emissions

UK Carbon Intensity Scraper: Grid Mix & Emissions

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.

Pricing

from $0.37 / 1,000 intensity scrapeds

Rating

0.0

(0)

Developer

Arman Hossain

Arman Hossain

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

a day ago

Last modified

Share

UK Carbon Intensity: Half-hourly carbon intensity and generation mix for the GB grid, national or by any of 18 regions

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

What you get

Output fieldMeaning
from, toThe half-hour settlement period, ISO 8601 UTC
intensityForecastForecast carbon intensity in gCO₂/kWh
intensityActualSettled actual intensity, national only, null for future periods and for regions
intensityIndexvery low, low, moderate, high, very high
regionRegion ID (1-18), or null for a national reading
regionNameNational, or the region short name such as London
generationMixRaw source array: [{ fuel, perc }, …]
fuelSharesThe same data flattened to { biomass: 7.9, coal: 0, … } so it survives a CSV export
postcodeThe outward code you queried, when you queried by postcode
scrapedAtRun timestamp

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

Four modes

ModeWhat it returnsNeeds
currentThe settlement period happening right now, nationally-
forecastThe next 48 hours, 97 half-hourly periods-
historicalEvery half hour in your window, nationallyfromDate
regionalPer-region readings, snapshot or time seriesoptional 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:

{
"mode": "current"
}

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

{
"mode": "forecast"
}

A full month of settled national data for reporting:

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

Two postcodes as a week-long time series:

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

All 18 regions right now, one request:

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

Input

FieldTypeDefaultNotes
modestringcurrentcurrent, forecast, historical or regional.
fromDatestring-YYYY-MM-DD or ISO 8601. Required for historical. In regional mode it turns a snapshot into a time series.
toDatestringnowYYYY-MM-DD or ISO 8601. Windows over 14 days are split into 14-day requests automatically.
regionsarray[]Region IDs 1-18, or UK postcodes. Empty in regional mode returns all 18 in one request.
includeGenerationMixbooleantrueInclude per-fuel shares. Turn off for a smaller, faster dataset.
maxResultsinteger0Cap on readings saved. 0 = no limit.

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

Output example

{
"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

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

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.