# Public Tender Monitor — EU (TED) + UK, new since last run (`handa-labs/public-tender-monitor`) Actor

Monitor EU public procurement (TED) and UK tenders (Contracts Finder, Find a Tender) with keywords, CPV codes and buyer countries. Returns clean, English-titled notices with buyer, deadline, value and links, and only what is new since your last run. Official APIs, no scraping.

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

## Pricing

from $5.00 / 1,000 tender 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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Public Tender Monitor (EU TED + UK) — new tenders by keyword, CPV & country

Get **new public tenders from the European Union and the United Kingdom** delivered as clean, structured data — filtered by keyword, CPV code and buyer country, with only **what is new since your last run**.

Three official sources, one record format:

- **TED (Tenders Electronic Daily)** — the EU's public procurement journal (~2,000 notices every working day from all EU member states plus EEA), via the official Search API.
- **UK Contracts Finder** — public-sector contracts in England, Wales and Northern Ireland, via its public OCDS API.
- **UK Find a Tender** — above-threshold UK procurement under the Procurement Act 2023, via its public OCDS API.

No scraping, no proxies, no login: results are exactly what the journals publish, normalised into one flat record per notice with an English title, the buyer (with contact e-mail where published), the deadline, the estimated value and direct links to the notice.

### Output

One `summary` record per run and one `notice` record per tender.

```json
{
  "type": "notice",
  "change": "new",
  "publicationNumber": "595241-2026",
  "title": "Germany – Software programming and consultancy services – Raumbuchungsprogramm für Desksharing",
  "titleLang": "eng",
  "noticeType": "cn-standard",
  "publicationDate": "2026-09-01",
  "deadline": "2026-09-30T09:00:00",
  "buyerName": "Stadt Chemnitz",
  "buyerCountry": "DEU",
  "buyerCity": "Chemnitz",
  "buyerEmail": "vergabe@stadt-chemnitz.de",
  "buyerWebsite": "https://www.chemnitz.de",
  "cpv": ["72200000"],
  "contractNature": ["services"],
  "procedureType": "open",
  "estimatedValue": 250000,
  "currency": "EUR",
  "placeOfPerformance": ["DED41", "DEU"],
  "lotTitles": ["Raumbuchungsprogramm"],
  "description": "Die Stadt Chemnitz beabsichtigt …",
  "documentUrl": "https://…/tender-documents",
  "htmlUrl": "https://ted.europa.eu/en/notice/-/detail/595241-2026",
  "pdfUrl": "https://ted.europa.eu/en/notice/595241-2026/pdf",
  "xmlUrl": "https://ted.europa.eu/en/notice/595241-2026/xml",
  "checkedAt": "2026-09-04T05:33:06.185Z",
  "llmSummary": "Stadt Chemnitz (DEU) published \"Germany – Software programming and consultancy services – Raumbuchungsprogramm für Desksharing\" on 2026-09-01, tenders due 2026-09-30T09:00:00. CPV 72200000."
}
```

The `summary` record carries the exact TED expert query that was executed (`query`), the total number of matching notices on TED, how many were new, and a one-sentence `llmSummary`.

You can download the dataset as JSON, CSV, Excel or XML, or read it via the API.

### Why use it

- **Win more bids** — be the first to see calls for competition in your niche (e.g. "cloud" + CPV 72\* + Germany) instead of browsing TED by hand.
- **Sales intelligence** — public buyers announce budgets months ahead. A tender for "SaaS HR system" is a qualified lead for every HR-software vendor.
- **Market research** — count tenders by country / CPV division over time.
- **AI agents & workflows** — schedule it daily, connect the dataset to Slack, email, Google Sheets, Make/n8n or your CRM, or call it as an MCP tool and let an agent read the `llmSummary`.

### How it works

1. Enter keywords (full-text, any language), CPV codes (with `*` prefix wildcards) and/or buyer countries (ISO alpha-3).
2. Choose the look-back window (`daysBack`, default 7) and notice types (default: contract notices — calls for competition).
3. Run. The first run captures a **baseline** (everything in the window). Every later run with the same filters returns **only notices not returned before** (`change: "new"`). Memory lives in a named key-value store, so schedules work out of the box.

### Input

```json
{
  "keywords": ["cloud", "software as a service"],
  "cpvCodes": ["72*", "48*"],
  "countries": ["DEU", "NLD"],
  "noticeTypes": ["cn-standard", "cn-social", "cn-desg"],
  "daysBack": 7,
  "sinceLastRun": true,
  "maxNotices": 500,
  "includeDescription": true
}
```

| Field | Default | Meaning |
|---|---|---|
| `sources` | all three | `ted`, `uk-contracts-finder`, `uk-find-a-tender`. UK sources are skipped automatically if `countries` excludes `GBR`. |
| `keywords` | — | Full-text search terms; a notice matching **any** keyword qualifies. Phrases allowed. (TED: server-side full text; UK: title, description and lot titles.) |
| `cpvCodes` | — | CPV codes; `72*` matches every code starting with 72 (IT services), `48*` software, `45*` construction, `79*` business services, `85*` health. |
| `countries` | — | Buyer country codes: DEU, FRA, NLD, ESP, ITA, POL, SWE, BEL, AUT, IRL, DNK, FIN, PRT, CZE, ROU, NOR … |
| `noticeTypes` | contract notices | TED notice types. `pin-*` = prior information notices, `can-*` = contract award notices, `cn-*` = calls for competition. |
| `daysBack` | 7 | Publication window. |
| `sinceLastRun` | true | Emit only notices not seen before for the same filters. |
| `maxNotices` | 500 | Safety cap per run (TED returns up to 250 per page; the Actor pages automatically). |
| `includeDescription` | true | Add up to 1,500 characters of the procedure/lot description (original language). |
| `stateStoreName` | `tender-monitor-state` | Where the "already seen" memory is kept. |

