# EU Tenders Watch: New TED Notices, CPV Labels & EUR Values (`taskfold/ted-tenders-watch`) Actor

Build a daily TED shortlist with labelled CPV sectors, EUR values where published, and named watches that skip notices you've already seen. Filter by country, CPV code and keyword.

- **URL**: https://apify.com/taskfold/ted-tenders-watch.md
- **Developed by:** [Jasmine Nguyen · Taskfold](https://apify.com/taskfold) (community)
- **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 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?

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 Tenders Watch** finds new **EU public tenders** and **government contracts in Europe** published on **TED** (Tenders Electronic Daily). Filter by **CPV code**, country, keyword, date and value.

- Get a daily shortlist of new TED notices for your sector and countries.
- Read English CPV labels alongside the buyer's title or TED's English headline.
- Compare stated values in EUR using ECB reference rates; unknown values stay empty.
- Enable **Only new since last run** and give each watch its own name.
- Schedule in Apify and export the rows to CSV, Excel or your own integration.

Try it now: click **Start**. The form is prefilled with CPV `72000000` (IT services), so your first run returns the newest IT tenders in under a minute.

Data comes from the **official TED Search API**. No scraping, no login, no proxy.

> Source: TED, Publications Office of the EU. Data changed by this Actor (see "Credit, changes made"). Not affiliated with or endorsed by the European Union or the Publications Office of the EU.

### What does EU Tenders Watch do?

- 🔎 **Searches TED public procurement notices** by CPV code, buyer country, keyword, publication date and value.
- 🇬🇧 **English headline where TED has one.** The buyer's English title if there is one, otherwise TED's English headline (country and sector in English, the buyer's own words in their language). The original is kept.
- 🏷️ **Adds CPV labels.** Every CPV code gets its English name (9,454 codes from the official EU list).
- 💶 **Converts values to EUR** with the European Central Bank reference rate of the run day.
- 🧹 **Skips repeats.** Duplicate rows inside a run are dropped, and a named watch skips notices it already returned.
- 🔔 **Only new since last run.** Schedule it and get only fresh tenders each time.
- 📤 **Export** to JSON, CSV or Excel, call it via the Apify API, or send results to email, Slack, Zapier, Make or a webhook.

### Who is it for? Use cases

- **Bid and sales teams** at IT, consulting, construction or service firms: get a daily list of open EU tenders in your CPV codes and countries.
- **Lead generation**: find public buyers that are spending money in your sector right now.
- **Contract award research**: switch to "Contract awards" to see who won, for which buyer, and for how much.
- **Tender alert services and agencies**: run one watch per client, each with its own name.
- **Data teams**: feed clean EU procurement data into a CRM, sheet or BI tool through the API.

### How to get EU tender alerts

1. Open the **Input** tab. Add a CPV code (for example `72000000` for IT services), a country, a keyword, or all three.
2. Set **Maximum results**. You pay $0.003 per result, so this is also your cost cap (see Pricing).
3. Click **Start**. Download the dataset as JSON, CSV or Excel, or call it from the API.
4. For alerts: tick **Only new since last run**, give the watch a name, and add a **Schedule** (for example every morning).

Quick answers:

- **A CPV code with trailing zeros also matches everything below it.** `72000000` covers all IT services (72xxxxxx). `72200000` covers software programming and consultancy.
- **Several CPV codes or countries are OR.** Different filters are AND. Example: (CPV 72000000 or 48000000) and (DE or FR) and keyword "cloud".
- **Notice type.** Default is open tenders (contract notices). Switch to "Contract awards" to see who won, or "All".
- **Newest first.** If you cap the results, you get the newest notices.
- **Advanced:** the `advancedQuery` box takes any TED expert-search condition, added with AND (for example `contract-nature=services`). See the [TED API docs](https://docs.ted.europa.eu/api/latest/index.html).

### What data you get: example output

One row per TED notice. Example (shortened):

```json
{
  "noticeId": "741024-2026",
  "title": "Denmark – Software package and information systems – Rammeaftale for Standardsoftware - 26/21024",
  "titleSource": "ted-english-headline",
  "titleOriginal": "Rammeaftale for Standardsoftware - 26/21024",
  "buyerName": "Energinet",
  "buyerCountry": "DK",
  "cpvMain": "48000000",
  "cpvMainLabel": "Software package and information systems",
  "estimatedValue": 320000000,
  "currency": "DKK",
  "valueEur": 42810509.98,
  "deadline": "2026-11-03",
  "procedureTypeLabel": "Open procedure",
  "tedUrl": "https://ted.europa.eu/en/notice/-/detail/741024-2026",
  "source": "Source: TED, Publications Office of the EU"
}
```

Every row carries the credit line `Source: TED, Publications Office of the EU`. Full field list: the Output tab (dataset schema).

### Why use this TED tenders tool

| Problem with raw TED data | What this Actor does |
|---|---|
| Titles come in 24 languages | `title` is the most English version TED has: the buyer's English title if there is one, otherwise TED's own English headline (country and CPV label in English, then the original title in its language). The original text is kept in `titleOriginal`. |
| CPV codes are numbers | `cpvLabels` gives the English name of every code (9,454 codes bundled from the official EU list). |
| Values come in 20+ currencies and in 5 different fields | One `estimatedValue` + `currency` + `valueType`, plus `valueEur` using the European Central Bank rate of the run day. If no rate exists, `valueEur` stays empty. We never guess. |
| The same notice shows up again and again | Duplicate rows inside a run are dropped. With **Only new since last run**, each named watch skips ids it already returned. Two runs of the same watch at the same moment can still overlap, so schedule one at a time. |
| You only want what is new | Turn on **Only new since last run**. The Actor remembers what it gave you. Schedule it daily and you get only fresh notices. |
| Errors are cryptic | Bad input gives a plain message ("CPV code 'abc' is not valid. Use 8 digits..."). TED errors are passed on with TED's own words. |

### Input examples

IT tenders in Germany and France, only new, for a daily schedule:

```json
{
  "cpvCodes": ["72000000"],
  "countries": ["DE", "FR"],
  "daysBack": 2,
  "sinceLastRun": true,
  "stateKey": "it-de-fr",
  "maxItems": 100
}
```

Cybersecurity or cloud tenders worth at least EUR 500,000:

```json
{
  "keywords": ["cybersecurity", "cloud"],
  "keywordMode": "any",
  "keywordScope": "title",
  "minValueEur": 500000,
  "daysBack": 7,
  "maxItems": 200
}
```

Who won? Contract awards for software:

```json
{
  "cpvCodes": ["48000000"],
  "noticeStage": "awards",
  "daysBack": 30,
  "maxItems": 500
}
```

#### Input fields

| Field | What it does |
|---|---|
| `cpvCodes` | 8-digit CPV codes (OR). |
| `countries` | Buyer countries, 2-letter codes (OR). |
| `keywords`, `keywordMode`, `keywordScope` | Words to find; any or all; in full text or title only. |
| `noticeStage` | `contracts` (default), `awards`, `all`. |
| `daysBack` or `publishedFrom` / `publishedTo` | Date range. Default: today and the 2 days before. |
| `minValueEur`, `maxValueEur`, `includeUnknownValue` | Value filter in EUR. Many notices state no value, so by default they are kept. |
| `sinceLastRun`, `stateKey` | Only-new mode and the name of the watch. Use one name per schedule. |
| `includeDescription` | Adds the description text (original language). |
| `maxItems` | Result cap. Default 100, hard cap 5,000 per run. |

### Tender alerts: how "Only new since last run" works

- The first run uses your normal date range and saves the ids it returned.
- Later runs start from the last publication date and skip every id already returned.
- If a run stops early (your `maxItems` or cost limit), the date does not move forward, so nothing is lost. The next run continues where you stopped.
- If you change the filters, the memory resets and the Actor tells you so in the log.

### How much does it cost? Pricing

Pay per event: **$3.00 per 1,000 results** plus **$0.005 per run start**. You pay only for what you get.

| Event | Price |
|---|---|
| Actor start | $0.005 per run |
| Result (one row in the dataset) | $0.003 per result |

**Cost per 1,000 results: $3.00 + $0.005 start = $3.005.** Platform usage (compute, storage) is included in these prices; you do not pay it on top.

| Run | Cost |
|---|---|
| Default run (100 results) | $0.305 |
| 500 results | $1.505 |
| 1,000 results | $3.005 |
| **Worst case: 5,000 results (the hard cap)** | **$15.005** |
| Empty run (nothing new) | $0.005 |

Cost alarms, built in:

- **`maxItems` defaults to 100 and cannot exceed 5,000 per run.** The run stops at that number, newest notices first.
- **Apify "Maximum cost per run"** is respected. If you set a budget, the Actor stops when it is used up and tells you in the status message.
- **Scheduled watch?** Turn on **Only new since last run**. A daily watch with a CPV filter usually returns a handful of rows, so it costs cents a month.
- Check **Billing > Usage** in Apify Console for your real spend.

### Good to know (honest limits)

- **English titles are English only in the CPV part.** TED translates the country and the sector name. The buyer's own wording stays in its language unless the buyer also published an English title (about 1 in 9 notices). `titleSource` tells you which case you have.
- **`cpvMain` is the first code listed**, not a ranked "main" code. A notice matches your CPV filter if any of its lots has that code.
- **Values are what the buyer wrote.** Many notices have none. Estimated values are not contract prices.
- **EUR conversion** uses today's ECB rate, not the rate on the notice date. Use it to compare size, not for accounting.
- **TED can be slow or busy.** The Actor waits and retries politely (about one request per second, longer after a "too many requests" answer). If TED stays down, the run fails with a clear message and you can run again later.
- **Range limit.** TED lets us read 15,000 results per query. A wide range is read one day at a time. One single day with more than 15,000 matches needs a filter.
- This Actor reads public procurement notices. It is not legal or tendering advice. Check the notice on TED before you bid.

### FAQ

**Is this legal and allowed?** Yes. It uses the official public TED Search API (no scraping, no login) and TED's reuse terms, with the credit below.

**What is TED?** Tenders Electronic Daily, the official EU platform where public buyers publish their procurement notices (all tenders above the EU value thresholds, and some below). This Actor reads it through the official API.

**What is a CPV code?** The Common Procurement Vocabulary: 8-digit codes that say what is being bought. `72000000` is IT services, `45000000` is construction work. Every row shows the English label of its codes.

**Do I get all TED notices?** You get the notices that match your filters and are published in your date range. TED itself decides what is published. Always check the notice on TED before you bid.

**Why is my title partly in another language?** Only about 1 in 9 buyers publish an English title. For the rest, TED's English headline translates the country and sector, and the buyer's text stays in its language. `titleSource` tells you which case it is.

**Why is `valueEur` empty?** The notice states no value, or no exchange rate exists for its currency. We never guess.

**How do I get tender alerts by email or Slack?** Tick **Only new since last run**, name the watch, add an Apify Schedule, and connect an integration (email, Slack, Zapier, Make, webhook).

**Can I run several watches?** Yes. Give each its own watch name.

**Can I use it from my own code?** Yes. Call it through the Apify API or the Apify clients for Python and JavaScript, and read the dataset as JSON or CSV.

**What if TED is down?** The Actor retries politely, then fails with a plain message. A failed run charges only the start event. Run it again later.

**How do I report a problem or ask for a custom version?** Use the Issues tab on this Actor page. Include the run link.

### Credit, changes made, and terms

**Source: TED, Publications Office of the EU.** Notices are published on [TED](https://ted.europa.eu) by the Publications Office of the European Union, which allows reuse, including commercial reuse, with credit. See the [TED legal notice](https://ted.europa.eu/en/legal-notice).

**Changes made (transformations).** This is not the original data. We changed it in these ways:

- English titles: the buyer's English title if given, otherwise TED's English headline. The original is kept in `titleOriginal`.
- CPV labels: English names added for every CPV code, from the official EU CPV list.
- EUR conversion: `valueEur` is calculated from the stated value with the European Central Bank reference rate of the run day.
- Duplicates removed, fields renamed and flattened, dates normalised.

Every row carries `source`: "Source: TED, Publications Office of the EU". If you republish this data, keep the credit and say that it was changed. The Actor does not use EU logos or emblems.

**Not affiliated.** This Actor is an independent tool. It is not affiliated with, endorsed by, or operated by the European Union or the Publications Office of the EU.

### For developers

- Python 3.13, Apify SDK. Code is in `src/`: `ted_client.py` (polite API client), `query.py` (input to TED query), `normalize.py` (one clean row), `pipeline.py` (dedupe, since-last-run), `main.py` (Actor).
- Tests: `python -m pytest` (40 unit tests with a real saved TED response in `tests/fixtures/`).
- Live reliability check: `python scripts/reliability.py 30` (writes `launch/reliability-30.md`).

# Actor input Schema

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

8-digit CPV codes. A code with trailing zeros also matches everything below it: 72000000 covers all IT services (72xxxxxx). Leave empty for all sectors.

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

Only tenders from buyers in these countries. Leave empty for all countries.

## `keywords` (type: `array`):

Words or phrases to look for, for example: cloud, cybersecurity. Works in any language TED has.

## `keywordMode` (type: `string`):

'Any' finds notices with at least one keyword. 'All' needs every keyword.

## `keywordScope` (type: `string`):

'Full text' is wider. 'Title only' is stricter and gives fewer false hits.

## `noticeStage` (type: `string`):

Contracts = open calls you can bid on. Awards = who won. All = everything.

## `daysBack` (type: `integer`):

Used when 'Published from' is empty. 2 means today and the 2 days before.

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

First publication date, format 2026-10-25. Overrides 'last N days'.

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

Last publication date, format 2026-10-25. Empty means today.

## `minValueEur` (type: `integer`):

Keep tenders worth at least this much. Other currencies are converted with the European Central Bank rate of the run day.

## `maxValueEur` (type: `integer`):

Keep tenders worth at most this much.

## `includeUnknownValue` (type: `boolean`):

Many notices do not state a value. Turn off to drop them when you use a value filter.

## `sinceLastRun` (type: `boolean`):

Remembers what you already got and returns only new notices. Best for scheduled runs.

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

Use a different name for each watch you schedule, so they do not share memory.

## `includeDescription` (type: `boolean`):

Adds the short procurement description (original language). Rows get larger.

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

You pay $0.003 per result, so this is also your cost cap. Default 100 (about $0.31 with the start fee). Hard limit 5,000 per run (at most about $15). Newest notices come first.

## `advancedQuery` (type: `string`):

A TED expert-search condition added with AND, for example: contract-nature=services. See docs.ted.europa.eu.

## Actor input object example

```json
{
  "cpvCodes": [
    "72000000",
    "48000000"
  ],
  "keywords": [
    "cloud",
    "cybersecurity"
  ],
  "keywordMode": "any",
  "keywordScope": "fullText",
  "noticeStage": "contracts",
  "daysBack": 2,
  "includeUnknownValue": true,
  "sinceLastRun": false,
  "stateKey": "default",
  "includeDescription": false,
  "maxItems": 100
}
```

# Actor output Schema

## `results` (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 = {
    "cpvCodes": [
        "72000000"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("taskfold/ted-tenders-watch").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"] }

# Run the Actor and wait for it to finish
run = client.actor("taskfold/ted-tenders-watch").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"
  ]
}' |
apify call taskfold/ted-tenders-watch --silent --output-dataset

```

## MCP server setup

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

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/FsS8oNpERhpO8j5py/builds/NXWQDENzHF29DU4PA/openapi.json
