# EU TED Tenders Scraper: Public Procurement by CPV & Country (`aeriontsai/ted-tenders`) Actor

Search EU public procurement notices on TED (Tenders Electronic Daily) by CPV code (sub-codes included), buyer country, date range and keyword. One row per notice: buyer, country, CPV, title, estimated value, deadline, procedure type. Contact-person data removed. Not affiliated with the EU.

- **URL**: https://apify.com/aeriontsai/ted-tenders.md
- **Developed by:** [Aerion](https://apify.com/aeriontsai) (community)
- **Categories:** Lead generation, Business
- **Security:** Limited permissions
- **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 TED Tender Notices

Find new EU tender opportunities on TED (Tenders Electronic Daily) without manual searching. Filter by CPV code (sub-codes included), buyer country, publication date and keyword, and get one clean row per notice with buyer, CPV codes, title, estimated and total value, tender deadline, procedure type and framework flag, in your preferred EU language where available. Contact-person data is removed. Ideal for bid teams, procurement consultants and market researchers.

### What does EU TED Tender Notices do?

It searches published public procurement notices of the European Union on TED (Tenders Electronic Daily, the online version of the Supplement to the Official Journal of the EU) and returns one clean row per notice: buyer, country, CPV codes, title, optional description, estimated and total value, tender deadline, procedure type and framework flag.
Filter by CPV code (sub-codes included), buyer country, publication date range and keyword. Titles and descriptions are returned in your preferred EU language when the notice has that version, otherwise in the original language.

Data freshness: TED publishes new notices every working day; this Actor queries the public TED search API at run time, so results reflect what TED returns at that moment.

### Personal data

Notices can contain names, e-mail addresses and phone numbers of contact persons. This Actor never requests those fields, removes any contact field that still appears (also when nested), and removes e-mail addresses and phone numbers found inside titles and descriptions. Web links and host names found in free-text fields (titles and descriptions) are replaced by \[link removed]: links starting with http, https, ftp or www, and host names written in lower case with any valid top-level domain (this also applies to a brand written as a domain inside a title). Organisation names (`buyerName`, `buyerNames`) are kept exactly as published, even when they contain a domain-style brand. Winner/tenderer details are not included at all.
Descriptions are free text written by the buyer and **may contain names of persons**, which cannot be detected automatically. They are therefore **off by default**; turn on `includeDescriptions` only if you need them (e-mail addresses and phone numbers are still removed).

### How much does it cost?

This Actor uses **pay-per-event** pricing: you pay a small fee per run start and per tender notice saved to the dataset (event `tender-notice`). Duplicate or malformed notices are not charged.
Set **Maximum results** (`maxItems`) or a run spending limit to cap your cost; the Actor stops cleanly and keeps the results collected so far.

| Event | When | Price (USD) |
|---|---|---|
| `tender-notice` | one notice saved to the dataset | 0.003 |
| Actor start | charged by the platform when a run starts | 0.00005 |

### Input

| Field | Type | Description |
|---|---|---|
| `cpvCodes` | array of strings | CPV codes, 8 digits (e.g. 72000000). Sub-codes are included. Empty = all. |
| `countries` | array of strings | Buyer country, ISO code with 2 or 3 letters (DE or DEU). Empty = all. |
| `keyword` | string | Optional full-text search. |
| `publishedFrom` / `publishedTo` | string (YYYY-MM-DD) | Publication date range. |
| `publishedWithinDays` | integer | Last N days (default 7), used when `publishedFrom` is empty. |
| `language` | string | Preferred language for titles/descriptions (default English, `eng`). |
| `includeDescriptions` | boolean | Include the procedure description (free text, max 2000 characters). **Off by default.** Free text may contain names of persons. |
| `maxItems` | integer | Stop after this many notices (default 50). |

### Output

Each result is one record in the dataset. Example (real record from a local test run on saved TED data, with `includeDescriptions` turned on; by default `description` and `descriptionLanguage` are null):

```json
{
  "noticeId": "696529-2026",
  "noticeVersion": 1,
  "noticeIdentifier": "4199552c-1e81-40d6-ae0e-82e8b785adf4",
  "noticeType": "cn-standard",
  "noticeSubtype": "16",
  "formType": "competition",
  "publicationDate": "2026-10-08",
  "dispatchDate": "2026-10-06",
  "title": "Prestación de los servicios necesarios para el desarrollo e implantación de una aplicación a medida para realizar los procesos de gestión de fondos con financiación del FONDO SOCIAL EUROPEO+ (FSE+) de la Fundación Biodiversidad.",
  "titleLanguage": "spa",
  "noticeTitle": "Spain – Custom software development services – Prestación de los servicios necesarios para el desarrollo e implantación de una aplicación a medida para realizar los procesos de gestión de fondos con financiación del FONDO SOCIAL EUROPEO+ (FSE+) de la Fundación Biodiversidad.",
  "noticeTitleLanguage": "eng",
  "description": "Los trabajos solicitados incluyen el diseño, desarrollo, integración con otras herramientas, implantación, despliegue y puesta en marcha, documentación, formación y gestión del cambio, y mantenimiento de la aplicación de gestión de fondos FSE+ denominada AGFSE.",
  "descriptionLanguage": "spa",
  "buyerName": "Dirección de la Fundación Biodiversidad",
  "buyerNames": [
    "Dirección de la Fundación Biodiversidad"
  ],
  "buyerCity": "Madrid",
  "buyerCountry": "ESP",
  "buyerCountries": [
    "ESP"
  ],
  "buyerLegalType": "pub-undert-cga",
  "cpvMain": "72230000",
  "cpvCodes": [
    "72230000"
  ],
  "contractNature": "services",
  "procedureType": "open",
  "tenderDeadline": "2026-11-17",
  "estimatedValue": 550000,
  "estimatedValueCurrency": "EUR",
  "estimatedValueBasis": "procedure",
  "totalValue": 550000,
  "totalValueCurrency": "EUR",
  "placeOfPerformanceCountries": [
    "ESP"
  ],
  "placeOfPerformanceNuts": [
    "ES300"
  ],
  "officialLanguages": [
    "spa"
  ],
  "frameworkAgreement": false,
  "source": "TED (Tenders Electronic Daily) / Publications Office of the European Union",
  "attribution": "Source: TED (Tenders Electronic Daily), Supplement to the OJ EU, © European Union, 1998-2026. Reused under Commission Decision 2011/833/EU; reformatted by this Actor (contact data and links in free text removed), may differ from the published notice; only the published notice is authentic. Not affiliated with or endorsed by the EU or the Publications Office.",
  "scrapedAt": "2026-10-09T15:50:49.891Z"
}
```

The output contains no notice page or PDF links, and web addresses in free-text fields are replaced by \[link removed]; organisation names are kept as published and may contain a domain-style brand name. Use `noticeId` (the TED publication number) to look a notice up on TED.

Every run also writes a `RUN_SUMMARY` record (counts, success rate, why the run stopped) and an `errors` dataset with one row per problem (no personal data, no stack traces).

### Data source and license

Source: Tenders Electronic Daily (TED), Supplement to the Official Journal of the European Union, © European Union, 1998-2026. Reused under the European Commission's reuse policy (Commission Decision 2011/833/EU). Data is reformatted by this Actor (fields selected and renamed, contact data and web links in free text removed) and may differ from the published notice. This Actor is not affiliated with or endorsed by the European Union or the Publications Office of the European Union.

TED states that only the electronically signed notices published in the Supplement to the Official Journal of the European Union are authentic, so check the official notice before you rely on it. TED and EU logos are not used.
This Actor uses the public TED search service, respects its fair usage policy and makes at most about 1 request every 1.5 seconds.

### Limitations

- Searches published notices only (no draft or unpublished notices, no documents/attachments).
- Values are copied as published; some notices publish only lot values (`estimatedValueBasis` = `sum-of-lots`) or no value.
- Link detection in free text is pattern-based: unusual spellings (for example a host name written in capitals with a rare ending) may not be recognised.
- Names of persons written inside free text (titles, and descriptions if turned on) cannot be detected automatically; e-mail addresses and phone numbers are removed. Descriptions are off by default for this reason.
- Language tags are copied from TED; occasionally a text is tagged with a different language than it is written in.

### Support

Found a problem or need a field? Open an issue in the **Issues** tab of this Actor. We aim to respond within 48 hours.

***

This Actor was developed with AI assistance and is covered by automated tests.

# Actor input Schema

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

Common Procurement Vocabulary codes, 8 digits (e.g. 72000000 = IT services). Sub-codes are included automatically. Leave empty for all categories.

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

Country of the buying organisation as ISO code, 2 or 3 letters (e.g. DE or DEU). Leave empty for all countries.

## `keyword` (type: `string`):

Optional full-text search in the notice (e.g. software). Quotes and brackets are ignored.

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

First publication date to include, YYYY-MM-DD. If empty, the last N days are used (see below).

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

Last publication date to include, YYYY-MM-DD. Empty = up to today.

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

Used only when 'Published from' is empty.

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

Language for titles, buyer names and descriptions when the notice has that version. Otherwise the notice's original language is used.

## `includeDescriptions` (type: `boolean`):

Off by default. When on, the procedure description is included (up to 2,000 characters). Free text written by the buyer may contain names of persons; e-mail addresses and phone numbers in it are always removed.

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

Stop after this many notices. You are charged per notice, so this also caps your cost.

## Actor input object example

```json
{
  "cpvCodes": [
    "72000000"
  ],
  "countries": [
    "DE"
  ],
  "publishedWithinDays": 7,
  "language": "eng",
  "includeDescriptions": false,
  "maxItems": 50
}
```

# Actor output Schema

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

Normalized records (one per charged result event).

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

JSON summary: counts, success rate, stop reason, billing summary, error counts.

## `errors` (type: `string`):

One record per error (taxonomy code, message, stage, URL). No stack traces.

# 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": [
        "DE"
    ],
    "maxItems": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("aeriontsai/ted-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 = {
    "cpvCodes": ["72000000"],
    "countries": ["DE"],
    "maxItems": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("aeriontsai/ted-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 '{
  "cpvCodes": [
    "72000000"
  ],
  "countries": [
    "DE"
  ],
  "maxItems": 50
}' |
apify call aeriontsai/ted-tenders --silent --output-dataset

```

## MCP server setup

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