# EU Tender Radar — TED & TenderNed new tenders (`benelux-signals/eu-tender-radar`) Actor

Find new public procurement tenders from TED (all EU) and TenderNed (Netherlands) via their official open APIs. Filter by keyword, CPV, country, NUTS region and date; get clean, deduplicated records with buyer, CPV, value, deadline and official link. Only-new mode for daily alerts.

- **URL**: https://apify.com/benelux-signals/eu-tender-radar.md
- **Developed by:** [Benelux Signals](https://apify.com/benelux-signals) (community)
- **Categories:** Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $7.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.
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 Tender Radar — TED & TenderNed new tenders

**EU Tender Radar** finds new public procurement tenders across Europe in seconds. It searches **TED (Tenders Electronic Daily)**, the official EU journal for tenders from all 27 EU member states plus EEA countries, and **TenderNed**, the official Dutch government tender platform. You get one clean, deduplicated list of tenders with the title, buyer, country, NUTS region, CPV codes, estimated value, procedure type, publication date, submission deadline and a link to the official notice.

Both sources are queried through their **official public APIs**. The actor does not scrape web pages, does not use proxies and does not need any login or API key.

### Who is EU Tender Radar for?

- **Bid managers and sales teams** at companies that sell to governments: IT, construction, consultancy, facility services, healthcare, mobility and more.
- **Tender consultants and bid-writing agencies** who monitor opportunities for several clients.
- **Start-ups and SMEs** who want a cheap, automated alternative to paid tender alert subscriptions.
- **Market researchers, journalists and analysts** who study public spending by sector, country or region.
- **Developers** building tender alert, CRM or lead-generation tools who need normalized tender data in JSON, CSV or Excel.

### What does EU Tender Radar do?

1. It builds an official query for each selected source from your keywords, CPV codes, countries, NUTS regions and date window.
2. It downloads matching notices (newest first) using the APIs' own pagination until all matches are read or your maximum is reached, with automatic retries, rate-limit handling and a safety cap on the number of pages.
3. It normalizes everything into a single format: ISO country codes, 8-digit CPV codes with English labels, ISO dates, deadlines with time zone, and harmonized procedure and notice types.
4. It **removes cross-source duplicates.** Most Dutch EU-level tenders are published on both TenderNed and TED. EU Tender Radar links the two copies via the TED publication number, the TenderNed link and a title + buyer fingerprint, and returns one merged record with both URLs.
5. It drops older versions of the same procedure (for example change notices) and, by default, tenders whose deadline has already passed.
6. In **only-new mode** it remembers every tender it has delivered in a named key-value store. Each scheduled run returns only tenders you have not seen before, which makes it a ready-made daily tender alert.

### How to use EU Tender Radar

1. Enter one or more **keywords** (for example `software`, `cloud`, `cleaning`) and/or **CPV codes** (for example `72000000` for IT services; check digits are optional).
2. Choose **countries** (ISO codes such as `NL`, `DE`, `BE`, `FR`) and, optionally, **NUTS regions** (for example `NL32` for Noord-Holland or `DEA` for Nordrhein-Westfalen).
3. Set the date window: the last *N* days (default **3**), or a fixed from/to date.
4. Choose your sources (TED, TenderNed or both) and the maximum number of results.
5. Run the actor, or schedule it daily with **Only new since last run** switched on and a **monitor name** for each saved search.
6. Export the dataset as JSON, CSV, Excel or HTML, or connect it to Make, Zapier, Slack, Google Sheets or e-mail through the Apify integrations.

#### Input example

```json
{
    "keywords": ["software", "cloud"],
    "cpvCodes": ["72000000"],
    "countries": ["NL", "BE"],
    "publishedWithinDays": 3,
    "sources": ["ted", "tenderned"],
    "noticeScope": "calls-for-competition",
    "excludeExpired": true,
    "onlyNew": true,
    "monitorName": "it-benelux",
    "maxResults": 100
}
```

Keywords are combined with OR, and so are CPV codes. Keywords, CPV codes, countries and regions are combined with each other using AND. CPV codes match hierarchically, so `72000000` also matches `72212000`.

#### Output example

Each tender is one dataset item:

```json
{
    "id": "TED-735368-2026",
    "source": "TED",
    "sources": ["TED", "TenderNed"],
    "title": "Veilig Mailen en uitwisselen van bestanden",
    "buyerName": "Shared Service Centrum Ons",
    "buyerCity": "Zwolle",
    "country": "NL",
    "countryName": "Netherlands",
    "regionCodes": ["NL211"],
    "cpvCodes": ["72260000", "48223000", "64216120"],
    "mainCpvCode": "72260000",
    "mainCpvLabel": "Software-related services",
    "estimatedValue": 1805000,
    "currency": "EUR",
    "procedureType": "open",
    "contractType": "services",
    "noticeType": "contract-notice",
    "publicationDate": "2026-10-23",
    "deadline": "2027-01-04T13:00:00+01:00",
    "url": "https://ted.europa.eu/en/notice/-/detail/735368-2026",
    "tenderNedUrl": "https://www.tenderned.nl/aankondigingen/overzicht/445651",
    "language": "nl",
    "tedPublicationNumber": "735368-2026",
    "tenderNedId": "445651",
    "buyerReference": "25.O.152",
    "isEuProcedure": true
}
```

The dataset has two views. **Overview** is a compact table with clickable links. **Details** shows all fields, including the description, the submission URL and the original procedure and notice type labels.

#### Output fields

| Field | Description |
|---|---|
| `id` | Stable ID: `TED-<publication number>` or `TENDERNED-<id>` |
| `source` / `sources` | Primary source, and every source in which the tender was found |
| `title`, `description` | Title and short description in the original language |
| `buyerName`, `buyerCity` | Contracting authority (an organisation, not a person) |
| `country`, `countryName`, `regionCodes` | ISO 3166 country of the buyer and NUTS codes for the place of performance |
| `cpvCodes`, `mainCpvCode`, `mainCpvLabel` | CPV 2008 codes (8 digits) and the English label of the main code |
| `estimatedValue`, `currency` | Estimated contract value if the buyer published it |
| `procedureType`, `contractType`, `noticeType` | Harmonized values such as `open`, `restricted`, `services`, `contract-notice` |
| `publicationDate`, `deadline` | Publication date (YYYY-MM-DD) and submission deadline (ISO 8601 with offset) |
| `url`, `tedUrl`, `tenderNedUrl`, `submissionUrl` | Official notice pages and the e-procurement link for documents and bids |
| `language` | ISO 639-1 language of the notice |

### Pricing

EU Tender Radar uses **pay-per-event** pricing with two events:

| Event | Price | When |
|---|---|---|
| `tender-result` | **$0.01** | per tender delivered to your dataset |
| `apify-actor-start` | **$0.005** | once per run start (the actor never uses more than 1 GB of memory) |

There is no extra charge for platform usage. Apify Store discounts on paid plans make each result cheaper: **$0.009** on Bronze, **$0.008** on Silver and **$0.007** on Gold.

- Duplicates that are merged across TED and TenderNed are charged **once**.
- In only-new mode, tenders you have already received are **not charged again**.
- The actor respects the **maximum cost per run** you set in Apify. It stops fetching as soon as your limit is reached and never delivers more results than you are willing to pay for.

#### Bill-shock protection

Large searches can match thousands of tenders (all EU countries, no keywords, a long window). The defaults keep a run small:

- **Max results = 100** by default, so a run with default settings costs at most 100 × $0.01 + $0.005 = **$1.005**.
- **Published in the last N days = 3** by default, so the first run of a new monitor only looks back 3 days instead of weeks. Raise it (up to 365) only if you really want a backlog.
- Your **maximum cost per run** in Apify is always enforced on top of that.

#### Cost table (Free/Starter prices)

| Results delivered in a run | Results | Run start | Total per run |
|---|---|---|---|
| 0 (nothing new) | $0.00 | $0.005 | **$0.005** |
| 10 | $0.10 | $0.005 | **$0.105** |
| 25 | $0.25 | $0.005 | **$0.255** |
| 100 (default max) | $1.00 | $0.005 | **$1.005** |
| 500 | $5.00 | $0.005 | **$5.005** |
| 1,000 | $10.00 | $0.005 | **$10.005** |

**Worked example:** you run a daily monitor for IT tenders in the Benelux with only-new mode switched on. The first run (last 3 days) returns 40 tenders: 40 × $0.01 + $0.005 = **$0.405**. After that, assume 12 new matching tenders per working day: 12 × $0.01 + $0.005 = $0.125 per run, or roughly 22 working days × $0.125 = **$2.75 per month**. A run with no new tenders costs only the $0.005 start fee.

### FAQ

#### Which countries are covered?

TED covers all EU member states, the EEA countries and the EU institutions. It contains every tender above the EU thresholds and many below them. TenderNed adds national Dutch tenders below the EU thresholds, which are often not on TED at all.

#### How fresh is the data?

Both APIs are queried live during each run. TenderNed usually publishes Dutch tenders a few days before they appear on TED. EU Tender Radar returns the TenderNed version first, and once the TED version appears it recognises it as the same tender.

#### Why is the estimated value sometimes empty?

Buyers are not required to publish an estimated value, and many do not. TenderNed's open API does not provide values at all. A value of 0 is treated as "not disclosed" and returned as empty.

#### How does only-new mode work?

The actor stores the IDs of delivered tenders (TED number, TenderNed ID, procedure ID and a title + buyer fingerprint) in a named key-value store. Give each saved search its own `monitorName` so that the monitors do not interfere with each other. IDs are kept for about 400 days.

#### Does the actor return contact persons?

No. EU Tender Radar deliberately leaves out contact person names, e-mail addresses and phone numbers, and it removes them from descriptions. It returns only organisational data: the buyer's name, city and official links.

#### What happens if an API changes?

The actor retries temporary errors and respects rate limits. If TED drops a field, the actor continues without it. If one source fails completely, you still get the results from the other source, and the failure is reported in the run log and in the `RUN_SUMMARY` record.

#### Can I get award notices or prior information notices?

Yes. Set **Notice types** to *Calls + planning / market consultations* for prior information notices and market consultations, or to *All notice types* to include award, modification and other notices.

### Is it legal to use this data?

Yes. The actor uses only **public procurement notices** that public authorities are legally required to publish, through the official open interfaces of the Publications Office of the European Union (TED) and the Dutch government (TenderNed). TED data may be reused free of charge under the EU's open-data and reuse policy, with attribution to the source. Please respect each source's terms of use and fair-use limits. Tender documents can contain their own conditions, so always consult the official notice and the procurement documents before you submit a bid. EU Tender Radar does not collect personal data about contact persons.

# Actor input Schema

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

Free-text search terms (any of them may match). Searched in the full notice text on TED and in title/description on TenderNed. Use the language of the notices (e.g. Dutch for NL: "software", "schoonmaak", "beveiliging"). Leave empty to get everything matching the other filters.

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

Common Procurement Vocabulary codes (8 digits, check digit optional), e.g. 72000000 (IT services), 45000000 (construction work), 79000000 (business services). Parent codes include all sub-codes. Multiple codes are combined with OR.

## `sources` (type: `array`):

TED = Tenders Electronic Daily, the official EU journal (all EU/EEA countries, above-threshold tenders). TenderNed = the official Dutch government tender platform (also national, below-threshold Dutch tenders). Tenders on both are merged into one record.

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

ISO country codes of the contracting authority, e.g. NL, BE, DE, FR (3-letter codes like NLD also work). Leave empty for all countries. TenderNed is skipped automatically if NL is not included.

## `regions` (type: `array`):

Optional place-of-performance filter using NUTS codes, e.g. NL32 (Noord-Holland), NL33 (Zuid-Holland), DE21 (Oberbayern), BE1 (Brussels). Codes match their sub-regions.

## `publishedWithinDays` (type: `integer`):

Publication window when no explicit start date is given. Default 3 days keeps the first run small and cheap; for a daily only-new monitor 2–3 days is enough (it also catches late TED publications). A larger window can match thousands of tenders, each billed as a result up to Max results.

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

Start of the publication window (YYYY-MM-DD). Overrides "Published in the last N days".

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

End of the publication window (YYYY-MM-DD). Leave empty for today.

## `noticeScope` (type: `string`):

"Open calls" returns only tenders you can bid on (contract notices). "Calls + planning" adds prior information notices and Dutch market consultations. "All" also includes award notices, corrections and modifications.

## `excludeExpired` (type: `boolean`):

Skip notices with a submission deadline in the past.

## `onlyNew` (type: `boolean`):

Remember which tenders were already delivered (in a named key-value store in your account) and only return new ones. Ideal for a daily schedule: you only pay for tenders you haven't seen.

## `monitorName` (type: `string`):

Name for this saved search's memory, e.g. "it-tenders-nl". Runs with the same name share the list of seen tenders. If empty, a name is derived from your filters.

## `stateStoreName` (type: `string`):

Named key-value store used to remember seen tender IDs.

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

Maximum number of tenders to return in this run. Each result is one billable event ($0.01), so the default of 100 caps a run at about $1.00 plus the $0.005 start fee. The run also stops at your maximum charge per run.

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

Include the (shortened) contract description. Contact e-mail addresses and phone numbers are always removed.

## Actor input object example

```json
{
  "keywords": [
    "software"
  ],
  "sources": [
    "ted",
    "tenderned"
  ],
  "countries": [
    "NL"
  ],
  "publishedWithinDays": 3,
  "noticeScope": "calls-for-competition",
  "excludeExpired": true,
  "onlyNew": false,
  "stateStoreName": "eu-tender-radar-state",
  "maxResults": 20,
  "includeDescription": true
}
```

# Actor output Schema

## `overview` (type: `string`):

All tender records in the default dataset, opened in the 'overview' table view (published date, title, buyer, country, CPV, value, deadline, procedure, source, link).

## `runSummary` (type: `string`):

JSON summary of the run (per-source counts, skipped already-seen tenders, errors) stored under the RUN_SUMMARY key.

# 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 = {
    "keywords": [
        "software"
    ],
    "countries": [
        "NL"
    ],
    "publishedWithinDays": 3,
    "maxResults": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("benelux-signals/eu-tender-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 = {
    "keywords": ["software"],
    "countries": ["NL"],
    "publishedWithinDays": 3,
    "maxResults": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("benelux-signals/eu-tender-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 '{
  "keywords": [
    "software"
  ],
  "countries": [
    "NL"
  ],
  "publishedWithinDays": 3,
  "maxResults": 20
}' |
apify call benelux-signals/eu-tender-radar --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,benelux-signals/eu-tender-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/bKRqlhqvlae4HzZVq/builds/Xml0HDcALsArqk4K6/openapi.json
