# TED EU Tenders Scraper (Tenders Electronic Daily) (`knotty_mistveil/ted-eu-tenders`) Actor

EU public procurement notices from the official TED (Tenders Electronic Daily) API: every buyer name and email, CPV, deadline, value, link.

- **URL**: https://apify.com/knotty\_mistveil/ted-eu-tenders.md
- **Developed by:** [Jason](https://apify.com/knotty_mistveil) (community)
- **Categories:** Business, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.00 / 1,000 notice returneds

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## TED EU Tenders Scraper (Tenders Electronic Daily)

Pulls EU public procurement notices from **TED (Tenders Electronic Daily)**, the official EU procurement journal (Supplement to the Official Journal of the EU), through the Publications Office's public **TED Search API v3**. No API key, no browser automation, no login. You get one flat JSON record per notice with the buyer, country, CPV codes, deadline, estimated value, contact email and a link to the notice on ted.europa.eu, filtered by date window, notice type, buyer country, CPV, keyword and minimum value. TED publishes roughly 3,000 to 3,600 new notices per working day, about 1,500 to 2,000 of them contract notices (measured against the live API for the working days of 28 August to 7 September 2026), so a daily run with the defaults is a complete feed of new EU tenders.

### What you get

Every record contains:

- `id` - TED publication number (for example `612553-2026`), `source` (`ted`), `url` - the English notice page on ted.europa.eu, `pdfUrl`, `xmlUrl`
- `publicationDate`, `dispatchDate`, `ojsIssue` (OJ S issue, for example `171/2026`), `noticeVersion`, `noticeIdentifier`, `procedureIdentifier`
- `title` in the notice's original language with `titleLanguage` (ISO 639-3, for example `fra`), plus `noticeTitleEn` - TED's English heading (`Country – CPV label – title`)
- `noticeType` (eForms code such as `cn-standard`, `can-standard`, `pin-only`), `formType` (`competition`, `result`, `planning`, ...), `contractNature` (`works`, `supplies`, `services`), `procedureType` (`open`, `restricted`, `neg-w-call`, ...), `legalBasis` (CELEX number of the directive)
- `buyerName`, `buyerCountry` (ISO alpha-3), `buyerCity`, `buyerEmail`, `buyerWebsite` - all five describe the **same** organisation, the lead buyer (the first buyer listed on the notice)
- `buyerCount` and the full arrays `buyerNames`, `buyerCountries`, `buyerEmails`, `buyerWebsites` - joint procurements list every participating authority (see "Joint procurement" below)
- `mainCpv`, `cpvCodes` (array, de-duplicated across lots), `placeOfPerformanceCountry`, `placeOfPerformanceCountries`
- `deadline` - earliest tender-receipt deadline across lots, ISO 8601 with the buyer's UTC offset
- `estimatedValue` (procedure total, or the sum of lot values when no total is given), `estimatedValueCurrency`
- `description` (procedure description, first 1,000 characters), `descriptionLanguage`, `lotCount`
- `fetchedAt` - ISO timestamp of the run

Multilingual fields prefer English when the buyer supplied it; otherwise the original language is used and its code is kept. HTML entities that TED leaves in notice text (`&amp;`, `&apos;`, `&#8211;`, and the occasional double-escaped `&amp;quot;`) are decoded, and buyer websites published without a scheme (`www.example.pl`) are prefixed with `https://` so every URL is clickable.

#### Joint procurement (multiple buyers on one notice)

About 3% of notices are joint procurements run by one authority on behalf of others: EU institutions buying together, or a national central purchasing body buying for its hospitals, schools and municipalities. TED returns the buyer details for those as parallel lists, and this Actor keeps them intact:

- `buyerName` / `buyerCountry` / `buyerCity` / `buyerEmail` / `buyerWebsite` are the **lead buyer** - the first organisation on the notice, and always one and the same organisation across all five fields.
- `buyerCount` is how many buying organisations the notice lists (1 for an ordinary notice; 39 for notice `612350-2026`, an EU joint call for financial IT systems; 541 for `615921-2026`, a Croatian central purchasing body).
- `buyerNames`, `buyerCountries`, `buyerEmails`, `buyerWebsites` are de-duplicated lists covering **every** buyer, so no contact is lost. For `612350-2026` that is 39 institution names across 19 countries.

Because a joint notice has several buyer countries, the `countries` filter matches a notice when **any** of its buyers is in one of your countries. The record's `buyerCountry` is the lead buyer's, which for such a notice can be a country you did not ask for - check `buyerCountries` to see the buyer that matched. In a live run filtered to `["DEU","NLD"]`, all 400 records had `DEU` or `NLD` in `buyerCountries`, and exactly one (the EU joint call above) had a lead `buyerCountry` of `BEL`.

### Input

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `daysBack` | integer | `2` | Look-back window in days on the publication date, ending today. Ignored when `dateFrom`/`dateTo` are set. |
| `dateFrom` | string | - | Earliest publication date, `YYYY-MM-DD`, inclusive. |
| `dateTo` | string | today | Latest publication date, `YYYY-MM-DD`, inclusive. |
| `noticeTypes` | string\[] | `["cn-standard","cn-social"]` | eForms notice-type codes. Contract notices: `cn-standard`, `cn-social`, `cn-desg`. Prior information: `pin-only`, `pin-buyer`, `pin-rtl`, `pin-cfc-standard`, `pin-cfc-social`. Awards and changes: `can-standard`, `can-social`, `can-desg`, `can-modif`, `can-tran`, `veat`. Also `qu-sy`, `subco`, `corr`, `brin-eeig`, `brin-ecs`. Empty list = all types. |
| `countries` | string\[] | all | Buyer country, ISO 3166-1 alpha-3 (`DEU`, `FRA`, `NLD`, `POL`, `ESP`, `ITA`, `IRL`, ...). |
| `cpvPrefixes` | string\[] | all | CPV codes or prefixes, 2 to 8 digits. `72` matches every IT-services code; `45` construction; `33` medical; `48` software; `79` business services. |
| `keywords` | string\[] | none | Full-text terms, any match. Sent to TED as a full-text query (`FT~"term"`), which searches the whole notice text in every language. |
| `strictKeywordMatch` | boolean | `false` | Also require the keyword in the title, lot titles, description or buyer name. Much narrower than TED's own search - see "Limits and freshness". |
| `minValue` | integer | `0` | Keep only notices whose estimated procedure or lot value is at least this amount (notice currency, usually EUR). |
| `onlyLatestVersions` | boolean | `true` | Skip superseded versions of corrected notices. |
| `maxItems` | integer | `500` | Stop after this many notices. Newest publication numbers come first. |

Example - German and Dutch IT tenders worth at least EUR 1 million from the last week:

```json
{
  "daysBack": 7,
  "countries": ["DEU", "NLD"],
  "cpvPrefixes": ["72", "48"],
  "minValue": 1000000,
  "maxItems": 1000
}
```

Invalid codes fail fast with a clear error before any request is made, and the API's own validation errors (unknown country, notice type or CPV) are surfaced verbatim.

### Output example

A real record from a live run on 2026-09-07 (OJ S issue 172/2026), unchanged:

```json
{
  "id": "616186-2026",
  "source": "ted",
  "url": "https://ted.europa.eu/en/notice/-/detail/616186-2026",
  "publicationDate": "2026-09-07",
  "dispatchDate": "2026-09-03",
  "ojsIssue": "172/2026",
  "noticeVersion": 1,
  "noticeIdentifier": "6f882a8a-4cea-413a-b63b-a20714de0345",
  "procedureIdentifier": "8761c053-e873-4203-b04f-56b396082f73",
  "title": "TEN2600157 - CfT for Construction of new Community Facility for use as a Men's and Women's Shed at Beckscourt View, Drumbannan, Bailieborough, Co. Cavan, A82 D928 for Cavan County Council.",
  "titleLanguage": "eng",
  "noticeTitleEn": "Ireland – Construction work for social services buildings – TEN2600157 - CfT for Construction of new Community Facility for use as a Men's and Women's Shed at Beckscourt View, Drumbannan, Bailieborough, Co. Cavan, A82 D928 for Cavan County Council.",
  "noticeType": "cn-standard",
  "formType": "competition",
  "contractNature": "works",
  "procedureType": "open",
  "legalBasis": "32014L0024",
  "buyerName": "Cavan County Council",
  "buyerCountry": "IRL",
  "buyerCity": "Cavan",
  "buyerEmail": "kevingsmith@cavancoco.ie",
  "buyerWebsite": "https://www.cavancoco.ie",
  "buyerCount": 1,
  "buyerNames": [
    "Cavan County Council"
  ],
  "buyerCountries": [
    "IRL"
  ],
  "buyerEmails": [
    "kevingsmith@cavancoco.ie"
  ],
  "buyerWebsites": [
    "https://www.cavancoco.ie"
  ],
  "mainCpv": "45215200",
  "cpvCodes": [
    "45215200",
    "45210000",
    "45262800"
  ],
  "placeOfPerformanceCountry": "IRL",
  "placeOfPerformanceCountries": [
    "IRL"
  ],
  "deadline": "2026-09-24T12:00:00+01:00",
  "estimatedValue": 100000,
  "estimatedValueCurrency": "EUR",
  "description": "TEN2600157 - CfT for Construction of new Community Facility for use as a Men's Shed and Women's Shed, together with Alterations to Existing Building for Disabled WC Accommodation at Beckscourt View, Drumbannan, Bailieborough, Co. Cavan, A82 D928 for Cavan County Council. Please refer to Tender Documents for further details.",
  "descriptionLanguage": "eng",
  "lotCount": 1,
  "pdfUrl": "https://ted.europa.eu/en/notice/616186-2026/pdf",
  "xmlUrl": "https://ted.europa.eu/en/notice/616186-2026/xml",
  "fetchedAt": "2026-09-07T06:11:18.378Z"
}
```

`estimatedValue` is `null` when the buyer did not publish a value (common in France and Germany) and `0` when the buyer explicitly entered zero.

The joint-procurement notice described above looks like this (buyer block only, trimmed):

```json
{
  "id": "612350-2026",
  "title": "Development, Implementation, Maintenance and Operation of financial IT Systems",
  "buyerName": "European Commission, DG BUDG - Budget",
  "buyerCountry": "BEL",
  "buyerCity": "Brussels",
  "buyerEmail": "BUDG-PROCUREMENT-CELL@ec.europa.eu",
  "buyerWebsite": "https://ec.europa.eu/info/departments/budget_en",
  "buyerCount": 39,
  "buyerNames": ["European Commission, DG BUDG - Budget", "EP - European Parliament", "European External Action Service (EEAS)", "..."],
  "buyerCountries": ["BEL", "LVA", "DEU", "FRA", "ROU", "DNK", "ESP", "ITA", "IRL", "LUX", "SVK", "HUN", "MLT", "NLD", "GRC", "AUT", "SVN", "EST", "PRT"]
}
```

The run also writes a `SUMMARY` record to the key-value store with the number of notices scanned, pushed and filtered, whether the strict keyword check was on, how many notices had more than one buyer, the total TED reported for the query (`0` rather than `null` when the query genuinely matches nothing), the resolved date window and the exact expert-search query used.

### Pricing

Pay per result: **US$0.003 per notice delivered** (US$3 per 1,000 notices). You are charged only for records pushed to the dataset; empty runs cost nothing. Runs on the Apify free plan are limited by the platform's free-tier usage allowance.

### Data source and licence

- Source: TED Search API v3, `POST https://api.ted.europa.eu/v3/notices/search`, operated by the Publications Office of the European Union. Public, anonymous, documented at <https://api.ted.europa.eu/swagger-ui/index.html> (spec `https://api.ted.europa.eu/api-v3.yaml`) and <https://docs.ted.europa.eu/api/latest/index.html>.
- Licence: the TED legal notice (<https://ted.europa.eu/en/legal-notice>, "Copyright notice", © European Union, 1998-2026) states that the Commission's reuse policy is implemented by Commission Decision 2011/833/EU of 12 December 2011 on the reuse of Commission documents (<http://data.europa.eu/eli/dec/2011/833/oj>) and that, unless otherwise noted, the procurement notices published in the Supplement to the Official Journal "can be freely reused, for commercial or non-commercial purposes". The same notice places SIMAP/TED system metadata in the public domain (CC0 1.0) and licenses the websites' editorial content under CC BY 4.0. The data.europa.eu catalogue entry for TED (<https://data.europa.eu/data/datasets/ted-1>) records the licence as "European Commission reuse notice" (COM\_REUSE). Attribute the source as "© European Union, TED (Tenders Electronic Daily), <https://ted.europa.eu>" and indicate any changes you make. TED logos may not be reused. Buyer contact details are published by the buyers themselves in the notice; use them in line with GDPR and your local marketing rules.

### Limits and freshness

- TED releases one OJ S issue per working day (Monday to Friday, except Publications Office holidays), early in the morning Central European time. Notices published in an issue carry that issue's date; the API serves them as soon as the issue is out. Run daily after 09:00 CET with `daysBack: 2` for a gap-free feed.
- The Actor pages with the API's ITERATION mode (250 notices per page), so there is no cap on the number of notices per query beyond your `maxItems`. Results are sorted by publication number descending (newest first).
- Date filters use TED's publication date in Central European time. When `dateTo` is not set, the upper bound is extended by one day so the latest issue is never missed because of the UTC offset.
- `keywords` uses TED's full-text index, which covers all languages and the whole notice text, and by default that server-side result is what you get. `strictKeywordMatch: true` re-checks each notice against the fields this Actor returns (title, lot titles, descriptions, buyer name) and is far narrower: in a live run for `"cloud"` over 7 days, TED matched 180 notices and the strict check kept 55, dropping 69%; for `"kubernetes"` it dropped 40% and for `"cybersecurity"` 8%. Use it only when you want tightly on-topic results and accept losing notices whose keyword sits in a part of the notice that is not returned here. The `filteredOutLocally` count and the `strictKeywordMatch` flag are reported in the run summary.
- Long runs page with an iteration token. If TED expires or rejects that token part-way through (`EXPIRED_ITERATION_TOKEN` / `INVALID_ITERATION_TOKEN`), the Actor restarts paging from a fresh token at the last publication number it delivered instead of failing the run; up to 5 such restarts are made and the result is identical to an uninterrupted run.
- `estimatedValue` follows what the buyer declared: procedure total first, otherwise the sum of lot values. Currencies are not converted.
- The TED API publishes no formal rate limit; the Actor keeps a short pause between pages and retries on 429/5xx with back-off.
- Only the search-index fields are returned. Full lot-level detail (award criteria, tenderers, documents) is in the XML linked in `xmlUrl`.

### Use cases

- Daily lead feed of new EU tenders in your CPV codes and countries, with buyer emails for bid/no-bid outreach - including every participating authority on joint procurements, not just the lead buyer.
- Market intelligence: track award notices (`can-standard`) to see which buyers award what, at what value.
- Compliance and research: archive all notices for a country or sector, or rebuild a history with `dateFrom`/`dateTo`.

### Support

Open an issue on the Actor's Issues tab on Apify Store. Replies within 14 days.

# Actor input Schema

## `daysBack` (type: `integer`):

Look-back window in days on the TED publication date, ending today. Ignored when Date from / Date to are set. TED publishes on working days, so 2 covers the latest issue plus one.

## `dateFrom` (type: `string`):

Earliest publication date, YYYY-MM-DD (inclusive). Overrides Days back.

## `dateTo` (type: `string`):

Latest publication date, YYYY-MM-DD (inclusive). Defaults to today.

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

TED/eForms notice-type codes. Default: contract notices only (cn-standard, cn-social). Other codes: cn-desg (design contest), pin-only, pin-buyer, pin-rtl, pin-cfc-standard, pin-cfc-social (prior information), qu-sy (qualification system), can-standard, can-social, can-desg, can-modif, can-tran (awards and modifications), veat, brin-eeig, brin-ecs, subco, corr. Set to an empty list for all types.

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

ISO 3166-1 alpha-3 codes of the buyer's country, for example DEU, FRA, NLD, POL, ESP, ITA, IRL. Empty = all countries.

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

Common Procurement Vocabulary codes or prefixes (2 to 8 digits). A prefix such as 72 matches every CPV code starting with 72 (IT services); 45 = construction, 33 = medical, 48 = software, 79 = business services. Empty = all.

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

Full-text search terms (any match). Sent to TED as a full-text query (FT~) that searches the whole notice text in every language. Empty = no keyword filter.

## `strictKeywordMatch` (type: `boolean`):

Off by default. When on, a keyword must also appear in the notice title, lot titles, description or buyer name, not just anywhere in the notice text. This is much narrower than TED's own full-text search and typically discards most matches (about 69% for the term "cloud"), so only turn it on when you want tightly on-topic results.

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

Keep only notices whose estimated procedure or lot value is at least this amount (in the notice's own currency, usually EUR). 0 = no minimum.

## `onlyLatestVersions` (type: `boolean`):

Return only the latest version of each notice (skips superseded versions of corrected notices).

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

Stop after this many notices. You are charged per notice delivered. Newest notices come first.

## Actor input object example

```json
{
  "daysBack": 2,
  "noticeTypes": [
    "cn-standard",
    "cn-social"
  ],
  "countries": [],
  "cpvPrefixes": [],
  "keywords": [],
  "strictKeywordMatch": false,
  "minValue": 0,
  "onlyLatestVersions": true,
  "maxItems": 500
}
```

# Actor output Schema

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

All notices returned by the run, one JSON object each. Append ?format=csv or ?format=xlsx to download a spreadsheet.

## `summary` (type: `string`):

Counts, the date window used, source freshness and any per-source warnings, written at the end of the run.

# 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 = {
    "daysBack": 2,
    "noticeTypes": [
        "cn-standard",
        "cn-social"
    ],
    "countries": [],
    "cpvPrefixes": [],
    "keywords": [],
    "maxItems": 500
};

// Run the Actor and wait for it to finish
const run = await client.actor("knotty_mistveil/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 = {
    "daysBack": 2,
    "noticeTypes": [
        "cn-standard",
        "cn-social",
    ],
    "countries": [],
    "cpvPrefixes": [],
    "keywords": [],
    "maxItems": 500,
}

# Run the Actor and wait for it to finish
run = client.actor("knotty_mistveil/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 '{
  "daysBack": 2,
  "noticeTypes": [
    "cn-standard",
    "cn-social"
  ],
  "countries": [],
  "cpvPrefixes": [],
  "keywords": [],
  "maxItems": 500
}' |
apify call knotty_mistveil/ted-eu-tenders --silent --output-dataset

```

## MCP server setup

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