# Tender Alert — New EU Tender Watch (`zinin/tender-alert`) Actor

Watch EU public-sector tenders (TED Europa) matching your keywords/CPV codes and get ONLY the new ones since your last check. Run it on a schedule and stop re-reading the same notices every time.

- **URL**: https://apify.com/zinin/tender-alert.md
- **Developed by:** [Tim Zinin](https://apify.com/zinin) (community)
- **Categories:** Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $8.50 / 1,000 change founds

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/platform/actors/running/actors-in-store#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

## Tender Alert — New EU Tender Watch

Watch EU public-sector tenders (TED Europa) by keyword or CPV code and get only the
tenders that are NEW since your last check — not the same 20 notices you already read
last week. Runs on our own live tender-search engine under the hood, so you never
re-download the whole list just to spot what changed.

### What you get

- A named "watch" (keywords + CPV code + optional buyer country) that remembers what
  it has already shown you.
- Every scheduled run reports ONLY tenders that were not there last time — title, buyer,
  country, CPV code, deadline and the notice URL.
- Run several independent watches from one Actor (e.g. one for IT services in Germany,
  one for construction tenders in France) — each has its own memory.
- The very first run for a new watch tells you honestly that it's establishing a
  baseline, not hiding a "no new tenders" run as if it found something.
- Runs on Apify: schedule it daily, monitor it, call it from the API, export to
  JSON/CSV/Excel or push straight into your own pipeline.

### How to run it

1. Click **Try for free** — no card needed on the free plan.
2. Paste your search keywords or a CPV code (e.g. `classification-cpv=72000000`) into
   **Search queries**, optionally a buyer country and a name for this watch.
3. Hit **Start**. The first run creates the baseline; schedule it to run again (daily or
   weekly) to get a stream of only the NEW tenders each time.

### Pricing

Pay-per-event: **$0.005 per run start + $0.01 per NEW tender / baseline
created**. The "no new tenders" notice on a quiet run and any error/notice row are never
charged. 10 new tenders cost about $0.11 (the one-time start fee plus 10 billed rows).

### Input

| Field | Required | What it does |
|---|---|---|
| `queries` | yes | Keywords or TED expert-query expressions, e.g. `"software"` or `"classification-cpv=72000000"`. Up to 20. |
| `country` | no | ISO 3-letter buyer-country code (e.g. `"DEU"`) ANDed into every query. Leave empty for all EU countries. |
| `baseline_key` | no | Name for this watch, so you can run several independent watches. Defaults to a single watch if left empty. |
| `limit` | no | Notices requested per query, per run (1-100, default 20). |
| `max_items` | no | Max NEW-tender rows delivered (and charged) per run, even if more were found (1-200, default 20). |

```json
{
    "queries": ["classification-cpv=72000000"],
    "limit": 5,
    "baseline_key": "eu-it-services"
}
```

### Output

Baseline-created row (first run for a watch — real output, live run 30.07.2026):

```json
{
    "baselineKey": "selftest-live-run",
    "found": true,
    "isNew": false,
    "baselineCreated": true,
    "publicationNumber": null,
    "title": null,
    "buyer": null,
    "buyerCountry": null,
    "cpv": null,
    "deadline": null,
    "publicationDate": null,
    "url": null,
    "currentTendersCount": 3,
    "newTendersCount": null,
    "error": "",
    "summary": "First check for \"selftest-live-run\" — baseline created with 3 current tender(s) matching the filter. Future runs report only NEW tenders against this baseline.",
    "checkedAt": "2026-07-30T07:49:04.762Z"
}
```

New-tender row (real output, live run 30.07.2026):

```json
{
    "baselineKey": "selftest-live-run",
    "found": true,
    "isNew": true,
    "baselineCreated": false,
    "publicationNumber": "526766-2026",
    "title": "Denmark – Architectural, construction, engineering and inspection services – DIS-IHK-AG - Dynamisk Indkøbssystem: In house-konsulenter til arealerhvervelse, GIS, CAD, kortlægning og ledninger",
    "buyer": "Vejdirektoratet",
    "buyerCountry": "DNK",
    "cpv": "71000000, 72000000, 71355000, 71354000, 71350000",
    "deadline": null,
    "publicationDate": "2026-07-30+02:00",
    "url": "https://ted.europa.eu/en/notice/-/detail/526766-2026",
    "currentTendersCount": null,
    "newTendersCount": 1,
    "error": "",
    "summary": "New tender for \"selftest-live-run\": Denmark – Architectural, construction, engineering and inspection services – DIS-IHK-AG - Dynamisk Indkøbssystem: In house-konsulenter til arealerhvervelse, GIS, CAD, kortlægning og ledninger — Vejdirektoratet (DNK).",
    "checkedAt": "2026-07-30T07:50:03.123Z"
}
```

| Field | Meaning |
|---|---|
| `baselineKey` | Which watch this row belongs to. |
| `found` | `true` for a real result row (new tender or baseline-created); `false` for a notice/error row. |
| `isNew` | `true` only for an actual new-tender row. |
| `baselineCreated` | `true` only on the first-ever run for this watch. |
| `publicationNumber`, `title`, `buyer`, `buyerCountry`, `cpv`, `deadline`, `publicationDate`, `url` | The tender's own fields, straight from the TED notice. |
| `currentTendersCount` | Only set on the baseline-created row: how many tenders matched at that moment. |
| `newTendersCount` | Total new tenders detected this run (may exceed the rows actually delivered if capped by `max_items` or your remaining budget). |
| `error` | Empty string when the check completed cleanly (including "nothing new"); non-empty only on a real problem. |
| `summary` | Human-readable one-liner. |

### Other tools we built

#### Related tools

Related tools for adjacent workflows in compliance and public procurement.

| Actor | What it does |
|---|---|
| [Sanctions Update Alert — Watchlist Change Monitor](https://apify.com/zinin/sanctions-update-alert) | Pair it in the compliance and public procurement workflow: Watch a list of names against OFAC SDN and EU consolidated sanctions lists and get ONLY the changes since... |
| [Gov Tender Radar](https://apify.com/zinin/gov-tender-radar) | Pair it in the compliance and public procurement workflow: Search EU public-sector tenders (TED Europa) by keyword or CPV code, optionally filtered by buyer country |
| [Company Registry Enricher](https://apify.com/zinin/company-registry-enricher) | Pair it in the compliance and public procurement workflow: Turn a company name, LEI or UK company number into an official registry card: legal name, status,... |
| [EU AI Act Transparency Lead Scanner](https://apify.com/zinin/eu-ai-act-transparency-lead-scanner) | Pair it in the compliance and public procurement workflow: BYOD compliance scanner for observed AI transparency signals on a submitted URL. Deterministic,... |
| [Litigation Check — Company Litigation History Screener](https://apify.com/zinin/litigation-check) | Pair it in the compliance and public procurement workflow: Screen a counterparty for litigation history across US federal PACER dockets, UK case law and Poland's... |

### FAQ / Limitations

**Does this cover tenders outside the EU?** No — TED Europa is EU-wide public
procurement only.

**What happens if I change my `queries` for an existing `baseline_key`?** The watch
keeps comparing against whatever it last saw, so changing the filter can produce a
burst of "new" tenders that simply weren't checked for under the old filter. Use a new
`baseline_key` for an intentionally different watch.

**What this is NOT.** This is not legal or compliance advice on tender eligibility, and
it does not submit bids or contact buyers. It surfaces public notice data only.

Found a bug or need a custom watch (e.g. tracking deadline changes, not just new
tenders)? Issues on the Actor's page.

# Actor input Schema

## `queries` (type: `array`):

Keywords or TED expert-query expressions defining which tenders to watch — same syntax as gov-tender-radar (e.g. "software", "classification-cpv=72000000", "cloud AND classification-cpv=72\*"). Every scheduled run re-checks these same queries and reports ONLY tenders not seen on a previous run for this watch.

## `country` (type: `string`):

ISO 3-letter buyer-country code to AND into every query, e.g. "DEU", "FRA". Leave empty for all EU countries.

## `baseline_key` (type: `string`):

A name for THIS watch, so you can run several independent tender watches from one Actor (e.g. "eu-it-services", "germany-construction") without one overwriting another's memory of what's already been seen. Each name is scoped to YOUR OWN Apify account — nobody else's watch is visible to you and vice versa. The prefilled value is only there so this Actor's own daily test run has a stable, obviously-a-test name; replace it with your own watch name.

## `limit` (type: `integer`):

Max number of notices gov-tender-radar returns per query, per run. Higher means more thorough coverage but more of OUR compute (never billed to you beyond the per-tender price below).

## `max_items` (type: `integer`):

Caps how many NEW-tender rows a single run will deliver and charge for, even if more were found.

## Actor input object example

```json
{
  "queries": [
    "classification-cpv=72000000"
  ],
  "baseline_key": "apify-daily-test",
  "limit": 5,
  "max_items": 20
}
```

# Actor output Schema

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

API URL for the default dataset items produced by this run.

# 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 = {
    "queries": [
        "classification-cpv=72000000"
    ],
    "baseline_key": "apify-daily-test",
    "limit": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("zinin/tender-alert").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 = {
    "queries": ["classification-cpv=72000000"],
    "baseline_key": "apify-daily-test",
    "limit": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("zinin/tender-alert").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "queries": [
    "classification-cpv=72000000"
  ],
  "baseline_key": "apify-daily-test",
  "limit": 5
}' |
apify call zinin/tender-alert --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=zinin/tender-alert",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/6Cd3RrzTJuGZKm9Ni/builds/WZOsKb4eYDyjCAfUD/openapi.json
