# TED EU Tenders Scraper: Public Procurement Notices (`mrslm_data/ted-eu-tenders`) Actor

Search EU public procurement notices on TED by keyword, CPV code, country and date: buyer, deadline, estimated value, CPV codes, winners and links.

- **URL**: https://apify.com/mrslm_data/ted-eu-tenders.md
- **Developed by:** [MRSLM Data](https://apify.com/mrslm_data) (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.60 / 1,000 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 EU Tenders Scraper: Public Procurement Notices

**Search EU public procurement notices on TED by keyword, CPV, country and date: buyer, deadline, estimated value, CPV codes, winners and links. $2.00 per 1,000 notices.**

TED (Tenders Electronic Daily) publishes every public contract above EU thresholds from 30+ European countries: hundreds of thousands of notices a year. This Actor turns it into a clean daily lead feed of open tenders, or a list of awards and winners.

### Quick start

1. Press **Start**. The form is already filled with this example input, so the first run returns real rows in under a minute:

```json
{
  "keywords": [
    "software development"
  ],
  "countries": [
    "DE",
    "FR",
    "NL"
  ],
  "publishedWithinDays": 14,
  "maxResults": 30
}
```

2. Swap in your own values, or call the Actor through the Apify API, Make, Zapier, n8n or the Python/JS clients with the same JSON.
3. Download the results as JSON, CSV or Excel from the **Output** tab, or schedule the run to repeat.

**Full guide: How to Find Translation and Interpreting Tenders in the EU and See Who Wins Them (Real Runs on TED)**

### Why language services are a good TED niche

Language services are a niche where TED works particularly well, for three reasons.

- **One clean pair of codes.** In the Common Procurement Vocabulary (CPV), translation services are **79530000** and interpretation services are **79540000**, both under 79500000 (office-support services). Buyers in every member state use these codes even when they write the notice in Dutch, Swedish or Latvian. So you can search by code once instead of guessing at keywords in 24 languages.
- **Lots of mid-sized contracts.** In the awards run below, the 46 euro-denominated award notices that gave a value (after removing modifications and duplicates) had a median of about €404,000, with a long tail up to a €90 million interpreting framework. That's large enough to matter, and small enough that specialised agencies win them.
- **Repeat buyers on a cycle.** Interpreting frameworks for police, councils and hospitals run two to four years, then go back out to tender.

The same workflow works for any service with clear CPV codes, such as cleaning (90910000) or legal services (79100000).

### What you need

A free Apify account and the Actor: [apify.com/mrslm_data/ted-eu-tenders](https://apify.com/mrslm_data/ted-eu-tenders). No TED account, API key or proxy is needed, because the Actor reads TED's public Search API (api.ted.europa.eu, version 3), the same index the TED website uses.

### Run 1: every open translation and interpreting tender in Europe

#### The input

Open the Actor, switch the input form to JSON and paste this:

```json
{
  "cpvCodes": ["79530000", "79540000"],
  "noticeKind": "tenders",
  "publishedWithinDays": 30,
  "openForBidsOnly": true,
  "maxResults": 40
}
```

Here is what each field does, taken from the Actor's input schema and source:

- **cpvCodes**: 8-digit CPV codes. You can paste codes with the check digit (`79530000-8`); the Actor strips the `-N` part. A code also matches the codes below it in the CPV tree, so `79530000` would also catch a narrower child code if a buyer used one.
- **noticeKind**: `tenders` (calls for competition, the default), `awards` or `all`. `tenders` covers ordinary contract notices, light-regime social services notices, design contests, prior-information notices used as a call for competition, qualification systems and subcontracting notices.
- **publishedWithinDays**: only notices published in the last N days. 30 gives a month of history.
- **openForBidsOnly**: keep only notices whose tender deadline is today or later, so nothing has already closed.
- **maxResults**: a hard cap on rows (and therefore on cost). 40 was a safe ceiling; the run found 37.

The other filters (countries, keywords, language, includeDescription, expertQuery and maxResultsPerSearch) are covered in "Choosing CPV codes and filters" below.

#### What the Actor sends to TED

The run summary (in the run's key-value store, record `RUN_SUMMARY`) records the exact TED query. For this input it was:

```
classification-cpv IN (79530000 79540000) AND notice-type IN (cn-standard cn-social cn-desg pin-cfc-standard pin-cfc-social qu-sy subco) AND publication-date>=today(-30) AND deadline-receipt-tender-date-lot>=20261031
```

You can paste it into TED's own expert search to check the results by hand, and add any filter the form lacks through **expertQuery** in the same syntax.

#### The result

Run `rRmhiQtHgyMbV2xUN` finished in **4.1 seconds** with **37 rows**. That's every notice that matched, below the 40 cap. It was charged for 1 start event and 37 notice events.

**Where the tenders were:**

| Country | Open notices |
|---|---|
| Netherlands | 5 |
| Poland, Germany, France, Sweden | 4 each |
| Norway, Denmark | 3 each |
| Spain, Slovakia, Finland | 2 each |
| Estonia, Latvia, Ireland, Greece | 1 each |

All 37 were `cn-standard` notices for services under the open procedure. Publication dates ran from 1 to 30 October 2026. Deadlines ran from **2 November 2026 to 27 January 2027**, most of them in November, so a weekly check would already miss some.

**A sample of real rows:**

| Buyer | Country | What | Value | Deadline | Notice |
|---|---|---|---|---|---|
| Ministerie van Buitenlandse Zaken | NL | Translation Dutch–English (framework) | €400,000 | 6 Nov 2026 | 755696-2026 |
| European Central Bank | DE | Translation EN→DE/FR/IT, 9 lots | est. €2.23M, total €2.3M | 2 Nov 2026 | 683267-2026 |
| European Central Bank | DE | Legal translation, 7 lots | total €1.45M | 27 Nov 2026 | 750606-2026 |
| Politie | NL | On-site interpreting, 3 lots | est. €60M, total €6M | 7 Dec 2026 | 678243-2026 |
| Stockholms läns sjukvårdsområde | SE | Interpreting services, 3 lots | total SEK 300M | 19 Nov 2026 | 720335-2026 |
| Innherred anskaffelser | NO | Interpreting framework | NOK 35M | 1 Dec 2026 | 747923-2026 |
| Department of Social Protection | IE | Polish document translation | €620,000 | 23 Nov 2026 | 732190-2026 |
| Tieslietu ministrija (Ministry of Justice) | LV | Translation, 13 lots | est. €467,851.24 | 27 Nov 2026 | 731547-2026 |
| Sametinget | NO | Norwegian–Northern Sami translation | NOK 2.2M | 23 Nov 2026 | 694228-2026 |
| Stichting Nidos | NL | Interpreting | not published | 27 Jan 2027 | 715098-2026 |

All 37 rows have a `deadline`. Two of them are sign-language interpreting tenders (a French ministry and a German federal body), which are also filed under these codes.

One full row, exactly as the dataset returns it:

```json
{
  "noticeId": "755696-2026",
  "title": "Netherlands – Translation services – Vertaaldiensten Nederlands-Engels",
  "procedureTitle": "Vertaaldiensten Nederlands-Engels",
  "buyerName": "Ministerie van Buitenlandse Zaken",
  "buyerCity": "Den Haag",
  "buyerCountry": "NLD",
  "noticeType": "cn-standard",
  "procedureType": "open",
  "contractNature": "services",
  "mainCpv": "79530000",
  "cpvCodes": ["79530000"],
  "placeOfPerformance": ["NLD"],
  "publicationDate": "2026-10-30",
  "deadline": "2026-11-06",
  "requestToParticipateDeadline": null,
  "estimatedValue": 400000,
  "totalValue": 400000,
  "currency": "EUR",
  "lots": 1,
  "winners": [],
  "frameworkAgreement": true,
  "url": "https://ted.europa.eu/en/notice/-/detail/755696-2026",
  "pdfUrl": "https://ted.europa.eu/en/notice/755696-2026/pdf",
  "scrapedAt": "2026-10-31T16:19:38.927Z"
}
```

#### What the open tenders tell you

Reading the 37 rows together brings out some patterns you'd never see one notice at a time:

- **Frameworks dominate.** 24 of the 37 notices are framework agreements. You're usually bidding for a place on a panel for two to four years, not for a single job. That changes how to price: a framework "value" is a ceiling, not a guaranteed spend.
- **Lots are how small agencies get in.** 24 notices have a single lot, but some are split finely. The Latvian Ministry of Justice has 13 lots, the ECB 9 and 7. A lot is usually a language pair or a region, and you can often bid for just one.
- **Values are optional.** Only 18 rows have `estimatedValue` and 22 have `totalValue`. 14 rows have no currency at all because the buyer published no value. Four in Swedish kronor, three in Norwegian kroner and one in Danish kroner remind you that the Nordic buyers don't price in euros.
- **Interpreting outweighs translation.** By main CPV, 21 rows were interpreting and 10 translation. Police, healthcare, social services and asylum make public-sector interpreting a big, steady market.

### Run 2: who wins these contracts

Finding tenders is half the job. The other half is knowing the competition: who won the last round, how many winners a framework takes, and what the contracts were worth. Award notices answer all three.

#### The input

```json
{
  "cpvCodes": ["79530000", "79540000"],
  "noticeKind": "awards",
  "publishedWithinDays": 180,
  "maxResults": 120
}
```

The only changes are `noticeKind: "awards"`, a longer look-back and a bigger cap. With `awards`, the Actor asks TED for notice types can-standard, can-social, can-desg, can-tran and can-modif. Those are ordinary award notices, light-regime awards, design-contest results, transport awards and contract modifications.

#### The result

Run `ErOam2bXgJFt2MZMR` finished in **8.5 seconds** with **120 rows**. Its status message was "Done. 120 results from 1 target(s). Reached maxResults (120)". It was charged for 1 start event and 120 notice events.

**Important:** TED returns notices **newest first**, and the cap cut the run off. So although the input asked for 180 days, the 120 rows only reach back to **31 July 2026**, three months. To cover the full six months, raise `maxResults` (the run summary reported at least 200 matching award notices), or run month by month. This is the single most common way to get a silently partial dataset, so check the oldest `publicationDate` in your results.

**By country:** Spain 28, Poland 20, Germany 10, France 9, Switzerland 9, Luxembourg 5, Czechia, Bulgaria and Ireland 4 each, and the rest spread across 14 other countries. Spain and Poland publish many small, single-lot awards; Luxembourg's 5 are mostly large EU-agency frameworks.

**By notice type:** 96 `can-standard`, 21 `can-modif` and 3 `can-social`. **None of the 21 modification notices names a winner** in the `winners` field; they amend an existing contract (an extension, a price change). Of the 99 actual award notices, 90 name at least one winner.

**Values:** 87 of the 120 rows have a `totalValue`. Currencies: 57 EUR, 18 PLN, 9 CHF, and smaller numbers of CZK, SEK, NOK, DKK, RON and HUF. After removing modifications and duplicates, the 46 euro award notices with a positive value have a median of about €404,000.

#### The biggest awards in the window

| Published | Buyer | Contract | Value | Winners (first few) |
|---|---|---|---|---|
| 26 Oct | European Border and Coast Guard Agency (Frontex), PL | Interpretation and cultural mediation framework, 7 lots | €90M | Authentic Enterprise Solutions, Interpreti Traduttori Mediatori, Language Academy, Ofilingua, SeproTec |
| 11 Aug | Høje-Taastrup Kommune, DK | Interpreting for the ICØ municipalities | DKK 59.8M (est. DKK 43.0M) | ViTolker ApS |
| 5 Aug | Politie, NL | Remote interpreting | €40M | Global Talk |
| 28 Aug | Ministerie van Justitie en Veiligheid, NL | Translation for the Public Prosecution Service | €36.9M (est. €24.6M) | AVB Language Group, thebigword |
| 22 Sep | Translation Centre for the Bodies of the EU, LU | Translation and post-editing of standardised technical texts, 22 lots | €10.55M | 16 winners, including ARTE.DOC, Acolad Luxembourg, Avatar and CBG Konsult |
| 23 Oct | Translation Centre for the Bodies of the EU, LU | Translation and post-editing, 12 lots | €3.80M | Acolad Luxembourg, Celer Pawlowsky, ETC Europe, IDEST Communication… |
| 2 Oct | DSS (Norwegian government shared services), NO | Translation of EEA-relevant EU rules, 3 lots | NOK 8M | Totaltekst, Semantix Translations Norway, and one sole trader |
| 28 Oct | Assistance Publique – Hôpitaux de Paris, FR | Remote interpreting for Paris hospitals | €3M | ISM Interprétariat, Association Tradlibre, Acolad France |
| 23 Oct | Giunta Regionale, IT | Linguistic mediation by telephone interpreting | €990,000 (est. €1.52M) | Translated srl |

The €64.8 million row from the EU Agency for Asylum is a `can-modif` (a modification), not a new award, which is why it isn't in the table. If you sort by value without filtering `noticeType`, modifications float to the top and mislead you.

Two Translation Centre notices (626728-2026 on 11 September and 651574-2026 on 22 September) carry the same title, value and 16 winners. That's a corrected re-publication, not two contracts. The €3.80M procedure also appears twice (674567-2026 on 1 October, then 736670-2026 on 23 October with a different winner list), so read both before deciding which is final. More on deduplication below.

#### Who wins most often

Counting each winner once per notice, after cleaning (see the next section), the award notices name **267 distinct winners**, companies and sole traders together. The most frequent names are:

- **Swiss federal frameworks dominate the top.** The Swiss Federal Chancellery split one tender into many lots of framework contracts and published nine award notices, each naming between 6 and 40 distinct winners. That's why Apostroph Bern (8 notices), Swisstrade Solutions (7), All Languages Made Accessible, Translingua and SwissGlobal Language Services (6 each), and Hieronymus, Supertext and About SA (5 each) lead the count.
- **Acolad** appears under four entities: Acolad France, Acolad Luxembourg (two spellings) and Acolad Finland Oy. Together they're on 6 distinct award notices in France, Luxembourg, Finland and Portugal. It's the clearest multinational in this sample.
- **Extran / TTN Translation Network** on 5 notices (Switzerland and Germany), **Ofilingua** on 4 (Spain and Poland), **SeproTec** on 3 under three different spellings (Spain and Poland), **Lingsoft** on 3 (Finland and Luxembourg) and **Semantix** on 2 (Luxembourg and Norway).
- **The long tail is local.** Most winners appear once: a Romanian media company on three Romanian awards (two ministries), a Prague translation centre that won with both the Czech Social Security Administration and the Ministry of Health, and many single-office agencies and sole traders in Poland and Spain. (This guide doesn't name individual sole traders. Their names are in the public notices, but there's no reason to repeat them.)

**What that means if you're bidding:** large frameworks attract multinationals and take many winners, so a panel place is achievable but is a place in a queue. Single-lot municipal and regional contracts in Spain, Poland, the Nordics and the Netherlands are where one capable local agency wins outright.

### Clean the data before you use it

Raw TED data has quirks. None of them is a bug in the Actor; they're how buyers publish. All of these turned up in the two runs above.

#### 1. The same procedure appears more than once

Run 1 had three pairs of notices that were really the same procedure, each published twice:

- Gemeente Súdwest-Fryslân: 713580-2026 and 710856-2026
- Ringkøbing-Skjern Kommune: 715122-2026 and 678911-2026
- Slovak Ministry of Transport: 724648-2026 and 705830-2026

Run 2 had the Translation Centre pair above and two identical Polizeipräsidium Wuppertal award notices for interpreting and translation (28 and 30 September, 24 winners each, €2.5M).

Usually the second notice is a correction, a changed deadline or a re-publication. **For open tenders, group by buyer and procedure title and keep the newest `publicationDate`.** For awards, be careful: the nine Swiss Chancellery notices share one procedure title but cover different lots and winners. So treat award notices as duplicates only when buyer, title, value and winners all match.

#### 2. Off-topic matches through secondary codes

TED matches a CPV code anywhere on the notice, not only the main code. In run 1, **31 of 37** rows had translation or interpreting as their main CPV. The other six listed it as a secondary code:

- Polish regional development agencies organising trade missions and study trips (main codes 79952000 event services and 55100000 hotel services). Interpreting is one item in a travel package.
- Two research-related notices (main code 73000000).
- A visitor-centre project for Santorini's volcano (main code 92110000).

If you sell translation, those six probably aren't for you. **Filter on `mainCpv`**, or keep them in a separate tab if you're a travel or events company that subcontracts interpreters.

#### 3. Two value fields that disagree

`estimatedValue` is the sum of the lot estimates; `totalValue` is the value given for the whole notice. Usually they match. When they don't, it's worth a look:

- Politie (NL) on-site interpreting: estimated €60M, total €6M. A factor of ten suggests one figure is per year and the other for the whole term, or a typo. Open the notice.
- Lomma kommun (SE): estimated SEK 80M, total SEK 40M.
- Högskoleservice i Jönköping (SE): estimated SEK 56M, total SEK 14M. In the awards run, the same buyer's award shows SEK 14M with an SEK 56M estimate.
- Høje-Taastrup (DK): estimated DKK 43.0M, awarded DKK 59.8M.

In the awards data, 11 rows have a total of 0 (mostly the Swiss frameworks, which publish no value) and one has −1, a placeholder some buyers use. **Treat 0 and negative values as "not published".**

#### 4. Winner names in several languages

Swiss notices are multilingual, and TED returns each winner name once per language with a prefix, for example `FR_Apostroph Bern GmbH`, `DE_Apostroph Bern GmbH`, `IT_Apostroph Bern GmbH`. The Actor passes these through unchanged. **Strip a leading two-letter prefix and an underscore before counting**, or a company is counted three times. Companies also appear under different spellings (SeproTec in three spellings), so a little manual merging is needed for a clean league table.

#### A small Python cleaning script

This is the script used for the counts in this guide (on run 2 it keeps 97 of the 120 notices). It works on the JSON export of an awards run:

```python
import json, re, collections

rows = json.load(open("ted-awards.json"))          # dataset export, JSON format

def clean_name(w):
    return re.sub(r"^(FR|DE|IT|EN)_", "", w).strip()

## 1. drop modifications and exact re-publications (same buyer, title, value, winners)
awards = [r for r in rows if r["noticeType"] != "can-modif"]
latest = {}
for r in sorted(awards, key=lambda r: r["publicationDate"]):
    key = (r["buyerName"], r["procedureTitle"], r["totalValue"],
           tuple(sorted({clean_name(w) for w in r["winners"]})))
    latest[key] = r
awards = list(latest.values())

## 2. count each winner once per notice
wins = collections.Counter()
for r in awards:
    for name in {clean_name(w) for w in r["winners"]}:
        wins[name] += 1

for name, n in wins.most_common(15):
    print(n, name)

## 3. values: treat 0 and negatives as missing
eur = sorted(r["totalValue"] for r in awards
             if r["currency"] == "EUR" and (r["totalValue"] or 0) > 0)
print("median EUR award:", eur[len(eur) // 2] if eur else None)
```

The same steps in a spreadsheet: sort by `publicationDate` descending, filter `noticeType` ≠ can-modif, use **Data > Data clean-up > Remove duplicates** on the buyer, procedure-title, value and first-winner columns, and filter `mainCpv` to your codes.

### Choosing CPV codes and filters

For language services, these codes cover nearly everything:

| CPV | Meaning | Use it when |
|---|---|---|
| 79530000 | Translation services | Written translation, sworn and legal translation, localisation |
| 79540000 | Interpretation services | On-site, remote, phone and video interpreting, sign language is often here too |
| 79500000 | Office-support services | Broader: catches some translation filed one level up, plus a lot of noise |
| 79952000 | Event services | Only if you also want conferences with interpreting bundled in |

Some practical filters:

- **Countries.** Add `"countries": ["NL", "BE", "DE"]` to narrow to where you can deliver. Two- and three-letter codes both work. The Actor converts to TED's three-letter form.
- **Keywords for sub-niches.** For sign language or a specific language pair, add `"keywords": ["sign language"]` or a phrase in the notice language, such as `"tolkning"` or `"vertaling"`. Each keyword line runs as its own search, combined with your CPV codes, and **maxResultsPerSearch** caps each one.
- **Language of titles.** `"language": "fr"` returns titles in French when TED has them, falling back to English and then to the first language available.
- **Descriptions.** `"includeDescription": true` adds up to 4,000 characters of the description, which often names the language pairs and volumes. Contact details are removed from that text.
- **expertQuery.** For anything else, add a clause in TED's syntax, such as `organisation-country-buyer=SWE`, or a buyer-name match. It's ANDed with the rest, and the full query is logged, so you can check it on TED.

### Run it every morning

Tender windows are short (run 1's earliest deadline was two days away), so fetch new notices daily.

1. Paste this input into the Actor's form and click **Save as new task**. Name it `ted-language-tenders-daily`.

   ```json
   {
     "cpvCodes": ["79530000", "79540000"],
     "noticeKind": "tenders",
     "publishedWithinDays": 2,
     "openForBidsOnly": true,
     "maxResults": 100
   }
   ```

   Using **2 days** instead of 1 means a notice published late in the day isn't missed. You'll see some notices twice across consecutive runs; deduplicate on `noticeId`.
2. Go to **Schedules > Create new schedule**. Use the cron expression `0 7 * * 1-5` (07:00 Monday to Friday) and pick your timezone. TED publishes on working days, so weekend runs add nothing.
3. Add the task to the schedule and save.
4. On the task's **Integrations** tab, add an email or Slack notification for "Run succeeded", or a webhook to your own system.

For a weekly competitor digest, add a second task with `"noticeKind": "awards"` and `"publishedWithinDays": 8`, scheduled for Monday morning (`0 8 * * 1`).

The newest results of a task are always at the same address:

```
https://api.apify.com/v2/actor-tasks/<USERNAME>~ted-language-tenders-daily/runs/last/dataset/items?status=SUCCEEDED&format=json
```

### Send the results to Google Sheets

Three options, from simplest to most automated:

1. **Manual export.** Open a run, go to **Output > Export**, choose CSV and the columns you want, then in Sheets use **File > Import > Upload**. Lists such as `winners` become numbered columns (`winners/0`, `winners/1`…).
2. **Formula in a private sheet.**
   `=IMPORTDATA("https://api.apify.com/v2/actor-tasks/<USERNAME>~ted-language-tenders-daily/runs/last/dataset/items?status=SUCCEEDED&format=csv&fields=publicationDate,deadline,buyerCountry,buyerName,procedureTitle,totalValue,currency,url&token=<YOUR_TOKEN>")`
   This refreshes from the latest successful run. Your token is visible to anyone who can open the sheet, so keep the sheet private, or use a token with limited permissions.
3. **Automatic append** with Make, Zapier or n8n, as described in the next section.

### Use it from Make, Zapier or n8n

All three have an official Apify integration, and the pattern is the same:

- **Trigger:** "Watch Actor runs" or "Watch task runs" (Make), "Finished Actor Run" or "Finished Task Run" (Zapier), or the Apify Trigger node (n8n), pointed at `ted-language-tenders-daily`.
- **Get the rows:** "Get dataset items" with the run's default dataset ID, which the trigger provides.
- **Act on each row**, for example:
  - Google Sheets "Add a row" into a "Pipeline" tab.
  - A Slack or Teams message for rows above a value: in Make, add a filter `totalValue > 100000`.
  - Create a card in Trello, Asana or your CRM with the deadline as the due date.

Tip: add a Google Sheets "Search rows" step on `noticeId` first, and only add new rows. That removes the overlap from the 2-day window.

To start the run from the automation tool itself, use "Run a task", then "Get dataset items".

### Call it from your own code

Put your API token (Console > Settings > API & Integrations) in an environment variable called `APIFY_TOKEN`; don't paste it into code.

**Python**, apify-client 3.x (`pip install "apify-client>=3"`):

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])

run = client.actor("mrslm_data/ted-eu-tenders").call(run_input={
    "cpvCodes": ["79530000", "79540000"],
    "noticeKind": "tenders",
    "publishedWithinDays": 30,
    "openForBidsOnly": True,
    "maxResults": 40,
})

## apify-client 3.x returns a Run object: use attributes, not run["defaultDatasetId"]
items = list(client.dataset(run.default_dataset_id).iterate_items())
interpreting = [i for i in items if i["mainCpv"] == "79540000"]
for i in sorted(interpreting, key=lambda i: i["deadline"] or ""):
    print(i["deadline"], i["buyerCountry"], i["buyerName"], "-", i["procedureTitle"])
```

**JavaScript / Node.js** (`npm install apify-client`):

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('mrslm_data/ted-eu-tenders').call({
    cpvCodes: ['79530000', '79540000'],
    noticeKind: 'awards',
    publishedWithinDays: 180,
    maxResults: 120,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
const withWinners = items.filter((i) => i.winners.length > 0);
console.log(`${withWinners.length} of ${items.length} award notices name a winner`);
```

**curl**, one synchronous request that returns CSV. It waits up to 5 minutes, which is plenty for runs this size:

```bash
curl -X POST "https://api.apify.com/v2/acts/mrslm_data~ted-eu-tenders/run-sync-get-dataset-items?format=csv&fields=publicationDate,deadline,buyerCountry,buyerName,procedureTitle,totalValue,currency,url" \
  -H "Authorization: Bearer $APIFY_TOKEN" -H "Content-Type: application/json" \
  -d '{"cpvCodes":["79530000","79540000"],"noticeKind":"tenders","publishedWithinDays":30,"openForBidsOnly":true,"maxResults":40}' \
  -o language-tenders.csv
```

### Ask an AI assistant through MCP

Apify runs a hosted MCP server that can expose any Store Actor as a tool. To give Claude, Cursor, VS Code or another MCP client this Actor and nothing else, add the server URL:

```
https://mcp.apify.com/?tools=mrslm_data/ted-eu-tenders
```

For example, in a client that reads `mcp.json`:

```json
{
  "mcpServers": {
    "ted-tenders": { "url": "https://mcp.apify.com/?tools=mrslm_data/ted-eu-tenders" }
  }
}
```

The server uses OAuth: on first use, your client opens an Apify sign-in window. Clients that can't do OAuth can send `Authorization: Bearer <your Apify token>` instead. Then ask in plain language, for example:

- "Find open interpreting tenders in the Netherlands and Belgium published in the last two weeks, and list buyer, deadline and value."
- "Get translation award notices from the last 90 days in Spain and tell me which companies won more than one."

The assistant fills in the Actor input (CPV codes, countries, notice kind) and runs it on your Apify account at the normal per-notice price. Keep `maxResults` modest in your prompt ("at most 50 notices"), so an over-eager assistant doesn't run a large search.

### What it costs

The Actor uses pay-per-event pricing, so you pay per notice returned, not for compute time.

| Event | Free / Bronze | Silver | Gold / Platinum / Diamond |
|---|---|---|---|
| Notice (per 1,000) | $2.00 | $1.80 | $1.60 |
| Actor start (once per run, at the default 512 MB) | $0.00005 | $0.00005 | $0.00005 |

The two runs in this guide, at Free/Bronze prices:

| Run | Rows | Charge |
|---|---|---|
| Run 1: open tenders (rRmhiQtHgyMbV2xUN) | 37 | 1 start + 37 notices = **$0.07405** |
| Run 2: awards (ErOam2bXgJFt2MZMR) | 120 | 1 start + 120 notices = **$0.24005** |

For ongoing use, at 21 working days and, say, 5 new notices a day, the daily schedule returns about 105 notices a month: **about $0.21 a month** plus 21 start fees (about $0.001). A weekly awards digest of 20–40 notices adds roughly $0.04–$0.08 a week. If you widen the search to 79500000 or remove the CPV filter, row counts rise quickly, so set **Maximum cost per run** in the run options (`maxTotalChargeUsd` in the API). The Actor stops cleanly at the cap.

### Output fields

| Field | Meaning |
|---|---|
| `noticeId`, `url`, `pdfUrl` | TED publication number, notice page and PDF. |
| `title`, `procedureTitle` | TED's composed title and the buyer's procedure title. |
| `buyerName`, `buyerCity`, `buyerCountry` | Contracting authority (ISO 3-letter country). |
| `noticeType`, `procedureType`, `contractNature` | For example can-standard, open, services. |
| `mainCpv`, `cpvCodes` | Main CPV code and every code on the notice. |
| `placeOfPerformance` | NUTS or country codes. |
| `publicationDate`, `deadline`, `requestToParticipateDeadline` | Publication date and deadlines. |
| `estimatedValue`, `totalValue`, `currency` | Sum of lot estimates, notice total (awarded value on awards), currency. |
| `lots`, `frameworkAgreement` | Number of lots; true for framework agreements. |
| `winners` | Winners on award notices. |
| `description` | With includeDescription only: up to 4,000 characters, contact details removed. |
| `scrapedAt` | When the row was saved. |

### Limits and honest caveats

- **TED isn't every tender.** It carries contracts above the EU thresholds plus those buyers publish voluntarily. Many small translation jobs only appear on national portals (TenderNed, Doffin, PLACSP, e-Zamówienia and so on), or are bought directly without a tender.
- **The cap cuts off the oldest results.** Results come newest first, so a capped run covers a shorter period than `publishedWithinDays` suggests (run 2 asked for 180 days and covered 92). Check the oldest date.
- **Data quality is the buyer's.** Missing values, values of 0 or −1, estimates that disagree with totals, and winner names in three languages are all published that way.
- **Award notices lag** the contract signature, often by weeks, and framework call-offs are usually not published at all.
- **No documents.** The Actor returns the notice data and links. Tender documents (specifications, price schedules) sit on the buyer's procurement platform, often behind a free registration, and aren't downloaded.
- **Personal data.** Some winners are sole traders trading under their own name. Treat those names as personal data under the GDPR if you store or republish them.

### FAQ

**Does this work for languages other than English?**
Yes. The search is by CPV code, which is language-independent, so you get notices from every country whatever language they're written in. Titles come in your chosen `language` when TED has a translation, and fall back to English, then to whatever language the notice has.

**Why did a hotel or travel tender show up in my translation search?**
Because translation or interpreting is one of its secondary CPV codes. TED matches every code on a notice. Filter on `mainCpv` to keep only notices that are mainly about language services.

**How do I get only sign-language interpreting?**
Keep `cpvCodes` at 79540000 and add `"keywords": ["sign language"]` plus the local terms you care about, such as "gebarentaal", "teckenspråk" or "Gebärdensprache". Each keyword runs as a separate search, and the results are merged.

**Can I see who won a specific tender I bid on?**
Yes. Run with `"noticeKind": "awards"`. Add an `expertQuery` that narrows to the buyer, or search by keyword using words from the procedure title. The award notice usually has a different notice ID from the original call, so match on buyer and title.

**Why are there no winners on some award notices?**
Modification notices (`can-modif`) amend an existing contract and don't name winners in this field. Some award notices are published for procedures that ended without an award, or where the buyer withheld the name. In run 2, 9 of the 99 non-modification award notices had no winners.

**Does it cover the UK, Switzerland or Norway?**
Switzerland and Norway publish on TED and appear above. The UK left TED in 2021; use [UK Public Tenders](https://apify.com/mrslm_data/uk-public-tenders) for Contracts Finder and Find a Tender. For US federal awards, see [USAspending Awards](https://apify.com/mrslm_data/usaspending-awards).

### Is it legal?

This Actor only reads official open-data APIs that the publisher offers to everyone without a login or key. It obeys robots.txt, stays under the published rate limits, and never tries to get past logins, paywalls, captchas or bot checks. Contact persons, e-mail addresses and phone numbers in notices are never requested, and descriptions are scrubbed of contact details. Award notices name the winning organisations as published by the buyer; in rare cases a winner is a sole trader, so treat winner names with care under GDPR. You are responsible for how you use the data: follow the source's licence and terms and the privacy and marketing laws that apply to you (GDPR, CAN-SPAM, PECR, etc.). This tool is independent and not affiliated with or endorsed by the data publisher.

# Actor input Schema

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

Full-text search terms, one search per line, e.g. software development, cleaning services.

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

8-digit CPV codes, e.g. 72000000 (IT services), 90910000 (cleaning).

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

2- or 3-letter country codes of the buyer, e.g. DE, FR, NL or DEU.

## `noticeKind` (type: `string`):

Open calls for tender, contract award notices (with winners), or everything.

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

Only notices published in the last N days. 0 = any date.

## `openForBidsOnly` (type: `boolean`):

Keep only tenders whose submission deadline is today or later.

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

2-letter code for titles and names when TED has several languages (falls back to the original).

## `includeDescription` (type: `boolean`):

Add the procedure description (original language, up to 4,000 characters).

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

Extra TED expert-search clause, combined with AND. Example: organisation-name-buyer~"Stadt".

## `maxResultsPerSearch` (type: `integer`):

0 = no cap.

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

Safety cap on saved notices across the whole run. 0 = no cap. Your max cost per run is always respected.

## Actor input object example

```json
{
  "keywords": [
    "software development"
  ],
  "countries": [
    "DE",
    "FR",
    "NL"
  ],
  "noticeKind": "tenders",
  "publishedWithinDays": 14,
  "openForBidsOnly": false,
  "language": "en",
  "includeDescription": false,
  "maxResultsPerSearch": 0,
  "maxResults": 30
}
```

# Actor output Schema

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

All notices as a dataset (JSON, CSV, Excel and more via the API or Console).

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

Matches and saved rows per search, failures and the stop reason.

# 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 = {
    "keywords": [
        "software development"
    ],
    "countries": [
        "DE",
        "FR",
        "NL"
    ],
    "publishedWithinDays": 14,
    "maxResults": 30
};

// Run the Actor and wait for it to finish
const run = await client.actor("mrslm_data/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 = {
    "keywords": ["software development"],
    "countries": [
        "DE",
        "FR",
        "NL",
    ],
    "publishedWithinDays": 14,
    "maxResults": 30,
}

# Run the Actor and wait for it to finish
run = client.actor("mrslm_data/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 '{
  "keywords": [
    "software development"
  ],
  "countries": [
    "DE",
    "FR",
    "NL"
  ],
  "publishedWithinDays": 14,
  "maxResults": 30
}' |
apify call mrslm_data/ted-eu-tenders --silent --output-dataset

```

## MCP server setup

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