At least one of `keywords`, `cpvCodes`, `countries` is required.

### Monitor mode, watchlists and the free first run

All handa-labs monitors share the same controls:

| Field | Default | Meaning |
|---|---|---|
| `mode` | `monitor` | `monitor` remembers what was returned and emits only changes on later runs; `snapshot` returns everything matching on every run |
| `watchlistId` | `default` | label for this watchlist; runs with the same ID share memory, different IDs are independent |
| `emitBaseline` | `false` | the first monitor run establishes a **free baseline** (only the Actor-start fee) and returns counts; set `true` to also receive, and pay for, every current item on that first run |

Schedule the Actor with the same input and you get a clean change feed. Nothing is charged for records on the first run unless you ask for them.

### Pricing

Pay-per-event: a small fee per run plus a fee per notice returned. Because only new notices are returned on scheduled runs, a daily watch on a focused query typically costs a few cents per day.

### Notes

- Source: [TED — Tenders Electronic Daily](https://ted.europa.eu), official Search API v3. TED data may be reused free of charge under the EU's reuse policy; keep the notice links when you republish.
- Titles come from TED's multilingual title field; English is preferred when available (it usually is, in the form "Country – CPV description – original title").
- Contract award notices (`can-*`) include winners; prior information notices (`pin-*`) announce future procurement. Add them to `noticeTypes` if you need them.
- Want US federal (SAM.gov), UK (Contracts Finder / Find a Tender) or other sources in the same feed? Open an issue on the Actor page — they are on the roadmap.

***

### 日本語

EUの公共調達公告(TED)を、キーワード・CPVコード・発注国で絞り込み、**前回以降に増えた公告だけ**を構造化して返します。TEDの公式検索APIを使うのでスクレイピング不要、ログイン不要です。

- 入力: キーワード(全文検索、複数可)、CPVコード(`72*` のような前方一致可)、発注国(ISO3文字)、遡る日数、公告種別
- 出力: 英語タイトル、発注者名・国・都市・メール・サイト、締切日時、推定金額と通貨、CPV、履行地、説明文、HTML/PDF/XMLリンク、AI向けの1文要約
- 初回はベースライン(期間内の全件)、2回目以降は新規のみ。スケジュール実行とSlack/メール/スプレッドシート連携でそのまま入札監視になります
- 料金: 実行ごとの少額 + 返した公告1件ごとの従量課金

# Actor input Schema

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

Words or phrases searched in the full text of the notice (any language). Notices matching ANY keyword are returned. Leave empty to filter by CPV / country only.

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

Common Procurement Vocabulary codes, e.g. 72000000 (IT services), 48000000 (software), 45000000 (construction). A trailing \* matches a prefix (72\* = all IT services).

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

e.g. DEU, FRA, NLD, ESP, ITA, POL, SWE, BEL, AUT, IRL. Empty = all countries.

## `sources` (type: `array`):

Which procurement journals to search. TED = EU (all member states + EEA). UK sources are skipped automatically when the countries filter excludes GBR.

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

"monitor" (default): remember what was returned and emit only changes on later runs. "snapshot": return everything matching, every run, no memory.

## `watchlistId` (type: `string`):

Label for this watchlist (e.g. "roofing-wa"). Runs with the same ID share memory; use different IDs for independent watchlists.

## `emitBaseline` (type: `boolean`):

The first monitor run establishes a free baseline and returns only counts. Turn this on to also receive (and pay for) every current item on that first run.

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

TED notice types to include. Default: contract notices (calls for competition). Use pin-\* for prior information notices, can-\* for contract award notices.

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

Only notices published within the last N days.

## `sinceLastRun` (type: `boolean`):

Legacy switch. Prefer "mode". false = snapshot mode.

## `maxNotices` (type: `integer`):

Safety cap on the number of notices fetched from TED per run.

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

Adds up to 1500 characters of the procedure/lot description (original language).

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

Named key-value store used to remember already-seen notices.

## Actor input object example

```json
{
  "keywords": [
    "cloud",
    "software as a service"
  ],
  "cpvCodes": [
    "72*",
    "48*"
  ],
  "countries": [
    "DEU",
    "NLD"
  ],
  "sources": [
    "ted",
    "uk-contracts-finder",
    "uk-find-a-tender"
  ],
  "mode": "monitor",
  "watchlistId": "default",
  "emitBaseline": false,
  "noticeTypes": [
    "cn-standard",
    "cn-social",
    "cn-desg"
  ],
  "daysBack": 7,
  "sinceLastRun": true,
  "maxNotices": 500,
  "includeDescription": true,
  "stateStoreName": "tender-monitor-state"
}
```

# 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 = {
    "keywords": [
        "cloud",
        "software as a service"
    ],
    "cpvCodes": [
        "72*",
        "48*"
    ],
    "countries": [
        "DEU",
        "NLD"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("handa-labs/public-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 = {
    "keywords": [
        "cloud",
        "software as a service",
    ],
    "cpvCodes": [
        "72*",
        "48*",
    ],
    "countries": [
        "DEU",
        "NLD",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("handa-labs/public-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 '{
  "keywords": [
    "cloud",
    "software as a service"
  ],
  "cpvCodes": [
    "72*",
    "48*"
  ],
  "countries": [
    "DEU",
    "NLD"
  ]
}' |
apify call handa-labs/public-tender-monitor --silent --output-dataset

```

## MCP server setup

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