# EU ETS Installations & Verified Emissions (EUTL) (`rastriq/eu-ets-installations-emissions`) Actor

- **URL**: https://apify.com/rastriq/eu-ets-installations-emissions.md
- **Developed by:** [Rastriq — Structured data from the world](https://apify.com/rastriq) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 1,000 installation returneds

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

## EU ETS Installations & Verified Emissions (EUTL)

**EU ETS Installations & Verified Emissions** returns every installation and operator in the **EU Emissions Trading System** - about **23,500** power plants, factories, aircraft operators, shipping companies and ETS2 fuel suppliers in 30+ countries - as one clean row each: installation, operator, **LEI**, activity, permit, address, and **verified CO2 emissions, free allocation and surrendered allowances for every year since 2005**. A `delta` mode tells you what changed since your last run (new installations, closures, newly verified emissions).

### Why use it

The European Commission publishes the EU Transaction Log (EUTL) as daily extracts split across several files, with one row per installation per year on a fixed 2005-2030 grid. This Actor joins them, keeps only the years that carry data, computes the latest-year figures and year-on-year change, and adds a change feed:

| | |
|---|---|
| 🏭 **One row per installation** | Operator, LEI, company registration number, activity, permit, address, status (`active` / `closed`). |
| 📈 **Emissions and allowances** | Latest verified emissions, previous year, % change, free allocation, surrendered, surrender gap, lifetime total, and a full yearly `history`. |
| 🔔 **Change feed** | `delta` returns only new, changed and removed installations; `changed_fields` and `previous_values` say what moved. |
| 🧭 **Filters** | Country registry, installation type, EUTL activity code, minimum emissions, active only. |
| ⚡ **Light** | Two compressed files (~7 MB), about a minute end to end. |

#### Snapshot vs delta: what each mode returns and charges

| Mode | Kind | What you get | What is charged |
|---|---|---|---|
| **Snapshot** | full list in scope | Everything matching your filters, on every run | `installation-record` per row |
| **Delta** | changes only | 1st run: everything in scope as `baseline`. Later runs: only `new`, `changed` (e.g. newly verified emissions) and `removed` installations | 1st run: `installation-record` per row. Later runs: `installation-change` per changed row only. **Unchanged installations are never charged** |

### Use cases

1. **Carbon and energy market analysis** - traders and analysts: emissions vs free allocation by installation, sector and country; who is short of allowances.
2. **ESG and climate research** - scope 1 emissions of listed groups via `operator_lei`; trends since 2005.
3. **B2B lead generation** - decarbonisation, CCS, heat-pump, hydrogen, energy-efficiency and PPA vendors: the largest emitters by sector and region, with address.
4. **Supply-chain and CBAM work** - cement, steel, aluminium, fertiliser and hydrogen plants with their verified emissions.
5. **Monitoring** - schedule `delta` weekly: get alerts when an installation opens, closes or reports a new year of emissions (verified emissions are reported each spring).

### How to use

1. Keep the pre-filled values and click **Start**: the 15 largest stationary emitters in Spain, about $0.04.
2. Change **Countries**, **Installation types** or **Activity codes**, and raise **Max rows** (0 = everything in scope).
3. For monitoring, switch **Mode** to **Delta** and add a schedule.

### Input

| Field | Type | Description |
|---|---|---|
| `mode` | enum | `delta` (default when empty) or `snapshot` |
| `countries` | array | Registry codes: `ES`, `DE`, `FR`, `PL`... |
| `installationTypes` | array | `stationary_installation`, `aircraft_operator`, `maritime_operator`, `ets2_regulated_entity` |
| `activityCodes` | array | EUTL activity codes (`20` combustion, `29` cement clinker, `24` steel...) |
| `minVerifiedEmissions` | integer | Minimum latest verified emissions (tCO2e) |
| `includeClosed` | boolean | Include closed installations (default on) |
| `includeHistory` | boolean | Yearly `history` array (default on) |
| `maxItems` | integer | Row limit and spending cap (pre-filled 15; 0 = none); rows are sorted by latest emissions, largest first |
| `stateKey` | string | Name of an independent change-tracking stream |

```json
{ "mode": "snapshot", "countries": ["DE", "PL"], "activityCodes": ["29"], "includeHistory": false }
```

### Output example

Real row (Spain, largest emitter; the `history` array is shortened to 3 of 26 years here):

```json
{
  "installation_key": "ES-212",
  "registry_code": "ES",
  "registry_name": "Spain",
  "installation_id": "212",
  "installation_name": "ARCELORMITTAL ESPAÑA, S.A.",
  "installation_type": "stationary_installation",
  "activity_code": 24,
  "activity_type": "Production of pig iron or steel",
  "permit_id": "ES033305000226",
  "permit_revocation_date": null,
  "eprtr_id": "3486",
  "account_id": "5008935",
  "status": "active",
  "year_first_emissions": 2005,
  "year_last_emissions": null,
  "address_line_1": "Factoria Aceralia Avilés",
  "address_line_2": null,
  "city": "Avilés y Gijón (Asturias)",
  "postal_code": "33468",
  "operator_name": "ARCELORMITTAL ESPAÑA, S.A.",
  "operator_lei": "54930044YNH8CDC9EI36",
  "operator_company_registration_number": "A81046856",
  "operator_country_code": "ES",
  "operator_city": "Gozón (Asturias)",
  "operator_postal_code": "33418",
  "operator_address_line_1": "Residencia La Granda",
  "operator_address_line_2": null,
  "is_operator_redacted": false,
  "quality_flags": [],
  "latest_emissions_year": 2025,
  "verified_emissions_latest": 4673784,
  "free_allocation_latest": 5361058,
  "surrendered_latest": 4673784,
  "verified_emissions_previous_year": 4780953,
  "emissions_change_pct": -2.24,
  "surrender_gap_latest": 0,
  "verified_emissions_total": 106608892,
  "history": [
    {
      "year": 2005,
      "verified_emissions": 5536780,
      "free_allocation": 8837379,
      "allocation_new_entrant_reserve": 0,
      "allocation_transitional": 0,
      "surrendered": 5536780,
      "excluded": false
    },
    {
      "year": 2025,
      "verified_emissions": 4673784,
      "free_allocation": 5361058,
      "allocation_new_entrant_reserve": 0,
      "allocation_transitional": 0,
      "surrendered": 4673784,
      "excluded": false
    },
    {
      "year": 2030,
      "verified_emissions": 0,
      "free_allocation": 2641328,
      "allocation_new_entrant_reserve": 0,
      "allocation_transitional": 0,
      "surrendered": 0,
      "excluded": false
    }
  ],
  "source": "EU Transaction Log (EUTL) public extracts, European Commission",
  "source_url": "https://union-registry-data.ec.europa.eu/",
  "source_updated_at": "2026-10-24",
  "first_seen_at": "2026-10-24T20:22:54Z",
  "last_seen_at": "2026-10-24T20:22:54Z",
  "detected_at": "2026-10-24T20:22:54Z",
  "change_type": "baseline",
  "changed_fields": [],
  "previous_values": null
}
```

### What data you get

| Field | Description |
|---|---|
| `installation_key` | Stable key `<registry>-<installation id>` |
| `registry_code`, `installation_name`, `permit_id`, `eprtr_id`, `activity_code`, `activity_type` | Where the installation is and what it does |
| `address_*`, `city`, `postal_code`, `operator_company_registration_number` | Installation and operator address and company number |
| `verified_emissions_previous_year`, `emissions_change_pct`, `free_allocation_latest`, `surrendered_latest`, `verified_emissions_total` | Year-on-year and lifetime figures (tCO2e / allowances) |
| `source_updated_at`, `first_seen_at`, `last_seen_at`, `detected_at`, `change_type`, `changed_fields`, `previous_values` | Extract date and delta tracking |
| `installation_type` | Derived from the EUTL activity code |
| `status` | `closed` if the permit was revoked or a final emissions year is set |
| `operator_*` | Account holder (operator) of the installation; `operator_lei` when registered (normalised to the 20-character LEI; the source publishes it with hyphens) |
| `latest_emissions_year`, `verified_emissions_latest` | Last year (up to last calendar year) with verified emissions, tCO2e |
| `surrender_gap_latest` | Surrendered minus verified for that year (derived; negative = less surrendered than emitted) |
| `history` | Every year with any data, including future free allocation already decided |
| `quality_flags` | `operator_redacted_possible_person`, `installation_name_redacted`, `operator_no_legal_form`, `no_operator`, `missing_address`, `invalid_lei`. Rows are flagged, never dropped |

### Personal data

A few account holders are private individuals (for example owners of private aircraft or small farms). An operator name is kept only when it is recognisably an organisation: it has an LEI, a legal form (GmbH, S.A., Sp. z o.o., A/S, Ltd...) or an organisation word, and no sole-trader marker (Inh., e.K.). Names that look like a person are set to `null` together with the operator's registration number and street address (and the installation name when it repeats the person's name); the row is kept and flagged. The free-text account label of the source is never output. The filter is deliberately cautious, so some companies without a legal form in their name also lose the operator name (`operator_redacted_possible_person`); the installation name and figures remain.

