# TED Tenders Scraper (EU public procurement, normalized) (`islandata/ted-tenders-eu`) Actor

EU public tenders from TED as clean JSON: one row per notice with CPV codes decoded, values in EUR (ECB rates), deadlines with days left, lots, winners and awarded amounts. Filter by country, CPV, NUTS, keywords; 'only new' monitoring mode. Official API, no personal data.

- **URL**: https://apify.com/islandata/ted-tenders-eu.md
- **Developed by:** [Radu Coman](https://apify.com/islandata) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.10 / 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.
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

## TED Tenders Scraper: EU public procurement, normalized for people and AI agents

**Every public tender published in the EU's Tenders Electronic Daily (TED), as one clean JSON row per notice: buyer, CPV codes decoded to English, values converted to EUR with the day's ECB rates, deadline with days left, lots, winners and awarded amounts.** Export EU tenders and contract awards to JSON, CSV or Excel, monitor new calls for competition by country and CPV every day, and feed them to Google Sheets, n8n, Make or an AI agent, without learning the TED API or the eForms schema.

TED publishes about 3,000 procurement notices a day from all EU and EEA countries. The official API returns them as sparse, multilingual, per-lot arrays with 1,800 possible fields. This Actor turns that into a flat, predictable schema you can filter, chart or hand to an AI agent without reading the eForms specification.

### Who uses it

- **Bid managers and tender consultants** who need a daily list of new EU tenders in their sector (CPV) and countries, in Excel or Google Sheets.
- **Sales teams** selling to the public sector who want to know which contracts were awarded, to whom and for how much.
- **Analysts and researchers** studying procurement volumes, values and winners across the EU.
- **AI agents and automations** (n8n, Make, Zapier, LangChain, MCP) that need flat JSON instead of 1,800 eForms fields.

### What you get per row

| Block | Fields |
|---|---|
| identity | publication number, notice id, TED page URL, PDF (English) and XML links, publication and dispatch dates, form type (competition, result, planning), notice type, procedure type, contract nature |
| text | `title` in the original language (`titleLang`), `titleEn` (TED's English headline: country, CPV, title), `description` |
| `buyer` | names, cities, countries (ISO alpha-3), main activity, buyer profile URL |
| `placeOfPerformance` | countries and NUTS regions |
| `cpv` | every CPV code with its English label, plus `cpvDivisions` (the 2-digit families, e.g. "Construction work") |
| `lots` | title, estimated value, currency, value in EUR, deadline, one entry per lot; `lotCount` |
| `value` | estimated (procedure level or sum of lots), currency, **estimatedEur**, awarded, **awardedEur**, ECB rate date |
| `deadline` | earliest tender deadline, date-time, **daysLeft** |
| `award` | winners and their countries (result notices) |
| `links` | submission portal, documents |
| extras | SME-suitable flag, contract duration in months, source attribution, scrape timestamp |

### Input

- **Buyer countries**: ISO alpha-3 codes (`ESP`, `PRT`, `ROU`, `DEU`, `FRA`, `ITA`…). Empty = all.
- **CPV codes or prefixes**: `45` (construction), `72` (IT services), `33` (medical), or full 8-digit codes.
- **Place of performance (NUTS)**: e.g. `ES70` Canarias, `RO32` Bucharest-Ilfov.
- **Notice kind**: calls for competition (default), results (awards), planning, or all.
- **Keywords**: full-text search, any language.
- **Published in the last N days** (default 3) or explicit `publishedFrom` / `publishedTo`.
- **Expert query**: a raw TED expert-search string, for people who know the syntax; overrides the filters.
- **Max items** (default 500) and **Only new since last run** (monitoring mode, remembers publication numbers per account).

Examples:

```json
{ "countries": ["ESP", "PRT"], "cpvPrefixes": ["72"], "formType": "competition", "publishedSinceDays": 7 }
```

```json
{ "countries": ["ROU"], "formType": "result", "publishedFrom": "2026-10-01", "publishedTo": "2026-10-31", "maxItems": 2000 }
```

```json
{ "nuts": ["ES70"], "formType": "competition", "publishedSinceDays": 1, "onlyNew": true }
```

### Pricing

Pay per event: **$0.003 per notice row**, charged only after the row is saved. No charge for platform usage. A country like Spain publishes roughly 150 to 250 calls for competition per day, so a daily monitor of one country costs about $0.50 to $0.75 per day; a full month of Spanish contract awards (about 4,000 rows) costs about $12.

### Quality and limits

- Every row is validated against a strict schema before it is saved; invalid rows are logged and never charged.
- Measured on real runs (October 2026): 300 notices for Spain and Portugal in 2 seconds, 0 invalid rows; 88 % of competition notices carry an estimated value, 96 % a deadline.
- Values in EUR use the European Central Bank's daily reference rates; when the ECB feed is unavailable, non-EUR values keep their currency and `estimatedEur` is `null`.
- Multilingual texts are returned in the notice's original language, with the English headline TED generates in `titleEn`.
- The TED API rate-limits bursts; the Actor stays at one request per second and retries politely. A page holds 250 notices, so even large monthly extractions take under a minute per thousand rows.
- Personal data: buyer contact persons, phones and emails are intentionally not extracted. Buyers are public bodies; winners are companies.

### Attribution and reuse

Data comes from TED, Publications Office of the European Union (https://ted.europa.eu). Notices can be freely reused for commercial and non-commercial purposes under Commission Decision 2011/833/EU; keep the attribution in `meta.source` when you redistribute.

### How to use it

**In the Apify Console**: choose countries and CPV prefixes, press Start, download the dataset as JSON, CSV or Excel.

**From your code (Apify API)**:

```bash
curl -X POST "https://api.apify.com/v2/acts/islandata~ted-tenders-eu/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"countries": ["ESP"], "cpvPrefixes": ["72"], "publishedSinceDays": 1}'
```

**In n8n, Make or Zapier**: use the Apify node or app, pick `islandata/ted-tenders-eu`, schedule it daily with `onlyNew: true` and map the rows to Google Sheets, Airtable, Notion, Slack or email. That is a complete EU tender alert in three nodes.

**With an AI agent (MCP)**: connect the agent to `https://mcp.apify.com?tools=islandata/ted-tenders-eu`. The schema is flat and small: the agent calls the Actor with a country, a CPV prefix and `onlyNew: true`, then ranks by `value.estimatedEur` and `deadline.daysLeft`.

### FAQ

**Is this the official TED API?** No. It uses the official, free TED Search API and returns its data normalized. You do not need a TED account or API key.

**Which countries are covered?** All EU member states plus EEA and other countries that publish on TED (Norway, Iceland, Switzerland, UK legacy notices…). Use ISO alpha-3 codes.

**Can I get awards (who won) as well as tenders?** Yes: `formType: "result"` returns contract award notices with winners and awarded amounts, when the buyer published them.

**Are the values comparable across countries?** Yes: `estimatedEur` and `awardedEur` use the ECB reference rate of the publication day; the original currency is kept.

**Does it include contact persons?** No. Buyer organisations are public bodies and are included; contact names, phones and emails are not extracted.

### In other languages

- **Español**: licitaciones públicas de la Unión Europea (TED, Diario Oficial) en JSON, CSV o Excel; una fila por anuncio con códigos CPV descifrados, importes en EUR, plazo con días restantes, lotes y adjudicatarios. Filtra por país, CPV, región NUTS y palabras clave; modo «solo nuevos» para alertas diarias.
- **Français**: appels d'offres européens (TED, Journal officiel de l'UE) en JSON, CSV ou Excel; une ligne par avis avec codes CPV décodés, montants en EUR, date limite et jours restants, lots et attributaires. Filtrez par pays, CPV, région NUTS et mots-clés; mode « nouveaux uniquement » pour une veille quotidienne.
- **Deutsch**: EU-Ausschreibungen (TED, Amtsblatt der EU) als JSON, CSV oder Excel; eine Zeile pro Bekanntmachung mit entschlüsselten CPV-Codes, Werten in EUR, Frist mit Resttagen, Losen und Zuschlagsempfängern. Filter nach Land, CPV, NUTS und Stichwörtern; Modus „nur neue“ für tägliche Überwachung.
- **Italiano**: gare d'appalto europee (TED, Gazzetta ufficiale UE) in JSON, CSV o Excel; una riga per avviso con codici CPV decodificati, importi in EUR, scadenza con giorni rimanenti, lotti e aggiudicatari.
- **Português**: concursos públicos da UE (TED, Jornal Oficial) em JSON, CSV ou Excel; uma linha por anúncio com códigos CPV descodificados, valores em EUR, prazo com dias restantes, lotes e adjudicatários.
- **Română**: licitații publice din Uniunea Europeană (TED, Jurnalul Oficial) în JSON, CSV sau Excel; un rând per anunț cu coduri CPV decodate, valori în EUR, termen cu zile rămase, loturi și câștigători. Filtre pe țară, CPV, NUTS și cuvinte-cheie; mod „doar noi” pentru alerte zilnice.

# Actor input Schema

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

Three-letter codes of the buyer's country, e.g. ESP, PRT, ROU, DEU, FRA, ITA. Empty = all EU/EEA countries.

## `cpvPrefixes` (type: `array`):

Common Procurement Vocabulary codes (8 digits) or prefixes, e.g. 45 (construction), 72 (IT services), 33 (medical). Empty = all.

## `nuts` (type: `array`):

NUTS region codes, e.g. ES70 (Canarias), ES51 (Cataluña), RO32 (București-Ilfov). Empty = anywhere.

## `formType` (type: `string`):

competition = tenders you can still bid on; result = who won and for how much.

## `keywords` (type: `string`):

Free-text search in the notice, any language, e.g. "energía solar" or "software".

## `publishedSinceDays` (type: `integer`):

Used when no explicit dates are given. Default 3. The daily volume is large (about 3,000 notices per day across the EU), so keep it small with broad filters.

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

Optional explicit start date; overrides the N days setting.

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

Optional end date.

## `expertQuery` (type: `string`):

A raw TED expert-search query. When set, all the filters above are ignored. Example: FT~("solar") AND buyer-country=ESP AND publication-date>=20261001

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

Stop after this many notices. Each row is one paid 'result' event.

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

Remembers the publication numbers already returned by this Actor (per account) and skips them. Ideal for a daily schedule or an AI agent that watches for new tenders.

## Actor input object example

```json
{
  "countries": [
    "ESP"
  ],
  "cpvPrefixes": [],
  "nuts": [],
  "formType": "competition",
  "keywords": "",
  "publishedSinceDays": 3,
  "expertQuery": "",
  "maxItems": 500,
  "onlyNew": false
}
```

# Actor output Schema

## `rows` (type: `string`):

One row per notice: buyer, CPV with labels, values in EUR, deadline, lots, winners.

## `rowsCsv` (type: `string`):

No description

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

Query used, counts and run economics.

# 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 = {
    "countries": [
        "ESP"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("islandata/ted-tenders-eu").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 = { "countries": ["ESP"] }

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

```

## MCP server setup

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

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/voS22qbyoVwTNQdug/builds/qjYk9r3LMD6HvpP3e/openapi.json
