# EU Grid Electricity & Gas Procurement Radar (`steppedatatools/eu-grid-energy-procurement-radar`) Actor

Find open EU electricity and stationary natural-gas supply tenders and watch awarded contracts approaching reliable end dates. Lot-level filtering and lifecycle deduplication.

- **URL**: https://apify.com/steppedatatools/eu-grid-energy-procurement-radar.md
- **Developed by:** [Oralzhan Kaliyev](https://apify.com/steppedatatools) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 results

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?

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

## EU Grid Electricity & Gas Procurement Radar

An independent Apify Actor that produces two high-precision procurement signals from the public TED Search API:

- `LIVE_TENDER` — an open competition with a future submission or expression-of-interest deadline;
- `EARLY_EXPIRY_SIGNAL` — an awarded contract with a reliable end date approaching the configured watch window.

This is not a generic TED scraper and it does not claim to predict that every expiring contract will be re-tendered.

### Deliberately narrow V1

| Product | Exact main lot CPV | Rule |
|---|---|---|
| Grid electricity | `09310000` | Supplies contract, EU-27 buyer |
| Stationary natural gas | `09123000` | Supplies contract; clear transport/fuel-card signals are excluded, multilingual neutral titles remain eligible |

Transport fuel, fuel cards, CNG/biomethane at filling stations, liquid fuels, broad CPV matches, inferred dates, national portals, attachments, OCR, CRM, alerts, and schedules are outside V1.

### Exact TED notice types

| Stream | Notice types | Behaviour |
|---|---|---|
| LIVE | `cn-standard`, `pin-cfc-standard` | Accept only a safely mapped lot with a future deadline |
| EARLY | `can-standard` | Create or update an awarded-contract watch record |
| EARLY lifecycle | `can-modif`, `compl` | Update or close an existing contract only after a deterministic match |

The collector sends ten narrow query families when both CPVs and both streams are enabled: four LIVE searches, two award searches, two modification searches, and two completion searches. It asks TED for latest notice versions only and uses a seven-day overlap on incremental lifecycle scans.

### Quality and deduplication

- One output item represents one safely mapped lot.
- Main lot CPV is the inclusion gate; an additional CPV is not enough.
- Ambiguous flattened-array mappings are suppressed by default.
- Contract end dates are `A_DIRECT` (TED end date) or `B_COMPUTED` (TED start plus calendar duration). Inferred dates are excluded.
- LIVE identity is `procedureId + lotId`.
- EARLY identity is `procedureId + contractId + lotId`; uncertain fallbacks are labelled and review-only.
- Replayed overlap windows are idempotent: unchanged records remain `UNCHANGED` instead of becoming duplicates.
- TED's production Search API is read with `ITERATION` pagination, avoiding the 15,000-result page-number ceiling and preserving a consistent point-in-time walk.
- Modification and completion notices that cannot be linked uniquely become anomalies and do not alter another contract.
- Tender-to-expiry linking is `LINKED_HIGH` only with the same exact buyer identifier, same CPV, and a contract start within 183 days of the old end date. Name-only candidates are review-only.

### Input

| Field | Default | Meaning |
|---|---|---|
| `signalTypes` | both | Select LIVE, EARLY, or both |
| `cpvCodes` | both V1 codes | Electricity, stationary gas, or both |
| `countries` | all EU-27 | Optional two-letter buyer-country filter |
| `expiryFromDays` | `90` | Start of the early-watch window |
| `expiryToDays` | `730` | End of the early-watch window |
| `includeReview` | `false` | Include clearly labelled ambiguous records for QA |
| `maxItems` | `1000` | Output cap; source lifecycle processing still completes |

### Output and state

Both streams use one JSON schema. Core fields include `signalId`, `signalType`, `status`, TED identifiers and URL, procedure/lot/contract identity, main CPV, scope decision, buyer, LIVE deadline, EARLY end-date confidence, nullable value, and conservative linkage fields. Under the hard `maxItems` cap, output alternates LIVE and EARLY records so a busy expiry stream cannot hide current tenders.

Award records beyond the current watch window remain in internal state as `PENDING_WINDOW`, so they can enter the output later without rescanning the complete history. State is split into 16 deterministic key-value-store shards to avoid one oversized JSON record. The initial eForms backfill starts on 25 October 2023; later scans use per-CPV cursors.

### Local verification

Tests use in-memory TED and storage doubles, so they do not contact TED or run the Actor:

```bash
PYTHONPATH=src python -m unittest discover -s tests -v
```

### Current status

The implementation is an independent Apify Actor. Local syntax and unit tests pass. The selected TED fields, query payload, and `ITERATION` pagination have been checked against production, including real notices `535223-2026`, `607260-2026`, `611080-2026`, `611838-2026`, `541608-2025`, and `12500-2026`. Build `0.0.9` and the first scheduled run succeeded with 1000 dataset records (500 LIVE and 500 EARLY records in the capped output). The daily schedule `EU Energy Radar — Daily TED scan` is enabled. Source files remain hidden from Store users.

# Actor input Schema

## `signalTypes` (type: `array`):

LIVE\_TENDER finds open competitions. EARLY\_EXPIRY\_SIGNAL watches awarded contracts.

## `cpvCodes` (type: `array`):

V1 is deliberately limited to grid electricity and stationary natural gas.

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

Two-letter EU country codes. Empty means all EU-27 countries.

## `expiryFromDays` (type: `integer`):

Minimum days from today to the contract end date.

## `expiryToDays` (type: `integer`):

Maximum days from today to the contract end date.

## `includeReview` (type: `boolean`):

Keep disabled for commercial output. When enabled, includes REVIEW records for data-quality analysis.

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

Hard safety limit across both signal types.

## Actor input object example

```json
{
  "signalTypes": [
    "LIVE_TENDER",
    "EARLY_EXPIRY_SIGNAL"
  ],
  "cpvCodes": [
    "09310000",
    "09123000"
  ],
  "countries": [],
  "expiryFromDays": 90,
  "expiryToDays": 730,
  "includeReview": false,
  "maxItems": 1000
}
```

# Actor output Schema

## `signals` (type: `string`):

The unified dataset containing open electricity or stationary natural-gas tenders and reliable contract-end watch signals.

# 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("steppedatatools/eu-grid-energy-procurement-radar").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("steppedatatools/eu-grid-energy-procurement-radar").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 steppedatatools/eu-grid-energy-procurement-radar --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,steppedatatools/eu-grid-energy-procurement-radar"
        }
    }
}

```

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/c5jxvcT21xwfX3080/builds/xX8P3IMYriLr60LxG/openapi.json
