# EU Public Tender Search (TED) (`swissdatatools/eu-ted-public-tenders`) Actor

Find open EU public tenders from the official TED API. Filter by keyword, country, value and deadline, then export the buyer, amount, currency and description.

- **URL**: https://apify.com/swissdatatools/eu-ted-public-tenders.md
- **Developed by:** [Matis Monnin](https://apify.com/swissdatatools) (community)
- **Categories:** Business, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.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 Public Tender Search (TED)

Export current EU public-procurement notices from the official TED Search API into a spreadsheet or JSON feed.

The TED website already lets you search for free. Use this Actor when you need the same public notices as rows you can filter, schedule, and download: buyer, title, description, value, currency, deadline, and the official notice link. It does not log in to TED, scrape the website, or add information TED did not publish.

### Why not just use the TED website?

TED is the source, and it stays free. The website is the right place to read one notice. This Actor is the right place to repeat a search:

- Every keyword has to match the title, unless you widen the search.
- The default keeps contract notices with a submission deadline that is still open. Award notices and closed notices stay out.
- Countries can be typed as `Germany`, `DE`, or `DEU`.
- The result is one row per notice, ready for CSV, Excel, a sheet, or an API call.
- Missing amounts, currencies, and descriptions stay `null`. They are never guessed.

### What a precise search looks like

```json
{
  "keywords": ["software"],
  "keywordMatch": "all",
  "keywordScope": "title",
  "countries": ["Germany"],
  "noticePurpose": "competition",
  "onlyOpenSubmissions": true,
  "minimumValue": 100000,
  "publishedWithinDays": 30,
  "maxResults": 25
}
```

| Input | Default | What it changes |
| --- | --- | --- |
| `keywords` | none | Phrases to find. `keywordMatch: all` requires every phrase. `any` keeps a notice when one phrase matches. |
| `keywordScope` | `title` | `description` searches the procedure and lot text. `fullText` is the widest search. |
| `countries` | all countries | Buyer country. Names and ISO codes both work. |
| `cpvCodes` | none | CPV codes such as `72000000` for IT services. |
| `noticePurpose` | `competition` | `award` for results, `planning` for prior notices, `all` for every type. |
| `minimumValue` | `0` | A number above 0 keeps notices with a published estimated or total value at least that high. Notices without a value are left out. |
| `onlyOpenSubmissions` | `true` | Turn off to include closed and historical notices. |
| `publishedWithinDays` | `30` | Publication window, from 1 to 365 days. |
| `maxResults` | `50` | Stop after this many unique notices. Maximum 2,000. |

### Fields

| Field | When TED publishes it |
| --- | --- |
| `publicationNumber`, `tedUrl` | Always |
| `title`, `description` | In the language TED provides. English is preferred when it exists. Descriptions longer than 2,000 characters are shortened. |
| `buyerName`, `buyerCountry`, `buyerCity` | Buyer identity |
| `buyerEmail`, `buyerWebsite` | Public buyer contact, when TED includes it |
| `noticeType`, `procedureType` | For example `cn-standard` and `open` |
| `cpvCodes` | Unique CPV codes |
| `publicationDate`, `deadline` | `YYYY-MM-DD`. The deadline is the submission deadline. |
| `estimatedValue`, `estimatedValueCurrency` | Procedure estimate and its currency, often `EUR` |
| `totalValue`, `totalValueCurrency` | Total value and its currency |

A value without a currency, or a currency without a value, can happen because TED fills those fields separately. Both stay `null` when TED omits them.

### Example

```json
{
  "publicationNumber": "650347-2026",
  "description": "Vertrag zur besonderen Versorgung zur Verbesserung der digitalen, ärztlichen Versorgung für Patienten durch einen elektronischen Kommunikations- und Vernetzungsprozess nach § 140a SGB V über Videosprechstunde",
  "buyerName": "AOK Bayern - Die Gesundheitskasse",
  "buyerCountry": "DEU",
  "buyerCity": "München",
  "buyerEmail": "vergabestelle1@by.aok.de",
  "noticeType": "cn-standard",
  "procedureType": "open",
  "cpvCodes": ["85100000"],
  "deadline": "2026-10-05",
  "estimatedValue": 2600500,
  "estimatedValueCurrency": "EUR",
  "totalValue": 2600500,
  "totalValueCurrency": "EUR",
  "tedUrl": "https://ted.europa.eu/en/notice/-/detail/650347-2026",
  "source": "ted.europa.eu"
}
```

This example comes from fields the official API returned for that publication number. It is not a promise that every notice has an email or a value.

### Pricing

This Actor uses pay per event. Platform usage is included in the price.

- **$0.003 per saved notice**, which is **$3.00 per 1,000 notices**
- **$0.00005 when a run starts**, once per GB of memory, with a minimum of one event
- Empty runs and failed searches do not add notice charges
- Duplicates are not written, so they are not charged

One run that saves 1,000 notices costs $3.00005. One thousand separate runs that each save one notice cost $3.05, because each run has its own start fee.

Examples:

- 25 notices in one run: $0.07505
- 100 notices in one run: $0.30005
- 1,000 notices in one run: $3.00005
- 10,000 notices, in runs of at most 2,000: $30.00025

Apify keeps 20% of event revenue. A measured 10-notice run used 256 MB for 2.5 seconds and cost $0.00019 of platform usage. From that measurement, about $2.38 of the $3.00 remains after Apify’s share and compute. That margin is an estimate, not a measured result for 1,000 notices.

### Limits

- TED decides which fields exist. Many notices have no value and no email.
- The default search is deliberately narrow. Set `noticePurpose` to `all`, `onlyOpenSubmissions` to `false`, or `keywordScope` to `fullText` when you want the wider journal.
- A minimum value excludes notices that do not publish a value, even if the contract is large.
- Descriptions are shortened at 2,000 characters.
- Maximum 2,000 notices per run, and the publication window cannot exceed 365 days.
- You are responsible for your use of the data and for the [TED terms of use](https://ted.europa.eu/).

### FAQ

**Does this replace the TED search page?**
No. It exports the public search result so you can reuse it. Open `tedUrl` to read the official notice.

**Why are there fewer rows than on TED?**
The default keeps contract notices whose submission deadline is still open, and it requires every keyword to appear in the title. TED’s own search is broader.

**Will every row have a budget?**
No. `estimatedValue` and `totalValue` are filled only when TED sends them. Use `minimumValue` above 0 if you want to drop notices without a published amount.

# Actor input Schema

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

Phrases to match. With the default match mode, every phrase must match. Leave empty to use only the country, CPV, type, value, and date filters.

## `keywordMatch` (type: `string`):

All means every phrase must match, so the result list stays specific. Any returns a notice when at least one phrase matches.

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

Title is the precise default. Description searches the procedure and lot text. Full text is the broadest search and returns more notices.

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

Buyer countries as names or ISO codes, for example Germany, DE, or DEU. Leave empty to search all TED countries.

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

Optional Common Procurement Vocabulary codes, for example 72000000 for IT services.

## `noticePurpose` (type: `string`):

Competition keeps contract notices you can still bid on. Award keeps result notices. Planning keeps prior information notices. All keeps every type.

## `minimumValue` (type: `integer`):

Keep notices whose estimated or total value is at least this amount, in the currency TED published. 0 ignores the filter. Notices without a published value are excluded when this is above 0.

## `onlyOpenSubmissions` (type: `boolean`):

Keep notices whose TED submission deadline is today or later. Turn this off to include closed and historical notices.

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

Only notices published in this many days.

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

Stop after this many unique notices.

## Actor input object example

```json
{
  "keywords": [
    "software"
  ],
  "keywordMatch": "all",
  "keywordScope": "title",
  "countries": [
    "Germany"
  ],
  "cpvCodes": [],
  "noticePurpose": "competition",
  "minimumValue": 0,
  "onlyOpenSubmissions": true,
  "publishedWithinDays": 30,
  "maxResults": 25
}
```

# Actor output Schema

## `notices` (type: `string`):

Saved EU public procurement notices.

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

The TED query, the number of notices saved, and the total match count.

# 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"
    ],
    "keywordMatch": "all",
    "keywordScope": "title",
    "countries": [
        "Germany"
    ],
    "noticePurpose": "competition",
    "minimumValue": 0,
    "onlyOpenSubmissions": true,
    "publishedWithinDays": 30,
    "maxResults": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("swissdatatools/eu-ted-public-tenders").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"],
    "keywordMatch": "all",
    "keywordScope": "title",
    "countries": ["Germany"],
    "noticePurpose": "competition",
    "minimumValue": 0,
    "onlyOpenSubmissions": True,
    "publishedWithinDays": 30,
    "maxResults": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("swissdatatools/eu-ted-public-tenders").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"
  ],
  "keywordMatch": "all",
  "keywordScope": "title",
  "countries": [
    "Germany"
  ],
  "noticePurpose": "competition",
  "minimumValue": 0,
  "onlyOpenSubmissions": true,
  "publishedWithinDays": 30,
  "maxResults": 25
}' |
apify call swissdatatools/eu-ted-public-tenders --silent --output-dataset

```

## MCP server setup

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

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/Fy2ufEQeOmAda6E3z/builds/e20UsfDBJthpIZxIf/openapi.json
