# EU Tenders & Contract Awards Scraper — TED Procurement (`mochiboo/eu-tenders-ted-scraper`) Actor

Scrape EU public procurement from TED (Tenders Electronic Daily). Find open tenders by CPV code, country, keyword, value and deadline, or mine awarded contracts for winners and prices. Official EU open-data API, English-normalised flat JSON/CSV, company-level only.

- **URL**: https://apify.com/mochiboo/eu-tenders-ted-scraper.md
- **Developed by:** [mochi](https://apify.com/mochiboo) (community)
- **Categories:** Developer tools, Automation, Other
- **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/platform/actors/running/actors-in-store#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

## EU Tenders & Contract Awards Scraper — TED public procurement data

Scrape **EU public procurement** from **TED (Tenders Electronic Daily)**, the European Union's
official journal for public contracts. Two jobs in one actor:

- **Find open tenders** you can still bid on — filtered by CPV code, country, keyword, contract
  value and submission deadline.
- **Mine awarded contracts** — who won, what they were paid, when it was signed, and whether the
  winner was an SME or a large company.

Around **€700 billion** of public contracts are advertised through TED every year, across the EU,
the EEA and candidate countries. This actor turns that into flat, English-normalised JSON/CSV you
can drop straight into a CRM, a spreadsheet, or an AI agent.

Built on TED's **official public API** — EU open data, no API key, no login, no scraping of a
rendered page.

***

### What you get

One flat row per notice. No nested objects, the same columns on every row:

| Field | Description |
|---|---|
| `publicationNumber` | TED publication number, e.g. `449762-2026` |
| `noticeCategory` | `tender`, `award`, `prior-information` or `other` |
| `noticeType` | Raw TED subtype, e.g. `cn-standard`, `can-standard`, `veat` |
| `title` | Notice title, in English (or your chosen language) |
| `buyerName` / `buyerCountry` / `buyerCity` / `buyerWebsite` | The contracting authority |
| `buyerLegalType` | e.g. `body-pl` (body governed by public law) |
| `cpvMain` / `cpvCodes` | TED's declared main CPV plus every classification on the notice |
| `contractNature` | `services`, `supplies` or `works` |
| `procedureType` | `open`, `restricted`, `neg-w-call`, … |
| `publicationDate` | When the notice was published |
| `deadlineDate` / `deadlineDateLatest` | Earliest and latest tender-submission deadline |
| `daysUntilDeadline` / `isOpenForBids` | Computed for you — sort by urgency without post-processing |
| `performanceCountries` / `performanceCities` | Where the work is delivered |
| `estimatedValue` / `estimatedValueCurrency` | The published budget |
| `awardedValue` / `awardedValueCurrency` | What was actually awarded (award notices only) |
| `winnerNames` / `winnerCountries` / `winnerSizes` | The winning companies, and `sme` / `large` |
| `contractConclusionDate` | When the contract was signed |
| `noticeUrl` / `xmlUrl` / `pdfUrl` | English notice page, full XML, and PDF |

***

### Example inputs

**Every IT tender in Germany and France still open for bids, closing before 30 September**

```json
{
  "searchMode": "openTenders",
  "cpvCodes": ["72000000"],
  "countries": ["Germany", "FR"],
  "contractNature": "services",
  "deadlineTo": "2026-09-30",
  "maxResults": 200
}
```

**Who is winning big EU IT contracts?**

```json
{
  "searchMode": "awards",
  "cpvCodes": ["72000000"],
  "minValue": 1000000,
  "maxResults": 500
}
```

**Everything mentioning "cloud migration", anywhere in the EU, in any language**

```json
{ "searchMode": "all", "keywords": "cloud migration", "maxResults": 100 }
```

Run it with empty input `{}` and you get the 20 most recently published open tenders across the
whole EU.

***

### Input reference

| Input | Default | Notes |
|---|---|---|
| `searchMode` | `openTenders` | `openTenders` (deadline still ahead), `awards` (winners & prices), `all` |
| `cpvCodes` | `[]` (all) | A code matches all of its sub-codes, so `72000000` = every IT notice |
| `countries` | `[]` (all) | Buyer country. Name, ISO-2 or ISO-3 — `Germany`, `DE`, `DEU` all work |
| `keywords` | `""` | Full-text phrase, matched across every published language version |
| `contractNature` | `any` | `services`, `supplies`, `works` |
| `minValue` | `0` | Minimum total value, in the notice's own currency |
| `publishedFrom` / `publishedTo` | last 12 months | `YYYY-MM-DD` |
| `deadlineFrom` / `deadlineTo` | today / none | `YYYY-MM-DD` |
| `sortOrder` | `newest` | `oldest` is faster and cheaper for a full-archive pull |
| `language` | `eng` | 3-letter code for translated text: `eng`, `deu`, `fra`, `spa`, … |
| `maxResults` | `20` | `0` = no limit |

#### Handy CPV codes

`72000000` IT services · `45000000` construction · `33000000` medical equipment ·
`79000000` business & management services · `71000000` architectural & engineering ·
`80000000` education & training · `85000000` health & social work · `50000000` repair & maintenance ·
`34000000` transport equipment · `09000000` energy & fuels

***

### Three things this actor gets right

**1. It hands you the newest notices first.** TED's API has no sort parameter and returns matches in
ascending publication-number order — so a naive scraper gives you decade-old notices first. This
actor slices the publication window by day and walks it backwards, so the first row you see is from
today.

**2. `cpvMain` is the real main CPV.** TED lumps the main and additional classifications into one
list. Taking the first one is wrong for **23%** of notices (measured on a 250-notice sample). This
actor reads TED's declared main classification instead.

**3. Estimated value and awarded value are never conflated.** TED publishes both under the same
`total-value` key, meaning "budget" on a call for tenders and "price paid" on an award notice. They
are separate columns here, so you never mistake one for the other.

***

### Data coverage and honest limits

- **Winner data starts with eForms.** Award notices published before 2024 carry **no** structured
  winner name, size or signature date — that data simply is not in the source. Measured fill rates:
  0% for 2016–2023, 58% in February 2024, 86–88% from April 2024, and 90–96% from 2025 onward.
  `awards` mode therefore defaults its window to 2024-01-01; set `publishedFrom` earlier if you want
  the older archive, and the run log will warn you that the winner columns will be empty.
- **Not every notice publishes a value.** Roughly 80–90% do. `minValue` silently excludes the rest.
- **Buyer and winner names are not translated.** TED machine-translates notice *titles* into all 24
  EU languages, but organisation names are published only in their own language. Asking for `eng`
  falls back to the original rather than dropping the name.
- **`newest` mode costs more requests than `oldest`**, because it queries day by day. For a full
  archive pull, use `sortOrder: "oldest"`.

### Privacy — organisation-level data only

This actor returns **legal entities only**: contracting authorities and the companies that won
public contracts, which are published as a matter of public record in the EU's official journal.

TED also exposes personal fields — contact persons, personal e-mail addresses, and beneficial-owner
records. **This actor never requests them**, so they cannot appear in your dataset. That is enforced
in code (the requested field list is fixed) and asserted by the test suite, which additionally scans
every output row for anything resembling an e-mail address.

### Source and licence

Data comes from the European Union's TED Search API v3 (`api.ted.europa.eu`), reused under the
[European Commission's reuse policy](https://ted.europa.eu/en/simap/legal-notice). This actor is not
affiliated with, endorsed by, or connected to the European Union or the Publications Office.

# Actor input Schema

## `searchMode` (type: `string`):

'openTenders' = calls for tenders whose submission deadline has not passed (your live bidding pipeline). 'awards' = contract-award notices, with the winning company and the price it was awarded. 'all' = every notice type, including prior-information notices.

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

Common Procurement Vocabulary codes. A code also matches all of its sub-codes, so 72000000 returns every IT notice. Examples: 72000000 IT services, 45000000 construction, 33000000 medical, 79000000 business services. Leave empty for all sectors.

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

Restrict to buyers in these countries. Country name, ISO-2 or ISO-3 all work (Germany / DE / DEU). Leave empty for all of the EU plus the EEA and candidate countries that publish on TED.

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

Free-text phrase searched across the whole notice, in every published language version. Example: 'cloud migration'. Leave empty to match on the other filters only.

## `contractNature` (type: `string`):

Restrict to service, supply or works contracts.

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

Only notices whose total value is at least this amount, in the notice's own currency (mostly EUR). 0 = no minimum. Note that many notices publish no value at all and are dropped by this filter.

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

Only notices published on/after this date. Format YYYY-MM-DD. Leave blank for no lower bound.

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

Only notices published on/before this date. Format YYYY-MM-DD. Leave blank for no upper bound.

## `deadlineFrom` (type: `string`):

Only notices whose tender-submission deadline is on/after this date. Format YYYY-MM-DD. In 'openTenders' mode this defaults to today, so you can use it to skip tenders closing too soon to bid on.

## `deadlineTo` (type: `string`):

Only notices whose tender-submission deadline is on/before this date. Format YYYY-MM-DD. Use it with 'openTenders' to get everything closing in the next N days.

## `sortOrder` (type: `string`):

'newest' returns the most recently published notices first — TED's API cannot sort, so the actor slices the publication window by day and walks it backwards. 'oldest' is TED's own order and is faster and cheaper for a full-archive pull.

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

Preferred language for translated text (TED machine-translates notice titles into all 24 EU languages). Use a 3-letter code: eng, deu, fra, spa, ita, nld, pol, por... Buyer and winner names are only published in their own language and fall back to it.

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

Stop after this many notices. Keep it low for a fast preview; raise it for a full pull. 0 = no limit.

## Actor input object example

```json
{
  "searchMode": "openTenders",
  "cpvCodes": [
    "72000000"
  ],
  "countries": [
    "Germany"
  ],
  "keywords": "",
  "contractNature": "any",
  "minValue": 0,
  "publishedFrom": "",
  "publishedTo": "",
  "deadlineFrom": "",
  "deadlineTo": "",
  "sortOrder": "newest",
  "language": "eng",
  "maxResults": 20
}
```

# 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": [
        "Germany"
    ]
};

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

# Run the Actor and wait for it to finish
run = client.actor("mochiboo/eu-tenders-ted-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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": [
    "Germany"
  ]
}' |
apify call mochiboo/eu-tenders-ted-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=mochiboo/eu-tenders-ted-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/Xuad6GfuSiuhGxiQO/builds/kQmOjUaoHW476fedw/openapi.json
