# EU Industrial Equipment Tender Matcher (`afkdigital/eu-industrial-equipment-tender-matcher`) Actor

Find EU TED tenders for industrial pumps, generators and compressors with CPV filtering, verified source-text evidence, buyer names and bid deadlines. Official public TED API.

- **URL**: https://apify.com/afkdigital/eu-industrial-equipment-tender-matcher.md
- **Developed by:** [AFK Digital](https://apify.com/afkdigital) (community)
- **Categories:** Business, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $6.00 / 1,000 verified industrial tender matches

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 Industrial Equipment Tender Matcher — API22

![EU Industrial Equipment Tender Matcher logo](https://afkmesh.com/social/actor-assets/api22/icon.png)

**Match EU TED notices to real equipment needs, with quoted source evidence.** Focused on industrial pumps, generators, compressors, power and mechanical equipment. Built independently for `/opt/APIFY/API22` using **only TED's public, published-notice Search API**, not browser scraping or private endpoints.

### Why this Actor is different

General TED searches return broad tender lists. This Actor applies a second, verifiable qualification layer: the buyer's actual notice **title, lot title or procurement description must contain a target equipment term**, an optional user-selected CPV family must match the same notice, and default industrial-context checks exclude medical/incidental keyword hits. Every output includes the evidence wording, source field, language, CPV signal, TED link, and conservative bidding status. CPV alone or a broad TED full-text hit never creates a billable record.

#### Input

- `productTerms`: 1–8 equipment words/phrases; use language-specific synonyms for better recall, e.g. `["pump", "pompe", "Pumpe"]`.
- `cpvCodes`: optional list of 8-digit CPV codes such as `42120000` (pump-family), `31120000` (generators), `42123000` (compressor family). Supplying codes requires *both* text evidence and matching CPV.
- `country`: optional buyer-country ISO alpha-3 (`ROU`, `DEU`, `FRA`).
- `language`: preferred **evidence** language ISO-639-3 (default `eng`). Search itself is multilingual and preferred language cannot guarantee translated procurement descriptions.
- `publishedFrom` / `publishedTo`: YYYY-MM-DD inclusive. Default: previous 30 days.
- `procurementNature`: `supplies` (default equipment purchasing), `services`, `works` or `all`. The default excludes service-only notices.
- `openClosed`: `open` (default), `closed`, `all`. "Open" requires a competition notice with a published bid deadline after today's UTC date. Results/awards are closed. Missing/today deadlines are `unknown` and excluded from "open" results.
- `industrialOnly`: default true; reject medical/nonindustrial incidental matches.
- `includeHistoricalVersions`: default false, asks TED itself for only latest versions. When true, historical/latest labels are **relative to scanned results**, not a promise to retrieve complete history.
- `maxResults`: 1–200 qualified outputs (default 25); `maxPages`: 1–30 (default 8). Page size is 60 to remain well inside TED's 250 rows/page and 10,000 fields/page rules.

Use `examples/input-open.json` and `examples/input-all.json` as starting points.

#### Output (default dataset)

One row per qualifying notice/version with:

`tedNoticeId`, `noticeIdentifier`, `noticeVersion`, `versionState`, `noticeType`, `noticeForm`, `status`, `statusBasis`, `buyerName`, `title`, `description`, `publishedAt`, `country`, `deadline`, `deadlines`, `estimatedValue`, `currency`, `estimateLevel`, `sourceNoticeUrl`, `sourceDocumentUrls`, `matchedTerms`, `matchedCpvCodes`, `noticeCpvCodes`, `evidence.text`, `evidence.sourceField`, `evidence.language`, `evidence.matchedTerm`, `matchSignals`.

- `evidence` is from TED's published **notice/lot procurement text**, not a generated description or inference. The exact field and language are included.
- `estimatedValue` is shown only when the source exposes a procedure-level value, or **one unambiguous distinct lot-level value**. Different lot estimates are **not summed**. Missing fields remain null.
- `deadline` is the next source-declared bid-deadline **date** (or last published date for closed items); `deadlines` contains all distinct retrieved dates. Lot/time-zone detail is not merged into a falsely precise timestamp. Check the actual source before bidding.
- `status` is a conservative classification from form type and date, not a legal eligibility determination. TED itself describes "ACTIVE" as including recent planning and results notices.
- `sourceDocumentUrls` only contains URLs supplied by TED; no fabricated document URLs.
- `RUN-SUMMARY` and `ERRORS` key-value records show scanned/qualified/duplicate counts, scan caps, retry attempts and TED retrieval failures.

#### Transparent Pay Per Event pricing

This Actor is published on Apify Store under the **Pay Per Event** model. Its configured price is **$0.006 per evidence-qualified, nonduplicate dataset item** (qualified-match), plus an automatic **$0.0005 Actor-start event** for a 256 MB run. At this default setting, 100 qualified matches cost $0.6005 before any other possible platform charges. Only verified results actually delivered to the dataset trigger the qualified-match event. Nonmatches, duplicates, invalid queries and error reports **never trigger that result event**. There are no per-page or per-request charging events in the Actor.

Apify Cloud billing verification: a run with two verified matches charged **2 qualified-match events + 1 startup**; a run with zero verified matches charged **0 qualified-match events + 1 startup**. User spending limits are honored by the official Apify SDK. Results do not guarantee tender eligibility, technical compatibility or winning probability.

#### Data source, API rules and safeguards

- Official endpoint: `POST https://api.ted.europa.eu/v3/notices/search` (anonymous published notices; no API key).
- Common pump/generator/compressor terms receive CPV query hints when explicit codes are omitted. Explicit CPVs require text AND CPV. This boosts precision but may miss unusual classifications.
- All queries use TED Expert Search with `FT~"..."`, `classification-cpv`, `buyer-country`, publication dates as `YYYYMMDD`, and newest publication-date sort. String input is validated to prevent expert-query injection.
- Official v3 OpenAPI: `https://api.ted.europa.eu/api-v3.yaml`. Setting `checkQuerySyntax:true` **only validates** and does not execute a search. The Actor sets it false. It uses `onlyLatestVersions` and PAGE_NUMBER pagination.
- 60 notices/page, at most 30 pages/1,800 notice candidates/run; serial fetching and a **minimum 1.1s pause** between pages. 429/5xx and transient networking issues get bounded retries with Retry-After (up to 30 seconds); 400/401/403 are not bypassed.
- Official limits: <=250 notices/page, <=10,000 fields/page, <=15,000 notices per paged search; iteration mode exists but is not required for this focused Actor.
- No login, customer credentials, private TED endpoints, proxy rotation or CAPTCHA bypass. No hidden portal scraping.
- Only publicly published procurement-notice data is processed. Do not add contact scraping or personal profiling.
- Tender suitability, technical specification compatibility and winning probability **are not guaranteed**. Buyer deadlines/values can change; always validate with the original TED notice and procurement documents.
- TED and European Union are source/organisational references only; this independent tool is **not officially endorsed**.

#### Local development and verification

```bash
cd /opt/APIFY/API22
npm ci
npm test
npm run smoke
npm run check
```

To test end-to-end using local Apify storage:

```bash
mkdir -p storage/key_value_stores/default
cp examples/input-all.json storage/key_value_stores/default/INPUT.json
npm start
```

The publisher tested genuine TED records and verified the live pricing behavior before public release on **13 October 2026**. Test artifacts are retained privately by the publisher; customers only receive qualified dataset records and run diagnostics.

#### References (official, checked 13 October 2026)

- TED Search API: https://docs.ted.europa.eu/api/latest/search.html
- TED Search paging and field limits: https://docs.ted.europa.eu/ODS/latest/reuse/search-api.html
- TED field dictionary: https://docs.ted.europa.eu/ODS/latest/reuse/field-list.html
- TED "active" and version-scope explanation: https://ted.europa.eu/en/help/search-browse
- Apify Pay Per Event: https://docs.apify.com/actors/publishing/monetize/pay-per-event
- Apify publishing: https://docs.apify.com/actors/publishing/monetize

**This code is original for API22. No other API Actor folders are edited.**

# Actor input Schema

## `productTerms` (type: `array`):

1–8 specific product phrases (e.g. industrial pump, compressor, generator). Match must appear in a TED notice title or procurement description; language-specific terms improve recall.

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

8-digit CPV codes, optional. If supplied, a notice must contain one of these CPV families AND a target text phrase.

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

Optional buyer country as ISO 3166 alpha-3, such as DEU, FRA, POL, ROU; omit for any country.

## `language` (type: `string`):

ISO 639-3, such as eng, deu, fra, ron, pol. Searches all languages and prioritizes this one for extracted evidence.

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

YYYY-MM-DD. If omitted, the previous 30 days are searched.

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

Optional YYYY-MM-DD inclusive.

## `openClosed` (type: `string`):

Open requires a competition notice with a source-published future bid deadline. Unknown is not treated as open.

## `industrialOnly` (type: `boolean`):

Exclude medical and other incidental keyword matches lacking industrial CPV families or clear industrial context.

## `includeHistoricalVersions` (type: `boolean`):

Off = ask TED for only latest versions. On = show and distinguish versions found in this query; historical completeness is not guaranteed by date-filtered searches.

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

Only evidence-qualified results count. Scan limits may result in fewer matches.

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

Each page is a single official TED Search API request. Rate limits and a hard scan cap apply.

## `procurementNature` (type: `string`):

Default supplies: prioritizes equipment purchases, excluding service-only tenders. Select all to include related services and works.

## Actor input object example

```json
{
  "productTerms": [
    "industrial pump",
    "generator",
    "compressor"
  ],
  "cpvCodes": [],
  "language": "eng",
  "openClosed": "open",
  "industrialOnly": true,
  "includeHistoricalVersions": false,
  "maxResults": 25,
  "maxPages": 8,
  "procurementNature": "supplies"
}
```

# Actor output Schema

## `matches` (type: `string`):

No description

## `runSummary` (type: `string`):

No description

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

// Run the Actor and wait for it to finish
const run = await client.actor("afkdigital/eu-industrial-equipment-tender-matcher").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("afkdigital/eu-industrial-equipment-tender-matcher").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 afkdigital/eu-industrial-equipment-tender-matcher --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,afkdigital/eu-industrial-equipment-tender-matcher"
        }
    }
}
```

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/3sdPGtzo5MQ4scJtz/builds/NdOcvrSG4LPvQ5WKz/openapi.json
