# TenderFeed: EU Public Tenders Scraper (TED API) (`tenderfeed/ted-eu-tenders`) Actor

Search EU public procurement tenders from TED (Tenders Electronic Daily) by keyword, CPV, country, date and value. Normalized JSON/CSV with buyers, deadlines, values. GDPR-safe defaults. MCP-ready.

- **URL**: https://apify.com/tenderfeed/ted-eu-tenders.md
- **Developed by:** [TenderFeed](https://apify.com/tenderfeed) (community)
- **Categories:** Lead generation, Open source, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.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?

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 Tenders Scraper (TED API) by TenderFeed

Search **EU public procurement tenders** from **TED (Tenders Electronic Daily)**, the official journal of every public contract above EU thresholds across the 27 EU member states plus EEA countries. Filter by **keyword, CPV code, buyer country, notice type, publication date, deadline and estimated value**, and get clean, normalized JSON, CSV or Excel with **buyer organisation, deadlines, values and document links** (contact person details optional, off by default for GDPR).

Built by **TenderFeed** ([tenderfeedhq.com](https://tenderfeedhq.com)) on the **official TED Search API v3**. No API key, no login, no HTML scraping, no CAPTCHAs.

### What can you do with it?

- **Find new EU tenders daily** for your industry (IT, construction, medical, consulting, energy...) and send them to Slack, email, Google Sheets or your CRM.
- **Lead generation for B2G sales teams**: buyer organisation and city in every row; contact name, email and phone on request (`includeBuyerContacts`).
- **Competitive intelligence**: pull contract award notices (`result`) to see who wins which contracts, and for how much.
- **Market research**: count tenders by CPV code, country and value to size public-sector demand.
- **AI agents and RAG**: call this Actor as an **MCP tool** (via the Apify MCP server) so Claude, ChatGPT or your own agent can search live EU tenders.

### Why TenderFeed?

- **Official data only**: the free TED Search API v3 from the EU Publications Office.
- **One schema across countries**: the same fields as our [SAM.gov](../sam-gov-opportunities) and [UK tenders](../uk-tenders) Actors, so you can merge US, UK and EU tenders in one sheet.
- **English-first**: picks titles and descriptions in your preferred languages and falls back to the original.
- **Value normalisation**: a numeric `estimatedValue` + `currency` (procedure estimate, total value, or the sum of lot estimates).
- **Robust**: cursor pagination (beyond the 15,000-result page limit), retries with backoff, and rate-limit handling.

### Input

| Field | Example | Notes |
|---|---|---|
| `keywords` | `["software", "cloud"]` | Full-text search in any language |
| `keywordMode` | `any` | `any` or `all` |
| `cpvCodes` | `["72000000"]` | Parent codes include children (72000000 = all IT services) |
| `countries` | `["DE", "FR", "NL"]` | ISO alpha-2 buyer countries |
| `noticeTypes` | `["competition"]` | `competition` (calls for tender), `result` (awards), `planning`, `change`... |
| `publishedFrom` / `publishedTo` | `"14 days"`, `2026-10-01` | Default: last 30 days |
| `onlyOpen` | `true` | Only tenders with a deadline today or later |
| `minValue` / `maxValue` | `1000000` | In the notice currency (mostly EUR) |
| `maxResults` | `250` | 0 = no limit |
| `customQuery` | `classification-cpv IN (72000000) AND buyer-country=DEU` | TED expert query, overrides the filters |

```json
{
  "keywords": ["software", "cloud"],
  "countries": ["DE", "FR", "NL"],
  "noticeTypes": ["competition"],
  "publishedFrom": "14 days",
  "onlyOpen": true,
  "maxResults": 250
}
```

### Output

One row per notice. Real example from a live run (2026-10-29):

```json
{
  "source": "ted",
  "sourceId": "711842-2026",
  "url": "https://ted.europa.eu/en/notice/-/detail/711842-2026",
  "title": "ITDZ Berlin: Zutrittskontrollsystem",
  "noticeType": "cn-standard",
  "status": "active",
  "procedureType": "open",
  "contractType": "supplies",
  "buyerName": "IT-Dienstleistungszentrum Berlin (AöR)",
  "buyerCountry": "DE",
  "buyerCity": "Berlin",
  "cpvCodes": ["35113300", "72250000"],
  "publishedAt": "2026-10-15",
  "deadlineAt": "2026-11-16",
  "estimatedValue": 442600.0,
  "currency": "EUR",
  "documentsUrl": "https://www.meinauftrag.rib.de/public/DetailsByPlatformIdAndTenderId/platformId/2/tenderId/211194"
}
```

Full field list (shared TenderFeed schema): `source, sourceId, ocid, url, title, description, noticeType, status, procedureType, contractType, buyerName, buyerCountry, buyerCity, buyerContactName, buyerContactEmail, buyerContactPhone, cpvCodes, naicsCodes, otherClassificationCodes, placeOfPerformance, publishedAt, deadlineAt, estimatedValue, currency, setAside, documentsUrl, submissionUrl, awardedValue, awardees, scrapedAt`.

Export as JSON, CSV, Excel, XML or RSS, or read it via the Apify API. The example uses the default `includeBuyerContacts: false`; with `true`, this notice also carries `"buyerContactEmail": "ausschreibungen@itdz-berlin.de"`.

### Buyer contacts and GDPR (`includeBuyerContacts`)

By default (`includeBuyerContacts: false`) the Actor returns **organisation-level data only**: buyer name, country, city, documents and submission links. Contact person names, email addresses and phone numbers are removed from the output, from the optional `raw` payload, and email addresses embedded in description text are masked as `[email removed]`.

Set `includeBuyerContacts: true` to keep the contact name, email and phone exactly as the authority published them. Only do this if you have a lawful basis under the GDPR (for example legitimate interest for B2G sales) and will honour objection and erasure requests. In a live test (40 TED notices, 2026-10-29) contacts-on returned an email on 40/40 rows, a phone on 40/40 and a named person on 23/40; contacts-off returned 0 of each while keeping `buyerName` on 40/40.

### Daily alerts ("only new" monitoring mode)

Turn on `onlyNew` and give the search a `monitoringId` (e.g. `"it-tenders-daily"`), then schedule the Actor daily. Each run returns, and charges for, only notices you have not received before. Pair it with an Apify integration (Slack, email, Google Sheets, Zapier, Make) to get a ready-made tender alert.

### Pricing

**Pay per result: $2.00 per 1,000 tenders** ($0.002 per row), plus Apify's small run-start fee. A daily alert returning 50 new tenders costs about **$3 a month**. Set *Max cost per run* to cap spending; the Actor stops cleanly at your limit.

### Use it as an MCP tool for AI agents

Add the Apify MCP server (`https://mcp.apify.com`) to Claude Desktop, Cursor or any MCP client and enable this Actor. Your agent can then ask: *"Find open IT tenders in Germany above EUR 500k closing this month"*.

### FAQ

**Is this legal?** Yes. TED data is published by the EU Publications Office for re-use (Commission Decision 2011/833/EU). Personal contact details are excluded unless you set `includeBuyerContacts: true`; if you do, you are the controller for that data and must process it in line with the GDPR.

**How fresh is the data?** TED publishes new notices every working day; the Actor queries the live API at run time.

**Can I schedule it?** Yes, use Apify Schedules (e.g. every morning) with `publishedFrom: "1 day"`.

**Something missing?** Open an issue on the Actor page. TenderFeed answers within one business day.

# Actor input Schema

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

Full-text search terms (any language). Example: \["software", "cloud"]. Leave empty to match all notices.

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

Match notices containing ANY of the keywords, or ALL of them.

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

Common Procurement Vocabulary codes (8 digits). Parent codes include their children, e.g. 72000000 = all IT services.

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

ISO 3166 alpha-2 country codes of the contracting authority, e.g. \["DE", "FR", "NL"]. Empty = all EU/EEA countries.

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

competition = open calls for tender, result = contract awards, planning = prior information notices, change = corrections.

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

YYYY-MM-DD or relative (e.g. "7 days"). Default: last 30 days.

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

YYYY-MM-DD. Leave empty for today.

## `onlyOpen` (type: `boolean`):

Keep only notices whose submission deadline is today or later.

## `minValue` (type: `integer`):

In the notice's own currency (mostly EUR).

## `maxValue` (type: `integer`):

In the notice's own currency (mostly EUR).

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

Many notices do not publish a value. Keep them when a value filter is set?

## `preferredLanguages` (type: `array`):

ISO 639-2 codes used to pick titles/descriptions, in order. Falls back to the original language.

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

Maximum number of tenders to save. 0 = no limit.

## `includeBuyerContacts` (type: `boolean`):

Include named contact persons, emails and phone numbers published in the notices (buyerContactName/Email/Phone). Off by default for GDPR/UK GDPR data minimisation: you then get organization-level info only (buyer name, city, country, notice and document links). Turn on only if you have a lawful basis (e.g. legitimate interest for B2G bid contact) to process this personal data.

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

Remember what earlier runs delivered and return (and charge for) only notices you have not received yet. Ideal for scheduled daily alerts.

## `monitoringId` (type: `string`):

Name of the watch-list memory used by 'Only new'. Use a different ID per saved search/schedule.

## `customQuery` (type: `string`):

Raw TED expert-search query; overrides all filters above. Example: classification-cpv IN (72000000) AND buyer-country=DEU AND publication-date>=20260101

## `scope` (type: `string`):

ALL = every notice, ACTIVE = notices still active, LATEST = latest notices only.

## `includeRawData` (type: `boolean`):

Adds the original TED API record under `raw`.

## Actor input object example

```json
{
  "keywords": [
    "software"
  ],
  "keywordMode": "any",
  "noticeTypes": [
    "competition"
  ],
  "onlyOpen": false,
  "includeUnknownValue": true,
  "preferredLanguages": [
    "eng"
  ],
  "maxResults": 20,
  "includeBuyerContacts": false,
  "onlyNew": false,
  "scope": "ALL",
  "includeRawData": false
}
```

# Actor output Schema

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

All normalized records from the default dataset, opened in the Overview view.

# 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"
    ],
    "maxResults": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("tenderfeed/ted-eu-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"],
    "maxResults": 20,
}

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,tenderfeed/ted-eu-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/Pa9d5O1kx14znXYQx/builds/Vo1HP4Fn32wTbBSbI/openapi.json
