# EU Tenders & TED — Public Procurement, Appels d'Offres (MCP) (`amygraphics/eu-tender-monitor`) Actor

TED monitoring for public procurement in Europe: tenders & appels d'offres from the official EU journal — buyer, CPV, deadlines, values, buyer e-mail. Monitor mode returns only NEW or changed notices. Aanbesteding, Ausschreibung, marchés publics. MCP-ready for AI agents.

- **URL**: https://apify.com/amygraphics/eu-tender-monitor.md
- **Developed by:** [Zakaria el bouami](https://apify.com/amygraphics) (community)
- **Categories:** Business
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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 Monitor — TED Public Procurement (MCP)

Search and **monitor EU public tenders** from **TED** (Tenders Electronic Daily) — the official journal of public procurement of the European Union: **~3 500 notices published every day, ~1.2 million per year**, covering roughly **€670 billion** of public spending. The data comes from the official TED Search API (no key, no browser, no proxy).

This Actor is built for one job: turn the firehose of EU tenders into a **filtered, deduplicated feed of opportunities** — and, when scheduled, into an **automatic watch service that tells you only what is new or changed**.

### Two modes

#### `search` — extract matching notices

Filter by buyer country, keywords, CPV code family, notice type, publication dates, submission deadline and estimated value. One clean JSON row per notice (or per lot).

#### `monitor` — only new & changed notices

The Actor stores every notice it has seen (publication number + fingerprint of deadline, value, titles, winners) in the Apify key-value store. On the next run it reports:

- **`isNew: true`** — a notice you have never seen before,
- **`isUpdated: true`** — an already-seen notice whose **deadline, value, title, winners or type changed** (new deadlines are the ones that make you lose a bid if you miss them),
- and skips everything else.

Add a **schedule (e.g. every morning)** and you have a private tender watch: you only ever pay for the rows that actually concern you.

### What you get

One row per notice with:

| Field | Example |
|---|---|
| `publicationNumber` / `tedUrl` | 685623-2026 / link to the official notice |
| `publicationDate` / `noticeType` | 2026-10-06+02:00 / can-standard |
| `title` / `titleEn` / `titleAll` | title in English + all published languages |
| `buyerName` / `buyerCountry` / `buyerCity` / `buyerEmail` | Département de la Vendée / FRA / La Roche Sur Yon / marchespublics@vendee.fr |
| `estimatedValue` / `estimatedValueCurrency` / `estimatedValuePerLot` / `totalValue` | 1 800 000 / EUR / per-lot values |
| `cpvCodes` / `mainCpv` | \["72400000", "72413000", …] |
| `placeOfPerformance` | NUTS codes |
| `contractNature` / `procedureType` | services / open |
| `deadlineReceiptTenders` / `deadlineReceiptRequests` | submission deadlines |
| `lotTitles` / `lotCount` / `lotNumber` / `lotTitle` / `lotDeadline` / `lotEstimatedValue` | per-lot details (`rowPerLot`) |
| `resultValues` / `winners` | award results when published |
| `links` | direct HTML / XML / PDF of the notice |
| `isNew` / `isUpdated` / `changeType` / `firstSeenAt` | monitor flags |

### How to use

1. Pick a **mode** (`search` for a one-off export, `monitor` for a watch).
2. Set filters: **countries** (FRA, DEU, ESP…), **keywords**, **CPV prefixes** (`45` construction, `72` IT services…), **notice types**, a **date window** and, optionally, **deadlines** and **value** range.
3. Set **max notices per run** and press **Start**.
4. For monitoring: save it as a **Task**, add a **schedule** (daily), and set the same **monitor state key** if you run several saved queries.

**Ready-made examples**

- All French construction tenders (CPV `45`) published in the last 7 days, deadline still open.
- IT services (`72`) in DEU/NLD with an estimated value above €500 000, sorted by value.
- Daily watch: `keywords: ["logiciel", "software"]`, `countries: ["FRA"]`, mode `monitor`, scheduled every morning.
- Solar/energy tenders across the EU, one row per lot.

### Use cases

- **SMEs & contractors** — find the tenders you can actually win, with the buyer's contact and the real deadlines.
- **Bid consultancies & business developers** — a daily feed of new opportunities for their clients, per sector and country.
- **Translation, IT, construction, facility, security, medical suppliers** — CPV-precise monitoring instead of manual daily browsing of TED.
- **Analysts & data teams** — a structured feed of EU public spending (prices, buyers, winners) for market studies.
- **AI agents** — a ready-to-call EU procurement tool (this Actor supports agentic payments via the Apify MCP server).

### Use from AI agents (MCP)

This Actor is exposed as a tool through **Apify's MCP server** — any MCP client (Claude, Cursor, VS Code / Copilot, Codex, custom agents) can discover and run it without writing code:

```json
{
  "mcpServers": {
    "apify": { "url": "https://mcp.apify.com?tools=amygraphics/eu-tender-monitor" }
  }
}
```

Then simply ask: *"Use amygraphics/eu-tender-monitor to list the newest EU tenders about solar panels in Spain and Germany."*

Agents can also pay autonomously — this Actor is **whitelisted for agentic payments** (Direct x402 / Apify AGI prepaid token), so a run does not require an Apify account.

### Pricing

Pay-per-event: a small start fee plus **$3 per 1 000 notices** extracted (one event per notice, or per lot with `rowPerLot`). In monitor mode you only pay for the notices that are new or changed — a daily watch costs a few cents.

### Notes & limitations

- Source: official TED Search API (European Publications Office). Data is reused as published; notices are also available in XML/PDF via the `links` field.
- TED publishes in all official EU languages, so `title` returns the first available language and `titleAll`/`descriptionAll` keep the full multilingual payload.
- Notices without a published value are kept unless you set a minimum value; `estimatedValue` prefers the smallest lot value or the global estimate.
- Pagination is capped at 250 notices per API request; keep the date window realistic (a 7-day window across all countries is tens of thousands of notices — use filters).
- This Actor reads the official API, not the TED website: it never breaks because of a redesign.

***

## Version FR

Recherchez et **surveillez les appels d'offres publics européens** depuis **TED** (Tenders Electronic Daily), le journal officiel de la commande publique de l'Union européenne : **~3 500 avis publiés chaque jour, ~1,2 million par an**, ~670 milliards d'€ de dépenses publiques. Les données viennent de l'API officielle TED (sans clé, sans navigateur, sans proxy).

**Deux modes :**

- **`search`** — extraction filtrable : pays acheteur, mots-clés, codes CPV, type d'avis, dates de publication, date limite de remise et valeur estimée. Une ligne JSON propre par avis (ou par lot).
- **`monitor`** — le meilleur : l'Actor mémorise tous les avis déjà vus (empreinte de la date limite, de la valeur, des titres et des attributaires) et ne ressort que **ce qui est nouveau (`isNew`) ou modifié (`isUpdated`)**. Programmez-le chaque matin (Task + Schedule) et vous obtenez une veille automatique — vous ne payez que les lignes qui vous concernent.

**Vous obtenez :** numéro d'avis et lien officiel, date de publication, type d'avis, titre (toutes langues publiées + version anglaise), acheteur (nom, pays, ville, **e-mail**), valeur estimée et globale, codes CPV, lieu d'exécution (NUTS), nature du marché, type de procédure, **dates limites de remise**, lots (titre, valeur, date limite), résultats et attributaires, liens HTML/XML/PDF.

**Cas d'usage :** PME et entreprises candidates, consultants et apporteurs d'affaires (flux quotidien par secteur et pays), traduction, informatique, BTP, fournitures, sécurité, santé — veille par CPV au lieu d'une consultation manuelle quotidienne de TED ; analystes (flux structuré de la dépense publique) ; **agents IA**.

**Agents IA (MCP) :** cet Actor est exposé comme outil du serveur MCP d'Apify — branchez `https://mcp.apify.com?tools=amygraphics/eu-tender-monitor` dans Claude, Cursor, VS Code ou Codex, puis demandez vos avis en langage naturel. Les agents peuvent payer seuls (x402 / AGI) : l'Actor est *whitelisted* pour les paiements agentiques.

**Prix :** paiement à l'événement — petit frais de démarrage + **3 $ les 1 000 avis** (un événement par avis, ou par lot avec `rowPerLot`). En mode monitor, vous ne payez que les avis nouveaux ou modifiés.

# Actor input Schema

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

search = return every matching notice in the date window. monitor = compare with the previous run (stored in the Apify key-value store) and flag what is new or changed.

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

ISO 3-letter buyer country codes, e.g. FRA, DEU, ESP, ITA, POL, NLD. Leave empty for all countries covered by TED (EU + EEA + partner countries).

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

Search terms matched against the full text of the notice (all languages published). Terms are OR-combined by default; set 'Match all keywords' to require every term. E.g. solar, 'data center', traduction, logiciel.

## `matchAllKeywords` (type: `boolean`):

If enabled, a notice must contain ALL keywords. Default: any keyword is enough (better recall for monitoring).

## `excludeKeywords` (type: `array`):

Drop any notice whose title or English description contains one of these terms (case-insensitive). Useful to cut noise from a broad query.

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

CPV (Common Procurement Vocabulary) codes to filter on. A prefix matches the whole family: '45' = construction, '72' = IT services, '30200000' = computers. Digits only.

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

Notice types to include. cn-standard = contract notices (the live opportunities to bid on); can-standard = contract award notices (who won, at what price); can-modif = award modifications; corr = corrections/corrigenda; veat = voluntary ex-ante transparency; pin-only/pin-buyer = prior information; cn-social/can-social = social & other services; cn-desg/can-desg = design contests; can-tran = subcontracted contracts; subco = subcontracting notice. Default: cn-standard only.

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

Only notices published on or after this date (YYYY-MM-DD). Leave empty for a default window: last 7 days in search mode, last 2 days in monitor mode.

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

Only notices published on or before this date (YYYY-MM-DD). Leave empty for today.

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

Only notices whose submission deadline is on or after this date (YYYY-MM-DD). Use it to get only opportunities you can still bid on.

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

Only notices whose submission deadline is on or before this date (YYYY-MM-DD).

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

Drop notices below this estimated contract value (in the currency of the notice, usually EUR). Filtered on the smallest lot value when per-lot values are published.

## `maxValue` (type: `integer`):

Drop notices above this estimated contract value. Notices without any published value are kept when a minimum is set only.

## `sort` (type: `string`):

newest = most recently published first (default). deadline = soonest submission deadline first. value-desc = biggest estimated value first. oldest = oldest publication first.

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

Hard cap on the number of rows saved in this run (1-5000). Each notice (or lot, when 'One row per lot' is on) is one billed event.

## `rowPerLot` (type: `boolean`):

When a notice contains several lots, output one row per lot with that lot's title, deadline and estimated value. Each row is billed.

## `includeRaw` (type: `boolean`):

Add the full raw notice object (every field requested from TED) under the 'raw' key of each row.

## `stateKey` (type: `string`):

Monitor mode only. Name of the memory slot used to remember what you already saw. Use a different key per saved query; leave empty to derive it from the filters automatically.

## `onlyChanges` (type: `boolean`):

Monitor mode only. When enabled, unchanged notices are skipped, so a daily schedule sends you only what actually moved. Disable it to see everything with isNew/isUpdated flags.

## `chargedEventName` (type: `string`):

Pay-per-event name to charge for each extracted notice. Keep the default unless you have a custom reason.

## Actor input object example

```json
{
  "mode": "search",
  "countries": [
    "FRA",
    "BEL"
  ],
  "keywords": [
    "solar",
    "photovoltaic"
  ],
  "matchAllKeywords": false,
  "excludeKeywords": [
    "nettoyage",
    "cleaning"
  ],
  "cpvPrefixes": [
    "45",
    "72"
  ],
  "noticeTypes": [
    "cn-standard"
  ],
  "dateFrom": "",
  "dateTo": "",
  "deadlineFrom": "",
  "deadlineTo": "",
  "sort": "newest",
  "maxResults": 10,
  "rowPerLot": false,
  "includeRaw": false,
  "stateKey": "",
  "onlyChanges": true,
  "chargedEventName": "notice-extracted"
}
```

# Actor output Schema

## `results` (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 = {
    "mode": "search",
    "noticeTypes": [
        "cn-standard"
    ],
    "sort": "newest",
    "maxResults": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("amygraphics/eu-tender-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 = {
    "mode": "search",
    "noticeTypes": ["cn-standard"],
    "sort": "newest",
    "maxResults": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("amygraphics/eu-tender-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 '{
  "mode": "search",
  "noticeTypes": [
    "cn-standard"
  ],
  "sort": "newest",
  "maxResults": 10
}' |
apify call amygraphics/eu-tender-monitor --silent --output-dataset

```

## MCP server setup

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