# EU Tenders Search & TED Notice Monitor (`miacx/eu-public-tenders-ted-search-monitor`) Actor

Search EU procurement notices through the official TED API. Schedule saved searches to return newly seen notices. No TED API key needed.

- **URL**: https://apify.com/miacx/eu-public-tenders-ted-search-monitor.md
- **Developed by:** [mia.cx](https://apify.com/miacx) (community)
- **Stats:** 2 total users, 1 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 Search & TED Notice Monitor

Find procurement opportunities without parsing TED notice pages.
Search the official TED API and receive structured notices, or schedule a saved search to return newly seen notices.
Each Notice costs $0.0009, plus $0.00005 per run start at default memory.

### Try it

```json
{"keywords":["software"],"monitor":true,"maxNotices":10}
```

Save this input as an Apify task and add a schedule to monitor it. No TED API key is needed.
An empty input `{}` searches software notices from seven days ago through today, using UTC dates, without saving monitor progress.
It returns at most 10 notices. The example enables monitoring with the same initial window.

### Use from an agent

Add `https://mcp.apify.com?tools=miacx/eu-public-tenders-ted-search-monitor` to your MCP client and sign in to Apify.
Call the dedicated Actor tool, then use `get-dataset-items` to read the completed run's dataset.
Or set `APIFY_TOKEN` to your Apify API token and call the API for JSON rows:

```sh
curl --fail-with-body 'https://api.apify.com/v2/actors/miacx~eu-public-tenders-ted-search-monitor/run-sync-get-dataset-items' \
  -H "Authorization: Bearer $APIFY_TOKEN" -H 'Content-Type: application/json' \
  -d '{"keywords":["software"],"monitor":true,"maxNotices":10}'
```

### What you get

Read `status` first. An `ok` row contains one notice; `no_results` means no matching or unseen notices.
Notice IDs are deduplicated within each run. Monitor runs also suppress notices recorded in saved progress.
Rows contain notice metadata and links; document files are not downloaded.

Selected fields from the recorded TED fixture, replayed with an October 2026 publication window:

```json
{
  "status": "ok",
  "noticeId": "675397-2026",
  "noticeType": "cn-standard",
  "buyerName": "Urząd Komisji Nadzoru Finansowego",
  "buyerCountry": "POL",
  "cpvCodes": ["72000000", "72611000", "72260000"],
  "estimatedValue": null,
  "currency": null,
  "submissionDeadline": "2026-10-09",
  "publicationDate": "2026-10-01",
  "procedureType": "open",
  "tedUrl": "https://ted.europa.eu/en/notice/-/detail/675397-2026",
  "documentUrl": "https://www.ezamowienia.gov.pl",
  "reason": null
}
```

Every row includes `input`, `source` and `fetchedAt`. `input` is the TED query, or submitted JSON before query construction.
`source` links to the notice, or the search API for failed and empty results.
`fetchedAt` marks this run's search start, including cache hits.

Titles and buyer names prefer English, then the first available language in sorted key order. No translation runs.
Buyer names join with semicolons; `buyerCountry` is the first country. `titleLanguage` identifies the selected title's language.
CPV codes retain source order with duplicates removed. Missing scalar fields are null; missing CPV codes are `[]`.
`estimatedValue` is a procedure estimate in `currency`, not an award amount.
`submissionDeadline` is the earliest lot submission date, without time of day or timezone.
`documentUrl` is the first supplied document link. Failed rows have null notice fields and an explanatory `reason`.

### Input

| Field | Default | Accepted input |
| --- | --- | --- |
| `keywords` | `["software"]` | Up to 50 phrases, each 1–150 characters. `[]` disables text filtering. |
| `cpvCodes` | `[]` | Up to 50 exact eight-digit CPV codes. Add child codes explicitly. |
| `buyerCountries` | `[]` | Up to 50 uppercase ISO alpha-3 codes, such as `NLD` or `FRA`. |
| `noticeTypes` | `[]` | Up to 50 entries using `cn-standard`, `can-standard` or `pin-only`. Empty includes all TED types. |
| `publishedFrom` | `""` | Inclusive `YYYY-MM-DD`; empty starts seven days ago in UTC. Saved monitor progress takes precedence. |
| `publishedTo` | `""` | Inclusive `YYYY-MM-DD`; empty uses today's UTC date. Leave empty for ongoing monitoring. |
| `monitor` | `false` | Save progress and return unseen notices. Scheduling is a separate Apify task setting. |
| `maxNotices` | `10` | 1–1,000 successful notices per run, subject to budget and runtime limits. |

Lists use OR within a field and AND between fields. Duplicate filter values are removed.
The explicit notice types mean contract notices, contract awards and prior information notices, respectively.
Unknown fields and invalid values produce `invalid_input`. The lower date must not exceed the upper date.

Monitor progress belongs to the running user, saved task and filter values.
Direct API runs use that user's direct-run store. Changing filters starts separate progress; changing `maxNotices` preserves it.
The first run returns the window's backlog. Subsequent runs continue capped backlogs and suppress seen notices.
Run each saved query sequentially; concurrent runs can return duplicates.

### Pricing

| Event | Price | Counted unit |
| --- | --- | --- |
| Notice | $0.0009 | One returned `ok` notice, including notices with missing optional fields. |
| Actor start | $0.00005 | One run start at the default 256 MB memory. |

Ten notices cost $0.00905 including startup at default memory.
Duplicates within the run and notices suppressed by saved monitor progress incur no additional Notice events.
Cached search results still count. Error and `no_results` rows incur no Notice events.
The $0.00005 per run start at default memory applies even with no billable notices.
Apify counts one startup event per GB of memory, with a minimum of one event.

### Limits and data freshness

- Each run reads at most eight 250-notice pages within a 24-second core deadline.
- Ordinary search responses can be reused for 15 minutes. Monitor searches bypass this cache.
- Monitor scroll snapshots expire; after 23 hours, the Actor restarts from the saved inclusive publication date and IDs.
- Interrupted runs or failed progress writes can redeliver notices. Deduplicate downstream by `noticeId`.
- Backdated notices inserted before saved progress require an ordinary search. Monitoring does not detect them retroactively.

### If a row fails

| Status | Next step |
| --- | --- |
| `no_results` | Broaden an ordinary search, or wait for new matching notices before rerunning a monitor. |
| `invalid_input` | Check field names, list values and dates. |
| `invalid_notice`, `invalid_response` | Inspect `reason`; TED omitted required notice data or returned an unexpected response. |
| `network_error`, `rate_limited`, `upstream_error`, `upstream_timeout` | Retry later; follow any wait described in `reason`. |
| `budget_exhausted` | Raise the spending limit. A monitor can resume from saved progress. |
| `run_limit` | Rerun the monitor to continue, or narrow an ordinary search. |
| `storage_error` | Retry and deduplicate notice IDs; progress could not be saved. |
| `internal_error` | Keep the run ID and `reason` when reporting the failure. |

Successful notices remain billable if another row fails in the same run.

# Actor input Schema

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

Up to 50 full-text phrases, each 1–150 characters. Default \["software"]; \[] removes text filtering. Duplicates are removed. Phrases use OR; different filters use AND.

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

Up to 50 exact eight-digit CPV codes, such as "72000000". Default \[] applies no CPV filter. Codes use OR; other filters use AND. Add child codes explicitly; descendants are not included automatically.

## `buyerCountries` (type: `array`):

Up to 50 uppercase ISO alpha-3 buyer country codes, such as "NLD" or "FRA". Default \[] includes all countries. Countries use OR; other filters use AND.

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

Up to 50 entries using cn-standard (contract notice), can-standard (contract award) or pin-only (prior information). Default \[] includes all TED notice types. Values use OR; other filters use AND.

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

Inclusive publication date, YYYY-MM-DD. Omitted or empty starts seven days before the run in UTC. Existing monitor progress overrides this lower bound. Must not exceed publishedTo; changing this value creates a separate saved query.

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

Inclusive publication date, YYYY-MM-DD. Omitted or empty uses the current UTC date. Leave empty for ongoing monitoring. Must not precede publishedFrom; changing this value creates a separate saved query.

## `monitor` (type: `boolean`):

Boolean; default false searches the date window each run. True returns unseen notices and saves progress for this user, task and filter set. First run returns the backlog. Schedule the task separately and run it sequentially. Direct runs share a user-scoped store. Changing maxNotices preserves progress.

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

Integer from 1 to 1,000; default 10. Caps successful Notice rows per run, subject to budget and runtime limits. Each costs $0.0009; 10 cost $0.009 plus $0.00005 per run start at default memory. Monitor runs resume capped backlogs. This field does not change saved-query identity.

## Actor input object example

```json
{
  "keywords": [
    "software"
  ],
  "cpvCodes": [],
  "buyerCountries": [],
  "noticeTypes": [],
  "publishedFrom": "",
  "publishedTo": "",
  "monitor": false,
  "maxNotices": 10
}
```

# Actor output Schema

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

One row per item, each with a status field.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("miacx/eu-public-tenders-ted-search-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("miacx/eu-public-tenders-ted-search-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 '{}' |
apify call miacx/eu-public-tenders-ted-search-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,miacx/eu-public-tenders-ted-search-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/Azq5FmtD6aNaT94SN/builds/0LAWOm7pQlqsArUKP/openapi.json
