# EU Tenders Scraper: TED Contract Awards and Winners (`agents001/eu-ted-tenders-feed`) Actor

Returns EU public procurement notices from TED: contract awards with the winning company, buyer, value in EUR, CPV and date, plus open tenders with deadlines. Filter by country, CPV, keyword and value. Daily mode returns only new records.

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

## Pricing

from $1.75 / 1,000 award records

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

## EU Tenders Scraper: TED Contract Awards and Winners

Get EU public procurement data from TED (Tenders Electronic Daily) as clean, flat records:

- **Contract awards**: one record per **winning company**, with the winner's name, country and size, the public buyer, the contract value (also converted to EUR), CPV code and description, award decision date and a link to the notice. A company that just won a public contract is a strong buying signal for sales teams.
- **Open tenders**: one record per **call for competition** that is still open, with buyer, estimated value, CPV, procedure type and the submission deadline.

Filter by buyer country, winner country, CPV code, keyword and minimum value. Schedule it daily with **Only records not returned in previous runs** switched on to get just the new awards and tenders.

### What you get

**Award records** (`recordType: "award"`), one per winning company on a contract award notice:

| Field | Example |
|---|---|
| `winnerName`, `winnerCountry`, `winnerSize` | `ORACLE IBÉRICA, S.R.L.`, `ESP`, `large` |
| `winnerIdentifier` | National company or VAT number, when TED ties it to this winner without doubt |
| `winnersInNotice`, `otherWinners` | `6`, the other winning companies on the same notice (private individuals are left out, see Privacy) |
| `buyerName`, `buyerCountry`, `buyerCity`, `buyerType` | `Gobierno de Zaragoza`, `ESP`, `Zaragoza`, `Local authority` |
| `title`, `titleLanguage` | Procedure title, in English when the buyer published one |
| `cpvDescription`, `mainCpv`, `cpvCodes` | `Database and operating software package`, `48600000` |
| `value`, `currency`, `valueEur`, `valueBasis` | `458411.48`, `EUR`, `458411.48`, `awarded, notice total` |
| `awardDecisionDate`, `publicationDate` | `2026-09-25`, `2026-10-20` |
| `contractType`, `procedureType`, `isFrameworkAgreement`, `lots` | `supplies`, `open`, `false`, `1` |
| `noticeId`, `noticeUrl`, `pdfUrl`, `procedureId` | `722698-2026` and links to the notice on TED |

**Tender records** (`recordType: "tender"`) have the same notice, buyer, CPV and value fields (value is the estimated value), plus:

| Field | Example |
|---|---|
| `deadline` | `2026-12-02T11:00:00Z` (earliest lot deadline, UTC) |
| `deadlineDate`, `daysToDeadline`, `isOpen` | `2026-12-02`, `40`, `true` |
| `participationRequestDeadlineDate` | For restricted and negotiated procedures |

Every record also has `isNew`, `firstSeenAt`, `source`, `licence` and `scrapedAt`. The Output tab has three views: **Contract awards**, **Open tenders** and **All records**.

### Who it is for

- **Sales and business development teams**: companies that just won public contracts often need staff, suppliers, subcontractors, software, equipment and finance. Filter by winner country to cover your territory.
- **Bid teams and consultants** looking for open tenders in their sector, with deadlines.
- **Market analysts** tracking who wins which public contracts, at what value, in which countries.
- **Suppliers** who want to know which competitors are winning and which buyers are spending.

### Quick start

1. Click **Start** with the example input. You get contract awards for IT services and software in Germany, France and the Netherlands from the last 3 publishing days.
2. Pick **What to return**: contract awards, open tenders, or both.
3. Set your filters. Countries take ISO codes (`DE`, `DEU`) or English names (`Germany`). CPV codes cover every code below them, so `72000000` returns all IT services. Keywords search the full notice text.
4. Schedule it daily with **Only records not returned in previous runs** switched on, and send new records to email, Slack, Google Sheets or your CRM with an Apify integration or webhook.

### Input

| Field | What it does | Default |
|---|---|---|
| `mode` | `awards`, `tenders` or `both` | `awards` |
| `daysBack` | Publication window in TED publishing days (Monday to Friday), counting today | 3 |
| `dateFrom`, `dateTo` | A fixed publication date window instead, for example `2026-10-01` | empty |
| `countries` | Buyer countries | all |
| `winnerCountries` | Countries of the winning companies (awards only) | all |
| `cpvCodes` | CPV codes, including every code below them | all |
| `keywords` | Any of these keywords in the notice text. Inside one keyword every word must appear. A trailing `*` matches word endings | none |
| `minValueEur` | Minimum value in EUR. Records without a value are dropped when this is above 0 | 0 |
| `onlyOpen` | Drop tenders whose deadline has passed | true |
| `onlyNew` | Only records not returned by a previous run with the same mode and filters | false |
| `maxItems` | At most this many records, newest notices first. `0` means no limit | 1000 |

Example input for a daily sales feed of software and IT service winners in the DACH region:

```json
{
  "mode": "awards",
  "countries": ["DE", "AT", "CH"],
  "cpvCodes": ["72000000", "48000000"],
  "minValueEur": 100000,
  "onlyNew": true,
  "maxItems": 0
}
```

Example input for open construction tenders in Ireland worth at least EUR 1 million:

```json
{
  "mode": "tenders",
  "countries": ["IE"],
  "cpvCodes": ["45000000"],
  "minValueEur": 1000000,
  "daysBack": 10
}
```

### Output example

```json
{
  "recordType": "award",
  "noticeId": "722698-2026",
  "noticeType": "can-standard",
  "publicationDate": "2026-10-20",
  "title": "Suministro de licencias de Software Oracle de los sistemas de información del Ayuntamiento de Zaragoza.",
  "titleLanguage": "spa",
  "cpvDescription": "Database and operating software package",
  "mainCpv": "48600000",
  "contractType": "supplies",
  "procedureType": "neg-wo-call",
  "buyerName": "Gobierno de Zaragoza",
  "buyerCountry": "ESP",
  "buyerCity": "Zaragoza",
  "value": 458411.48,
  "currency": "EUR",
  "valueEur": 458411.48,
  "valueBasis": "awarded, notice total",
  "winnerName": "ORACLE IBÉRICA, S.R.L.",
  "winnerCountry": "ESP",
  "winnerSize": "large",
  "winnersInNotice": 1,
  "awardDecisionDate": "2026-09-25",
  "noticeUrl": "https://ted.europa.eu/en/notice/-/detail/722698-2026",
  "pdfUrl": "https://ted.europa.eu/en/notice/722698-2026/pdf",
  "isNew": true,
  "firstSeenAt": "2026-10-23"
}
```

A run report is saved in the key-value store as `RUN_SUMMARY`: the exact TED query used, how many notices matched, how many were scanned, filtered or already returned, and the exchange rate date.

### Pricing

This Actor uses **pay per event** pricing. Platform usage is included.

- **$2.50 per 1,000 award records** (`award-record` event, $0.0025 each)
- **$1.50 per 1,000 tender records** (`tender-record` event, $0.0015 each)

You only pay for records saved to your dataset, so filters, `onlyNew` and `maxItems` keep costs down. For example, a daily `onlyNew` run for IT and software awards in Germany, France and the Netherlands typically returns a few dozen award records, which costs well under $0.25 a day. Set a maximum cost per run in the run options and the Actor stops cleanly when it is reached.

### How values and winners work

- For awards, `value` is the total awarded value of the notice, across all lots and winners. When a notice has several winners, each winner gets its own record with the same notice total and `winnersInNotice` tells you how many share it.
- `valueEur` converts the value with the European Central Bank euro reference rates of the run date. Currencies the ECB does not publish get no `valueEur`.
- TED publishes winner countries, sizes and company numbers as separate lists that do not always line up with the winner names. The Actor fills `winnerCountry`, `winnerSize` and `winnerIdentifier` only when the match is unambiguous, and leaves them empty otherwise.
- Roughly 1 award notice in 10 has no winner (for example, when no tender was received). Those notices produce no record and are counted in `RUN_SUMMARY`.

### How "only new records" works

When `onlyNew` is on, the Actor remembers which records it has already returned for this mode and these filters in a key-value store in your account called `agents001-eu-ted-tenders-feed-state`. The date window is not part of that history, so a daily run with the default 3 day window never returns the same record twice. The first run returns everything that matches. The store holds short hashes and dates only.

With `onlyNew` off, the Actor does not open or update this store, so every record has `isNew` set to true. If a run stops with a message that it cannot open the store (for example because another Actor in your account already uses that name), enter a different name in **State store name** and run again.

### Data source, licence and privacy

All data comes from the official TED Search API of the Publications Office of the European Union (`api.ted.europa.eu`), which is open to the public and needs no login or API key. The Actor keeps well within TED's published fair usage limits.

TED's legal notice states that procurement notices published in the Supplement to the Official Journal can be freely reused for commercial or non-commercial purposes, under the Commission's reuse policy (Decision 2011/833/EU). Source: TED, Publications Office of the European Union. Each record carries a `licence` note and a link to the original notice so you can credit the source when you pass the data on. Exchange rates: European Central Bank euro foreign exchange reference rates.

The Actor returns **company level data only**. It never requests or returns contact persons, email addresses, phone or fax numbers, or street addresses, even though TED holds some of them.

Some contract winners are private individuals, such as sole traders who trade under their own name. TED does not flag them, so the Actor **drops a winner** when TED marks it as a micro enterprise or masks its identification number (TED masks personal ID numbers), **and** its name contains no legal form (such as GmbH, Ltd, S.A., S.r.l., Sp. z o.o., s.r.o., Kft, Oy, AB, d.o.o.) or organisation word (such as university, hospital, municipality, association, foundation, group, holding). Words that only describe a trade, such as "services", "consulting" or the Polish "usługi", do not count, because sole traders often trade as "first name, surname, trade" (for example "Jan Kowalski Usługi Remontowe"). Dropped winners get no record and are not named in `otherWinners`; `winnersInNotice` still counts them, and `RUN_SUMMARY` reports how many were dropped as `personalWinnersDropped`. This errs on the side of privacy, so a few small companies that leave out their legal form may also be dropped. Company numbers of kept micro enterprises are left out too.

