# EU Tender Feed (TED) - CPV, country & alert mode (`itulo/ted-tender-feed`) Actor

Structured, deduplicated EU public procurement notices from the official TED Search API, filtered by CPV code, country and date, with an alert mode that returns only new notices since the last run. Organisation-level fields only. Attribution included in every record.

- **URL**: https://apify.com/itulo/ted-tender-feed.md
- **Developed by:** [Italo Armenti](https://apify.com/itulo) (community)
- **Categories:** Business, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 tender notices

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?

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 Tender Feed (TED) — European Public Procurement Notices API (`itulo/ted-tender-feed`)

Search, deduplicate, and monitor official **European Union public procurement notices** from the **TED (Tenders Electronic Daily) Search API v3** (`api.ted.europa.eu/v3/notices/search`; keyless official EU open data). Built for B2G sales pipelines, RFP monitoring, CPV code tracking, and AI procurement agents.

Ships as both a **Pay-Per-Event Apify Actor** (`itulo/ted-tender-feed`) and an open-source **stdio MCP Server** (`src/mcp-server.js`) powered by one shared library (`src/lib/ted.js`).

> **Independent Tool Notice:** Not affiliated with or endorsed by the Publications Office of the European Union. Uses no TED or EU logos and is a structured data feed from one official API.

***

### Key Features for EU Public Procurement & Tender Monitoring

- **CPV Hierarchy & Multi-Country Filtering:** Filter by 8-digit Common Procurement Vocabulary (`cpvCodes`, e.g. `"72000000"` IT services, `"45000000"` construction, `"33000000"` medical equipment), buyer country or place of performance (`FIN`, `SWE`, `DEU`, `FRA`, `NLD`, etc.), and notice category (`competition`, `planning`, `award`, `change`).
- **Procedure Deduplication (`collapseByProcedure`):** Collapses corrigenda and multi-version notices so your CRM or Slack webhook receives only the latest notice per procurement procedure.
- **Stateful Incremental Alert Mode (`alertMode: true`):** Stores seen notice IDs in the named Key-Value Store `ted-tender-feed-alert-state` so daily/hourly scheduled runs emit **only brand-new tenders**.
- **Strict GDPR Data Minimisation:** Requests organisation-level buyer and contract fields only — never natural-person contact points, emails, phones, jury members, or individual tenderer names.

***

### Input Parameters

| Field | Example | Description |
|---|---|---|
| `cpvCodes` | `["72000000"]` | 8-digit CPV codes; parent codes automatically match child codes |
| `countries` | `["FIN", "SWE"]` | ISO alpha-2 or alpha-3 country codes |
| `countryField` | `"buyer"` / `"performance"` | Filter by contracting authority country or place of performance |
| `noticeTypes` | `["competition"]` | Groups: `competition`, `planning`, `award`, `change`, or raw TED subtype codes |
| `publishedFrom` / `publishedTo` | `"2026-10-01"` | `YYYY-MM-DD` range (defaults to last 7 days or since last alert run) |
| `maxResults` | `50` | Maximum notices returned per run (`1`–`1000`) |
| `collapseByProcedure` | `true` | Keep only the newest notice per procurement procedure identifier |
| `alertMode` / `alertStateKey` | `true`, `"nordic-it"` | Emit only unseen notices across scheduled runs |

***

### Sample Output Record

```json
{
  "publicationNumber": "700214-2026",
  "noticeType": "cn-standard",
  "noticeTypeLabel": "Contract notice",
  "title": "Sweden – Software package and information systems – Personal- och lönesystem",
  "buyerNames": ["Vaggeryds kommun"],
  "buyerCountries": ["SWE"],
  "cpvCodes": ["48000000", "72000000"],
  "publicationDate": "2026-10-09",
  "tenderDeadline": null,
  "estimatedValue": { "amount": 9600000, "currency": "SEK" },
  "url": "https://ted.europa.eu/en/notice/-/detail/700214-2026",
  "attribution": "Source: TED - Tenders Electronic Daily (ted.europa.eu), © European Union, 1998-2026. Reused under the European Commission reuse policy (Decision 2011/833/EU); changes: fields selected, normalised and deduplicated.",
  "licenceUrl": "https://ted.europa.eu/en/legal-notice"
}
```

***

### Pay-Per-Event Pricing (`PAY_PER_EVENT`)

| Event Name | Price (USD) | Description |
|---|---|---|
| `actor-start` | **$0.01 / run** | Flat fee for starting an Actor run |
| `tender-notice` | **$0.003 / notice** ($3.00 / 1,000) | Each normalized EU public procurement notice written to the dataset (primary event) |

***

### Data Licence & Attribution Compliance

- **Official Source & Reuse Policy:** Reused under **European Commission Decision 2011/833/EU** on the reuse of Commission documents and CC BY 4.0 ([TED Legal Notice](https://ted.europa.eu/en/legal-notice)).
- **Mandatory Attribution on Every Record:** `Source: TED - Tenders Electronic Daily (ted.europa.eu), © European Union, 1998-2026. Reused under the European Commission reuse policy (Decision 2011/833/EU); changes: fields selected, normalised and deduplicated.`

***

### Connect to Claude Desktop, Cursor & MCP Clients (Model Context Protocol)

You can use **`itulo/ted-tender-feed`** as a live tool inside **Claude Desktop**, **Cursor**, **Windsurf**, or any MCP-compatible AI agent in two ways:

#### Option A: Hosted Apify MCP Server (`@apify/actors-mcp-server`)

Zero local setup required. Add this to your `claude_desktop_config.json` (macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`, Windows: `%APPDATA%\Claude\claude_desktop_config.json`) or Cursor `.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "eu-ted-tenders": {
      "command": "npx",
      "args": [
        "-y",
        "@apify/actors-mcp-server",
        "--actors",
        "itulo/ted-tender-feed"
      ],
      "env": {
        "APIFY_TOKEN": "YOUR_APIFY_API_TOKEN"
      }
    }
  }
}
```

#### Option B: Local Stdio MCP Server (Open-Source CLI)

Run the bundled stdio MCP server (`src/mcp-server.js`) directly on your machine. Exposed MCP tools: `ted_search_tenders`, `ted_new_tenders_alert`, `ted_licence_info`.

```json
{
  "mcpServers": {
    "eu-ted-tenders-local": {
      "command": "node",
      "args": [
        "/absolute/path/to/tools/ted-tender-feed/src/mcp-server.js"
      ]
    }
  }
}
```

***

### n8n, Make.com & REST API Integration (One-Call Sync JSON)

Trigger **`itulo/ted-tender-feed`** synchronously and get the normalized JSON dataset items back in a single HTTP request — ideal for **n8n `HTTP Request` nodes**, **Make.com**, **Zapier**, or cron webhooks:

- **Method:** `POST`
- **Endpoint:** `https://api.apify.com/v2/acts/itulo~ted-tender-feed/run-sync-get-dataset-items?token=YOUR_APIFY_API_TOKEN`
- **Headers:** `Content-Type: application/json`

#### cURL Example

```bash
curl -s -X POST \
  "https://api.apify.com/v2/acts/itulo~ted-tender-feed/run-sync-get-dataset-items?token=${APIFY_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{"cpvCodes":["72000000"],"countries":["FIN","SWE"],"countryField":"buyer","noticeTypes":["competition"],"maxResults":10,"language":"eng","collapseByProcedure":true,"alertMode":false}'
```

***

### LangChain, CrewAI, Node.js & Python SDK Snippets

#### Node.js (`apify-client`)

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('itulo/ted-tender-feed').call({
  "cpvCodes": [
    "72000000"
  ],
  "countries": [
    "FIN",
    "SWE"
  ],
  "countryField": "buyer",
  "noticeTypes": [
    "competition"
  ],
  "maxResults": 10,
  "language": "eng",
  "collapseByProcedure": true,
  "alertMode": false
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### Python (`apify-client` / LangChain `ApifyWrapper` / CrewAI)

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("itulo/ted-tender-feed").call(run_input={
  "cpvCodes": [
    "72000000"
  ],
  "countries": [
    "FIN",
    "SWE"
  ],
  "countryField": "buyer",
  "noticeTypes": [
    "competition"
  ],
  "maxResults": 10,
  "language": "eng",
  "collapseByProcedure": true,
  "alertMode": false
})
items = client.dataset(run["defaultDatasetId"]).list_items().items
for row in items:
    print(row)
```

#### LangChain Python Tool

```python
from langchain_community.utilities import ApifyWrapper

apify = ApifyWrapper()
loader = apify.call_actor(
    actor_id="itulo/ted-tender-feed",
    run_input={
  "cpvCodes": [
    "72000000"
  ],
  "countries": [
    "FIN",
    "SWE"
  ],
  "countryField": "buyer",
  "noticeTypes": [
    "competition"
  ],
  "maxResults": 10,
  "language": "eng",
  "collapseByProcedure": true,
  "alertMode": false
},
    dataset_mapping_function=lambda item: str(item),
)
docs = loader.load()
```

***

### Frequently Asked Questions (FAQ)

#### How does the EU TED Tenders API actor filter public procurement notices?

It queries the official Publications Office of the European Union TED Search API v3 (`api.ted.europa.eu/v3/notices/search`) using Expert Query syntax. You can filter by 8-digit CPV procurement codes (parent CPV codes automatically match child categories), ISO buyer or performance countries (`FIN`, `SWE`, `DEU`, etc.), publication date window, and notice type (`competition`, `planning`, `award`, `change`).

#### How does incremental alert mode (`alertMode: true`) prevent duplicate tenders?

When `alertMode: true` is enabled, the actor persists seen notice publication numbers in a named Apify Key-Value Store (`ted-tender-feed-alert-state`) under your `alertStateKey` and emits only newly published notices since the previous scheduled run.

#### Is personal data stripped for GDPR compliance?

Yes. The tool requests strictly organisation-level fields (`buyer-name`, `buyer-country`, CPV codes, estimated values, deadlines, and official TED notice links) and never requests natural-person contact names, emails, phone numbers, or jury members.

**Related Search Keywords:** EU TED Tenders API · TED Europa API v3 · European Public Procurement Notices API · CPV Code Tender Monitor · EU Government Contracts Feed · B2G Sales Lead Generation Europe · TED MCP Server

# Actor input Schema

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

8-digit CPV codes (e.g. 72000000 = IT services). Parent codes also match child codes.

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

ISO alpha-2 (FI) or alpha-3 (FIN) country codes.

## `countryField` (type: `string`):

Filter by contracting authority (buyer) country or place of performance country.

## `noticeTypes` (type: `array`):

Groups: competition, planning, award, change - or raw TED notice types (cn-standard, can-standard, ...).

## `publishedFrom` (type: `string`):

Start of publication date window (YYYY-MM-DD). Default: 7 days ago (or since last run in alert mode).

## `publishedTo` (type: `string`):

End of publication date window (YYYY-MM-DD). Default: today.

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

Maximum number of deduplicated tender notices to return per run.

## `language` (type: `string`):

ISO 639-2/B code: eng, fin, swe, deu, fra ...

## `collapseByProcedure` (type: `boolean`):

Keep only the newest notice per procurement procedure.

## `alertMode` (type: `boolean`):

Return only notices not seen in previous runs with the same alert key. Schedule the actor daily.

## `alertStateKey` (type: `string`):

Separate alert states for different saved searches.

## Actor input object example

```json
{
  "cpvCodes": [
    "72000000"
  ],
  "countries": [
    "FI",
    "SE"
  ],
  "countryField": "buyer",
  "noticeTypes": [
    "competition"
  ],
  "maxResults": 200,
  "language": "eng",
  "collapseByProcedure": false,
  "alertMode": false,
  "alertStateKey": "default"
}
```

# Actor output Schema

## `results` (type: `string`):

Deduplicated, normalized EU public procurement notices from the official TED Search API.

## `summary` (type: `string`):

Summary metadata and licence attribution stored in the default 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 = {
    "cpvCodes": [
        "72000000"
    ],
    "countries": [
        "FI",
        "SE"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("itulo/ted-tender-feed").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 = {
    "cpvCodes": ["72000000"],
    "countries": [
        "FI",
        "SE",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("itulo/ted-tender-feed").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 '{
  "cpvCodes": [
    "72000000"
  ],
  "countries": [
    "FI",
    "SE"
  ]
}' |
apify call itulo/ted-tender-feed --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,itulo/ted-tender-feed"
        }
    }
}
```

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/jcGqBCVowOvega1FL/builds/z1sSuKMr0RrPQKq73/openapi.json