### Pricing

Pay per event. Tier discounts: Silver -15 %, Gold and above -30 % (Free plan +15 %).

| Event | Price (Bronze) |
|---|---|
| `installation-record` - one installation in a snapshot or baseline | $2.00 / 1,000 |
| `installation-change` - one new / changed / removed installation in delta | $4.00 / 1,000 |
| Actor start | $0.01 per run |

The pre-filled example (15 installations) costs about $0.04; 100 installations cost $0.21; the full register (~23,500 installations, with yearly history) about $47. Only result rows are billed; unchanged installations in delta are free. **Max rows** caps what a run can bill.

### Source, licence and limits

- Source: European Commission, **EU Transaction Log (EUTL) / Union Registry public data**, daily extracts listed at [union-registry-data.ec.europa.eu](https://union-registry-data.ec.europa.eu/). Attribution: *Source: European Commission, Union Registry / EUTL*. Reuse of Commission information follows the Commission reuse policy (Decision 2011/833/EU); check it for your use case.
- Verified emissions of year N are reported by 31 March of N+1 and allowances surrendered by 30 September; figures for the latest year can change during that period.
- Swiss linked-registry columns are not included. Transactions and holdings are not included.
- Not affiliated with or endorsed by the European Commission. Not legal or investment advice.

### FAQ

**How fresh is the data?** The Commission refreshes the extracts every day; each row carries the extract date in `source_updated_at`.

**Is it legal to use this data?** Yes: the EUTL extracts are public Commission data, reusable under the Commission reuse policy with attribution (see *Source, licence and limits*). Private individuals' names are removed.

**Do I need proxies or an account?** No. The Actor downloads two public files over plain HTTPS.

**How often should I run it?** Weekly or daily in delta mode is plenty: the source refreshes daily and verified emissions are reported once a year (spring).

**Where is the change state kept?** In a named key-value store (`eu-ets-state`) in your own Apify account; delete it to start a new baseline.

**Why is `verified_emissions_latest` empty?** The installation has no verified emissions on record (new, closed long ago, or an ETS2 entity before its first reporting year).

**Can I get one row per year?** The `history` array has one entry per year; export as JSON, or flatten it in your tool.

Built by **Rastriq** - reliable data from official registers. Questions or feature requests: use the Actor's Issues tab.

# Changelog

This Actor's version history is a separate document: https://apify.com/rastriq/eu-ets-installations-emissions/changelog.md

# Actor input Schema

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

<b>Delta</b> (default when empty): only new, changed and removed installations since your last run; the first run returns everything as <code>baseline</code>.<br><b>Snapshot</b>: everything in scope on every run.

## `countries` (type: `array`):

Keep only installations of these national registries (2-letter codes, e.g. <code>ES</code>, <code>DE</code>, <code>PL</code>). Empty = all 30+ registries. You can type any other code and press Enter.

## `installationTypes` (type: `array`):

Keep only these types of regulated entity. Empty = all types.

## `activityCodes` (type: `array`):

EUTL activity codes, e.g. <code>20</code> combustion of fuels, <code>29</code> cement clinker, <code>21</code> mineral oil refining, <code>24</code> pig iron or steel, <code>36</code> paper or cardboard. Empty = all.

## `minVerifiedEmissions` (type: `integer`):

Keep only installations whose latest verified emissions are at least this value. 0 = no minimum.

## `includeClosed` (type: `boolean`):

Installations with a revoked permit or a final year of emissions. They carry <code>status = closed</code>.

## `includeHistory` (type: `boolean`):

Adds a <code>history</code> array with verified emissions, free allocation, reserve allocation, transitional allocation and surrendered allowances for every year with data (2005 onwards, including already-decided future free allocation). Turn off for a lighter table.

## `maxItems` (type: `integer`):

Stop after this many rows (0 = no limit). Rows come sorted by latest verified emissions, largest first. Each row is billed, so this is also your spending cap. Pre-filled with 15 so the first test costs about 4 cents.

## `stateKey` (type: `string`):

Optional name of the change-tracking stream (e.g. <code>spain-steel</code>). Use different keys to keep independent histories. Mainly for delta mode.

## Actor input object example

```json
{
  "mode": "snapshot",
  "countries": [
    "ES"
  ],
  "installationTypes": [
    "stationary_installation"
  ],
  "minVerifiedEmissions": 0,
  "includeClosed": true,
  "includeHistory": true,
  "maxItems": 15
}
```

# Actor output Schema

## `installations` (type: `string`):

No description

## `runSummary` (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 = {
    "mode": "snapshot",
    "countries": [
        "ES"
    ],
    "installationTypes": [
        "stationary_installation"
    ],
    "maxItems": 15
};

// Run the Actor and wait for it to finish
const run = await client.actor("rastriq/eu-ets-installations-emissions").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": "snapshot",
    "countries": ["ES"],
    "installationTypes": ["stationary_installation"],
    "maxItems": 15,
}

# Run the Actor and wait for it to finish
run = client.actor("rastriq/eu-ets-installations-emissions").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": "snapshot",
  "countries": [
    "ES"
  ],
  "installationTypes": [
    "stationary_installation"
  ],
  "maxItems": 15
}' |
apify call rastriq/eu-ets-installations-emissions --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,rastriq/eu-ets-installations-emissions"
        }
    }
}
```

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/n3f6EPGeTEhWxwW7w/builds/fk1mN17NpAxZCdvap/openapi.json