### FAQ

**How fresh is the data?**
TED publishes new notices every working day, Monday to Friday. Each run queries TED live, so notices appear on the day they are published.

**Why is the title in another language?**
Buyers publish the title in their own language. The Actor uses the English title when the buyer provided one, and `cpvDescription` always gives the main CPV description in English.

**My keyword returns too much or nothing.**
TED ignores very common words (such as "and", "the" or "IT"), so the Actor drops them. Use more specific words, a trailing `*` (for example `softw*`), or a CPV code. TED also matches its own translations of CPV descriptions, so English words often find notices in other languages.

**Can I get older notices?**
Yes. Set `dateFrom` and `dateTo`, up to 365 days per run.

**Something looks wrong. What should I do?**
Open an issue on the Actor's Issues tab with the run link and your input.

### Disclaimer

This Actor is independent and is not affiliated with, endorsed by or sponsored by the Publications Office of the European Union, TED or the European Commission. TED and the names of buyers and companies belong to their owners. Notices can be corrected after publication; always check the official notice before acting on it.

# Actor input Schema

## `mode` (type: `string`):

Contract awards give one record per winning company (buying signals). Open tenders give one record per call for competition with its deadline. Both returns both record types.

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

Publication date window in TED publishing days (Monday to Friday), counting today. 3 on a Monday covers Thursday, Friday and Monday. Ignored when you set a start date below.

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

First publication date, for example 2026-10-01. Leave empty to use 'Published in the last N days'.

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

Last publication date, for example 2026-10-15. Leave empty for today.

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

Only notices whose buyer is in one of these countries. Use 2 or 3 letter ISO codes (DE, DEU) or English names (Germany). Leave empty for all countries.

## `winnerCountries` (type: `array`):

Only winning companies from these countries, for example your sales territory. Same formats as buyer countries.

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

Only notices with one of these CPV codes or any code below it. 72000000 covers all IT services, 48000000 all software packages, 45000000 all construction work. Short codes such as 72 are padded to 72000000.

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

Only notices whose text contains any of these keywords. Inside one keyword every word must appear. A trailing \* matches word endings, for example softw\*. TED also searches its own translations of the CPV descriptions, so English words find many notices in other languages.

## `minValueEur` (type: `number`):

Only records whose value, converted to euro with the latest ECB reference rates, is at least this much. Records without a published value are dropped when this is above 0. For awards the value is the notice total across all lots and winners.

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

Drop tenders whose submission or participation deadline has already passed. Tenders without a published deadline are kept.

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

Remember which records were already returned for these filters and only return new ones. The date window is not part of the history, so a daily run with a 3 day window never repeats a record. Changing the mode or filters starts a fresh history.

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

Stop after this many records in total, newest notices first. 0 means no limit. In 'both' mode tenders get half and awards get the rest. You pay per record returned.

## `stateStoreName` (type: `string`):

Only used when 'Only records not returned in previous runs' is on. Name of the key-value store in your account that keeps the list of records already returned. Each different set of filters gets its own record inside it. Change it only to keep separate histories. If a run reports that it cannot open the store, enter a different name here.

## `requestDelaySecs` (type: `number`):

Minimum seconds between requests to the TED API. TED's fair usage policy allows far more; keep this polite.

## Actor input object example

```json
{
  "mode": "awards",
  "daysBack": 3,
  "countries": [
    "DE",
    "FR",
    "NL"
  ],
  "cpvCodes": [
    "72000000",
    "48000000"
  ],
  "minValueEur": 0,
  "onlyOpen": true,
  "onlyNew": false,
  "maxItems": 1000,
  "stateStoreName": "agents001-eu-ted-tenders-feed-state",
  "requestDelaySecs": 0.3
}
```

# Actor output Schema

## `awards` (type: `string`):

No description

## `tenders` (type: `string`):

No description

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

No description

# 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": [
        "DE",
        "FR",
        "NL"
    ],
    "cpvCodes": [
        "72000000",
        "48000000"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("agents001/eu-ted-tenders-feed").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": [
        "DE",
        "FR",
        "NL",
    ],
    "cpvCodes": [
        "72000000",
        "48000000",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("agents001/eu-ted-tenders-feed").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": [
    "DE",
    "FR",
    "NL"
  ],
  "cpvCodes": [
    "72000000",
    "48000000"
  ]
}' |
apify call agents001/eu-ted-tenders-feed --silent --output-dataset

```

## MCP server setup

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

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/HaZU1ER2ALiNqBu6c/builds/vnnTsci16e5z69R6g/openapi.json
