# EU Tenders Scraper & Monitor (TED) (`gr1n/ted-tenders-monitor`) Actor

Get EU public tenders and contract awards from TED as a clean table: buyer, deadline, estimated value, winners and CPV codes. Filter by keyword, CPV, country and date, or get only new tenders every day. Official TED API, no personal data.

- **URL**: https://apify.com/gr1n/ted-tenders-monitor.md
- **Developed by:** [Green](https://apify.com/gr1n) (community)
- **Categories:** Lead generation, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

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

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 & Monitor (TED)

Get **EU public tenders and contract awards** from TED (Tenders Electronic Daily), the official EU procurement journal, as a clean table you can open in Excel or Google Sheets, or send to your CRM or an AI agent.

TED publishes about **2,500 new notices every working day** from all 27 EU countries plus Norway, Iceland, Switzerland and others. This Actor finds the ones that matter to you and, if you want, sends you only the new ones every day.

### What you can do with it

- **Find tenders to bid on.** Search by keywords in any language, by category in plain words ("road construction", "cleaning services") or by CPV code. Filter by buyer country, publication date and minimum value in euros, and keep only tenders that are still open.
- **Get a daily feed of new tenders, straight to Telegram or Slack.** Turn on *Only new notices since the last run*, add a Telegram bot, a Slack webhook or any webhook (Make, Zapier, n8n), and schedule the Actor. Each run sends a short digest of what's new, and you never pay twice for the same notice.
- **Decide whether to bid.** Turn on *Include lot details* to get every lot's value, deadline and contract duration, plus the award criteria (price, quality…), selection criteria and the submission link.
- **Research competitors and the market.** Pull contract award notices to see who won, for how much, and which buyers spend on what.
- **Feed a CRM or sales pipeline.** Each row has the buyer name, city and website, a deadline, an estimated value and a link to the tender documents.

### How to use it

1. Enter **keywords** (for example `solar`, `cybersecurity`), **categories in plain words** (for example `road construction`, `hospital beds`), **CPV codes** (for example `45000000`), or any mix of them.
2. Optionally pick **buyer countries** (`DE`, `FR`, `PL` …), **notice types** and a **date range**.
3. Set **Max notices**, or leave it empty to get every match.
4. Click **Start**, then download the results as Excel, CSV or JSON, or connect them to Google Sheets, Make, Zapier or n8n.

#### Example inputs

Open solar tenders in Germany, Poland and France from the last two weeks:

```json
{
  "keywords": ["solar", "photovoltaic"],
  "countries": ["DE", "PL", "FR"],
  "noticeTypes": ["competition"],
  "daysBack": 14,
  "openOnly": true
}
```

A daily Telegram digest of new software tenders worth at least €200,000, to run on a schedule:

```json
{
  "categories": ["software"],
  "noticeTypes": ["competition"],
  "minEstimatedValue": 200000,
  "monitorMode": true,
  "telegramBotToken": "<your bot token>",
  "telegramChatId": "<your chat id>"
}
```

Large construction tenders in Poland with lot details, to decide where to bid:

```json
{
  "categories": ["road construction", "bridge construction"],
  "countries": ["PL"],
  "minEstimatedValue": 1000000,
  "openOnly": true,
  "includeDetails": true
}
```

Who won construction contracts in Spain last month:

```json
{
  "cpvCodes": ["45000000"],
  "countries": ["ES"],
  "noticeTypes": ["result"],
  "publishedFrom": "2026-09-01",
  "publishedTo": "2026-09-30"
}
```

### What you get

Each notice becomes one row:

| Field | What it is |
|---|---|
| `noticeId`, `publicationDate` | TED notice number and publication date |
| `noticeTypeLabel`, `formType` | Open tender, contract award, prior information, change… |
| `title`, `description` | Original title and description, in the notice's own language |
| `headline` | Short headline in your chosen language: country, subject and title |
| `buyerName`, `buyerCountry`, `buyerCity`, `buyerWebsite` | The contracting authority |
| `contractNature`, `procedureType` | Works, supplies or services; open, restricted, negotiated… |
| `cpvMain`, `cpvCodes` | Main and all CPV codes |
| `placeOfPerformance` | NUTS region and country codes |
| `deadline` | Earliest submission deadline across the notice's lots |
| `estimatedValue`, `estimatedCurrency` | Estimated contract value in the notice's currency, when the buyer published it |
| `estimatedValueEur`, `awardedValueEur` | The same values converted to euros at the European Central Bank's daily rate |
| `awardedValue`, `awardedCurrency`, `winners`, `winnerCountries` | Contract award notices only |
| `frameworkAgreement` | Whether it is a framework agreement |
| `documentsUrl` | Link to the procurement documents on the buyer's platform |
| `noticeUrl`, `pdfUrl`, `xmlUrl` | Links to the notice on TED |

With **Include lot details** on, each row also gets:

| Field | What it is |
|---|---|
| `lots` | Every lot: title, description, CPV, value (also in EUR), deadline, duration, contract start and end, submission link, award criteria |
| `lotCount` | Number of lots |
| `awardCriteria` | How the winner is chosen, for example `price: Cena`, `quality: Termin gwarancji` |
| `selectionCriteriaTypes`, `selectionCriteria` | What bidders must prove: economic standing, technical ability… |
| `contractDuration`, `contractStart`, `contractEnd` | How long the contract runs |
| `submissionUrl` | Where to submit a bid |

**Compact output** (`"outputMode": "compact"`) returns only the essentials per notice: ID, date, type, headline, buyer, country, CPV, deadline, value in EUR, winners and link. It suits spreadsheets, alerts and AI agents.

Example row:

```json
{
  "noticeId": "715073-2026",
  "publicationDate": "2026-10-16",
  "noticeTypeLabel": "Contract notice",
  "title": "Servizi alberghieri e di ristorazione presso una struttura civile, ...",
  "headline": "Italy – Hotel accommodation services – Servizi alberghieri ...",
  "buyerName": "COMANDO TERRITORIALE SUD",
  "buyerCountry": "ITA",
  "buyerCity": "NAPOLI",
  "contractNature": "services",
  "procedureType": "open",
  "cpvMain": "55110000",
  "deadline": "2026-11-09",
  "estimatedValue": 3883600,
  "estimatedCurrency": "EUR",
  "documentsUrl": "https://www.acquistinretepa.it/bandi/aperta/gara-asp/832d5209a69b9f9c",
  "noticeUrl": "https://ted.europa.eu/en/notice/-/detail/715073-2026"
}
```

### Pricing

You pay **per notice saved**: **$2.50 per 1,000 notices**. Lot details, when you turn them on, add **$1.50 per 1,000 notices**. Alerts, euro conversion and category search are free. There is no monthly fee.

- Set **Max notices** or a spending limit for the run, and the Actor stops exactly there.
- In monitor mode you pay only for notices you haven't received before.
- A typical daily feed for one sector and a few countries is tens of notices a day, which comes to a few cents.

### Tips

- **Keywords match any language.** TED notices are written in the buyer's language, so add translations for better coverage, for example `solar`, `Photovoltaik`, `fotowoltaika`.
- **Categories in plain words** are matched against the official CPV names in 9 languages. The run summary (`OUTPUT_SUMMARY`) lists the codes used, so you can copy them into *CPV codes* and fine-tune. A term that matches nothing, or matches too much (like "services"), is searched as a keyword instead.
- **Minimum value is in euros.** Notices in złoty, kronor, forints and other currencies are converted with the ECB's daily rates. Notices without any published value are skipped when the filter is on.
- **CPV codes include their children.** `45000000` also returns `45210000`, `45233140` and so on. Codes with or without the check digit (`72000000-5`) both work.
- **Country codes** can have 2 letters (`DE`) or 3 (`DEU`).
- **Monitor mode remembers each search separately.** Change the keywords or countries and the Actor starts a new feed. The first run of a new feed uses *Published in the last N days* as its starting window.
- **Need a filter that isn't in the form?** Add a TED *expert query* in the Advanced section, for example `buyer-city=Paris`.

### FAQ

**Where does the data come from?**
From TED's official public search API, run by the Publications Office of the European Union. The Actor doesn't scrape web pages, so it is fast and doesn't break when the website changes.

**Is it legal to use this data?**
Yes. According to TED's legal notice, procurement notices published in the Supplement to the Official Journal of the EU can be freely reused for commercial or non-commercial purposes (Commission Decision 2011/833/EU).

**Does it collect personal data?**
No contact persons, emails or phone numbers are included. The Actor returns the buyer organisation, its city and public website.

**Why is the estimated value empty for some tenders?**
Buyers often don't publish an estimate. When a notice has no value at all, the field stays empty. The Actor never guesses.

**How fresh is the data?**
TED publishes new notices every working day. The Actor queries TED live on every run.

**Can I get notices older than this year?**
Yes. Fill in *Published from* and *Published to* to pick any period in TED's history. These dates take priority over *Published in the last N days*.

***

Source: TED – Tenders Electronic Daily, © European Union, https://ted.europa.eu. This Actor is not affiliated with or endorsed by the European Union or the Publications Office.

# Actor input Schema

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

Full-text search in any language. A notice matches if it contains any of these words or phrases. Example: \["solar", "photovoltaic"].

## `categories` (type: `array`):

Describe what is being bought, like "road construction", "cleaning services" or "hospital beds" (English, German, French, Spanish, Italian, Polish, Dutch, Portuguese or Swedish). Each one is turned into matching CPV codes; the codes used are listed in the run summary. Terms that match nothing or are too broad are searched as keywords instead.

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

Common Procurement Vocabulary codes, like 45000000 (construction work) or 72000000-5 (IT services). Child codes are included automatically.

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

2- or 3-letter country codes of the contracting authority, like DE, FR or POL. Leave empty for all countries.

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

Which notices to get. 'Open tenders' are calls for competition you can still bid on; 'Contract awards' name the winners and awarded values.

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

Used when 'Published from' is empty. Set 0 to search all of TED's history.

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

Only notices published on or after this date.

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

Only notices published on or before this date.

## `openOnly` (type: `boolean`):

Skip tenders whose submission deadline has passed.

## `minEstimatedValue` (type: `integer`):

Only notices whose estimated value (or awarded value for contract awards) is at least this many euros. Other currencies are converted with the European Central Bank's daily rates. Notices without a published value are skipped when this is set.

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

Stop after this many notices. Leave empty to get every match.

## `includeDetails` (type: `boolean`):

Add each lot's title, value, deadline and contract duration, plus award criteria (price, quality…), selection criteria and the submission link. Useful for deciding whether to bid.

## `outputMode` (type: `string`):

'Full' gives every field. 'Compact' gives a short row per notice, handy for spreadsheets, alerts and AI agents.

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

Language of the translated headline and the PDF link. The original title and description stay in the notice's own language.

## `monitorMode` (type: `boolean`):

Remember what earlier runs of this same search returned and save only notices published since. Schedule the Actor daily to get a feed of new tenders and pay only for new ones.

## `telegramBotToken` (type: `string`):

Send a digest of the saved notices to Telegram. Create a bot with @BotFather and paste its token here.

## `telegramChatId` (type: `string`):

Chat, group or channel to send the digest to. Add the bot to it first.

## `slackWebhookUrl` (type: `string`):

Send the digest to a Slack channel through an incoming webhook.

## `webhookUrl` (type: `string`):

POST the digest as JSON to any URL, for example a Make, Zapier or n8n webhook.

## `notifyWhenEmpty` (type: `boolean`):

Also send a message when a run finds no notices.

## `customQuery` (type: `string`):

Optional TED expert-search query added to the filters above with AND, for example: buyer-city=Paris. See the expert search help on ted.europa.eu.

## Actor input object example

```json
{
  "keywords": [
    "solar"
  ],
  "noticeTypes": [
    "competition"
  ],
  "daysBack": 7,
  "openOnly": false,
  "maxItems": 100,
  "includeDetails": false,
  "outputMode": "full",
  "language": "ENG",
  "monitorMode": false,
  "notifyWhenEmpty": false
}
```

# Actor output Schema

## `notices` (type: `string`):

One row per TED notice: buyer, deadline, values, winners and links.

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

The TED query used and how many notices were saved or skipped.

# 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": [
        "solar"
    ],
    "maxItems": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("gr1n/ted-tenders-monitor").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": ["solar"],
    "maxItems": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("gr1n/ted-tenders-monitor").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": [
    "solar"
  ],
  "maxItems": 100
}' |
apify call gr1n/ted-tenders-monitor --silent --output-dataset

```

## MCP server setup

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

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/kLQTntNARNoX7gHKy/builds/JBBnMJv9op6yfe7nF/openapi.json
