# EU TED Tenders Monitor & API — Public Procurement Alerts (`cranewell/ted-monitor`) Actor

Monitor new EU public tenders from TED (Tenders Electronic Daily) through the official TED API v3. Filter by CPV code, buyer country, notice type, keywords and value. Scheduled runs return only new notices. No API key needed.

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

## Pricing

from $3.00 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/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 TED Tenders Monitor & API — Public Procurement Alerts

📖 Guide: [How to get EU tender alerts automatically with the TED API (no API key)](https://medium.com/@cranewell/ted-tenders-api-how-to-get-eu-tender-alerts-automatically-no-api-key-5f9d18a5afdd)

> Built by Cranewell. Maintained on a best-effort basis by automated checks; **no custom support, consulting, or SLA is offered.** Report issues via the Store's Issues tab.

### What does EU TED Tenders Monitor do?

EU TED Tenders Monitor searches **TED (Tenders Electronic Daily)**, the EU's official public-procurement journal, through the **official TED API v3** anonymous search endpoint. It outputs clean, normalized tender records. Run it on a schedule with **Only new since last run** on, and each run returns only notices you haven't seen yet. That gives you an EU tender alert feed you can send to email, Slack, Google Sheets or your own endpoint with Apify integrations and webhooks.

- **No TED API key, no login, no website scraping.** It uses the official API only.
- **Filters:** publication date range or "last N days", CPV codes (hierarchical), buyer country (ISO alpha-3), eForms notice type, full-text keywords, exclude keywords, minimum value, and deadline on/after.
- **Large result sets** via TED's iteration (scroll) pagination.
- **Webhook-ready run summary** (counts + up to 50 new items) saved as `SUMMARY` in the run's key-value store.

#### Who uses TED tender alerts?

Bid and proposal teams watching their CPV codes, consultants tracking public-sector demand in specific countries, and analysts building datasets of EU public procurement.

### How to monitor EU tenders with the TED API

1. Pick your **CPV codes** (e.g. `72000000` for IT services) and **buyer countries** (`DEU`, `FRA`, `IRL`...).
2. Run once to check results.
3. Save the input as a **Task** and add a **Schedule** (e.g. daily at 07:00).
4. Leave **Only new since last run** on. Each scheduled run returns only new tenders.
5. Optional: connect an integration (email, Slack, Sheets, Zapier, Make) or a webhook that reads the `SUMMARY` record.

#### Get only new tenders since the last run

"Only new" state is stored in a named key-value store (`tenders-monitor-ted`) keyed by a hash of your filters, so different Tasks keep separate state. Use **Reset seen-ID state** to start over.

### Input example

```json
{ "keywords": ["software"], "codes": ["72000000"], "countries": ["DEU", "FRA"], "noticeTypes": ["cn-standard"], "daysBack": 7, "maxItems": 50 }
```

#### Filter by CPV code, country, notice type and value

| Field | What it does |
|---|---|
| `publishedFrom` / `publishedTo` or `daysBack` | Publication window (default: last 7 days) |
| `codes` | CPV codes; hierarchical, so `72000000` covers its sub-codes |
| `countries` | Buyer country, ISO 3166 alpha-3 (e.g. `DEU`) |
| `noticeTypes` | eForms notice type (pre-filled with `cn-standard`; leave empty for all types) |
| `keywords` / `excludeKeywords` | `keywords`: TED full-text search (any word matches). `excludeKeywords`: drop notices whose title/buyer contains these words |
| `minValue` | Minimum estimated value. Notices without a value are dropped when this is set |
| `deadlineAfter` | Only tenders whose deadline is on or after this date |
| `onlyNew`, `resetState`, `maxItems` | Monitoring state and per-run cap (default 1,000) |
| `webhookSummary` | Write the `SUMMARY` record (default on) |

### Output example (real TED record, fetched 2026-10-28)

```json
{
  "id": "TED-737689-2026",
  "source": "ted",
  "title": "Germany – IT services: consulting, software development, Internet and support – Rahmenvereinbarungen Programmier- und Beratungsleistungen zur Anwendungsentwicklung im Ressort des StMELF",
  "buyer": "Freistaat Bayern, vertreten durch das Bayer. StM f. Ernährung, Landwirtschaft, Forsten und Tourismus",
  "country": "DEU",
  "codes": ["72000000", "72211000", "72200000", "72243000", "72262000", "72263000"],
  "codeType": "CPV",
  "noticeType": "cn-standard",
  "setAside": null,
  "publishedDate": "2026-10-26",
  "deadline": "2026-11-23",
  "value": 46875000,
  "currency": "EUR",
  "url": "https://ted.europa.eu/en/notice/-/detail/737689-2026",
  "isNew": true,
  "fetchedAt": "2026-10-29T00:38:37.174Z"
}
```

**Fields:**

- `id, source, title, buyer, country, codes, codeType, noticeType, setAside, publishedDate, deadline, value, currency, url, isNew, fetchedAt`.
- `value`/`currency` are the buyer's *estimated* value at publication. It is often missing: about 30% of contract notices in our test had one.
- `deadline` is the earliest lot deadline.
- The title is TED's English title, which usually quotes the original-language name.
- The output does not include full notice text, lots breakdown, or contact details. Open `url` for the full notice.

### How to use the TED tenders API in Python or JavaScript

Call this Actor with the Apify API or the official `apify-client` packages, pass the JSON input above, and read the dataset items. Monitoring state lives on Apify, so your code doesn't need to track seen notices.

### How much does it cost to monitor TED tenders?

| Event | Price |
|---|---|
| Actor start | $0.005 per run |
| Result | $0.003 per notice written to the dataset ($3 per 1,000) |

Example: a daily run returning 20 new notices costs $0.005 + $0.06 = $0.065, about $2 a month. With **Only new** on, you pay only for new notices. Set a **maximum total charge** on the run to cap spend; the Actor is designed to stop cleanly when it's reached (covered by our automated tests). Platform compute is billed by Apify as usual.

### Limits

- Notices beyond **Max results** aren't marked as seen, so later runs deliver them.
- TED's fair-usage policy allows 700 HTTP requests per minute. This Actor self-limits to 60 per minute, with retry/backoff on 429 and 5xx errors.
- Covers notices published on TED, which are mainly above-EU-threshold tenders. It doesn't cover national-only portals. Fields vary by notice type and country.

### FAQ

#### Do I need a TED API key?

No. TED's search API allows anonymous access to published notices.

#### Is it legal to reuse TED data?

TED states that its API allows anonymous access for searching and retrieving published notices, and reuse is governed by the EU Publications Office legal notice (linked below). This Actor isn't affiliated with or endorsed by the EU Publications Office.

#### Can I get alerts in Slack or email?

Yes. Schedule a Task and attach an Apify integration, or a webhook that reads the `SUMMARY` record.

#### Can I also monitor US federal contracts?

Yes. Use our twin Actor **[SAM.gov Contracts Monitor & API — Federal Bid Alerts](https://apify.com/cranewell/sam-gov-monitor)**. It outputs the same core fields (plus SAM-specific ones such as `agencyPath` and `solicitationNumber`), so EU and US tenders fit one workflow.

### Data source & terms

- TED API docs: https://docs.ted.europa.eu/api/latest/index.html
- Fair-usage policy: https://ted.europa.eu/en/news/fair-usage-policy-on-ted
- Legal notice (reuse): https://ted.europa.eu/en/legal-notice

# Actor input Schema

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

Published from (YYYY-MM-DD).

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

Published to (YYYY-MM-DD).

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

When no from-date is set, look back this many days.

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

Full-text search in TED (any word matches), e.g. 'software'.

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

Drop notices whose title/buyer contains any of these words.

## `codes` (type: `array`):

8-digit CPV codes, e.g. 72000000 (IT services). TED matches the hierarchy.

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

e.g. DEU, FRA, IRL

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

TED eForms notice types, e.g. cn-standard (contract notice), pin-only, can-standard (award). Leave empty for all.

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

Drop notices with a value below this (notices without a value are dropped when set).

## `deadlineAfter` (type: `string`):

Deadline on/after (YYYY-MM-DD).

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

Output only notices not seen in previous runs with the same filters (state is kept in a named key-value store, keyed by a hash of your filters).

## `resetState` (type: `boolean`):

Forget previously seen notices for this filter set before running.

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

Hard cap on results output (and charged) per run.

## `webhookSummary` (type: `boolean`):

Save a compact JSON summary (counts + new items) to the default key-value store under key SUMMARY, for use in Apify webhooks/integrations.

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

Named key-value store for seen IDs. Leave default unless you want separate state.

## `pageSize` (type: `integer`):

Page size.

## `maxPages` (type: `integer`):

Cap on API requests per run.

## Actor input object example

```json
{
  "daysBack": 7,
  "noticeTypes": [
    "cn-standard"
  ],
  "onlyNew": true,
  "resetState": false,
  "maxItems": 1000,
  "webhookSummary": true,
  "pageSize": 100,
  "maxPages": 50
}
```

# Actor output Schema

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

All notices pushed to the default dataset, shown in the Overview view.

# 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 = {
    "noticeTypes": [
        "cn-standard"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("cranewell/ted-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 = { "noticeTypes": ["cn-standard"] }

# Run the Actor and wait for it to finish
run = client.actor("cranewell/ted-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 '{
  "noticeTypes": [
    "cn-standard"
  ]
}' |
apify call cranewell/ted-monitor --silent --output-dataset

```

## MCP server setup

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