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

Find industrial equipment tenders across the EU with separate CPV and exact keyword evidence, source-verified lot deadlines and official links. $7 per 1,000 verified lots.

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

## Pricing

$7.00 / 1,000 verified unique ted notice / lot 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 TED Industrial Equipment Tender Keyword Matcher

**NEWAPI17 · Version 1.0.0 · Official EU TED Search API v3**

Discover industrial machinery and equipment procurement notices on TED, with **separate, source-backed evidence for CPV matches and original-language technical keyword matches**. Each eForms lot is matched to **its own tender deadline** from the original XML. No inferred deadline/lot combinations.

### What you get

- Official Publications Office TED Search API v3, not website scraping.
- CPV hierarchy matching with explicit lot-level or procedure-fallback provenance.
- Literal technical keyword matches with source scope, field, original language, matched text and excerpt.
- Keyword absence is marked **not observed in available original-language text**, never “unsuitable” or “non-compliant.” Cross-language translations may hide exact phrases.
- Deadline timestamps and UTC offsets preserved from each XML lot. Expired and timezone-unknown deadlines excluded by default.
- Unique per-notice/lot results, published buyer, title, official TED link and UTC observation time.
- JSON and CSV Dataset exports with incremental checkpoints, request and spending bounds.

### Example input

```json
{
  "cpvCodes": ["42000000", "43000000"],
  "keywords": ["CNC", "laser", "robot", "milling"],
  "countryCodes": ["DEU", "FRA"],
  "publicationDateFrom": "2026-10-01",
  "publicationDateTo": "2026-10-22",
  "deadlineFrom": "2026-10-22",
  "deadlineTo": "2026-12-31",
  "keywordMatchMode": "ANNOTATE",
  "maxResults": 10,
  "maxCostUsd": 0.07
}
```

All dates are YYYY-MM-DD. Empty publication dates mean the last 14 days, relative to the run date. Country codes are ISO 3166-1 alpha-3, e.g. DEU, FRA, ITA or ESP. CPV values accept 2–8 digits plus an optional check digit. A division code such as 42000000 matches hierarchical prefix 42\*, and 43000000 matches 43\*.

With keywordMatchMode ANNOTATE (default), retain CPV-matching tenders even if literal keyword evidence is absent. With REQUIRE, return only lots with directly observed keywords, but never assert the excluded results are unsuitable. Add synonyms from the relevant procurement languages.

includeExpired and includeUnknownDeadlines default to false. Without a complete trustworthy deadline and timezone, the Actor will not falsely mark a tender as open. Deadline date range filters operate on the original lot-local calendar date.

The valid prefilled input caps results at 10 and result charges at $0.07, with a maximum 65 notices scanned, 5 search pages, 100 HTTP requests, and 240 seconds run time. Increase these limits carefully. maxResults supports 1–2,000; maxCostUsd supports 0–1,000. A $0 maximum returns no results without querying TED.

### Output fields

Required fields:

- publication_number, lot_id, country_code, buyer_name, title_text
- matched_keywords and match_evidence (independent CPV and KEYWORD evidence objects)
- cpv_codes and cpv_match_scope
- submission_deadline and deadline_timezone
- ted_notice_url, observed_at

Additional fields include title_language, keyword_match_status, cpv_match, deadline_precision, deadline_source_date, deadline_source_time, source and estimated_value.

For every lot, submission_deadline combines original eForms TenderSubmissionDeadlinePeriod EndDate and EndTime; deadline_timezone is the original Z or numeric UTC offset, not an invented IANA timezone. observed_at is the UTC ingestion time. A source-reported date without a precise time remains date-only if includeUnknownDeadlines is enabled.

The optional estimated_value retains the eForms lot's **EstimatedOverallContractAmount** as its original string, with the source field type and source currencyID, without currency conversion. It is not an award price or guaranteed budget. Missing source fields are null.

Example CPV evidence object:

```json
{"type":"CPV","source_scope":"lot","field":"ItemClassificationCode","matched_code":"42623000","input_prefix":"42","method":"hierarchy_prefix"}
```

Example keyword evidence object:

```json
{"type":"KEYWORD","keyword":"CNC","matched_text":"CNC","source_scope":"lot","field":"title","language":"deu","excerpt":"CNC Bearbeitungszentrum","method":"case_insensitive_literal_phrase"}
```

The keyword status not_observed_in_available_original_text means only that a literal phrase was not found in the fetched source text. It is not disqualifying evidence.

### Pricing

**$0.007 per unique output lot record**, billed through Apify Pay-per-event with only the synthetic event **apify-default-dataset-item** enabled. Do not also enable an Actor-start event or a second custom row charge. 1,000 rows correspond to **$7.00 gross** before optional user discounts. The publisher's net income is lower after commission and platform costs.

The user-defined maxCostUsd imposes a local cap on the number of emitted records: floor(maxCostUsd / 0.007). The platform's maximum run charge limit is independently enforced and should also be set in Apify Console.

### Source, lot and deadline fidelity

The Search API response is **notice based**. Lot identifiers, CPVs and deadline arrays cannot be zipped by position. Instead, search returns candidate notices; the Actor then uses the official linked eForms XML and reads each ProcurementProjectLot's own ProcurementProject, TenderingProcess and TenderSubmissionDeadlinePeriod.

Lot-level CPVs take precedence. If a lot supplies no CPV, a procedure-level match is labelled procedure_fallback; it is never passed off as lot-specific evidence.

Only one TED request is made at a time, voluntarily spaced at least 1.1 seconds apart. Source responses 429 and transient 5xx are retried with bounded exponential backoff and Retry-After. Other HTTP failures are counted or propagated, as appropriate; request limits and timeouts prevent runaway costs.

The official Search API supports ITERATION scroll pagination with 250 notices maximum per page and 10,000 fields per page. Unlike normal pagination (15,000 notices per query), iteration avoids that total cap and reduces source-index changes during paging. The scroll token expires after the next OJ S release plus 24 hours; failed runs can resume only while the checkpoint remains valid.

A per-run key-value checkpoint stores the iteration token, processed lot keys and counters after each successfully persisted result. The key-value SUMMARY record reports counts, skipped/expired items, source requests, runtime and estimated result charges. New runs have independent datasets and caches. A source correction/new edition can receive a different publication number; always validate directly before bidding.

### Official RDF fallback

In Apify cloud environments TED may return empty HTTP 202 instead of the official XML notice. In that case the Actor queries the exact publication-number named graph from the **official TED Open Data SPARQL service** (https://docs.ted.europa.eu/ODS/latest/connecting/sparql.html) at the Publications Office. It follows each typed Lot, Purpose and SubmissionTerm relationship, retaining the lot-specific CPV and the exact hasReceiptTenderDeadline. Participation and expression-of-interest deadlines are never substituted for tender deadlines. No third-party scraper or proxy is used.

TED Open Data graphs may be behind the latest TED publications. If a graph has not yet been ingested, the corresponding notice is omitted, not fabricated. The output source property identifies whether the result used the original eForms XML or the official TED Open Data RDF. The latter uses the source type epo:hasEstimatedValue for amounts if present, rather than claiming to expose the original XML field. No currency conversion is performed. Because of RDF ingestion lag, this Actor cannot guarantee complete coverage of the newest notices.

### Download JSON or CSV

In Apify Console, open the run, select Dataset and choose Export > JSON or CSV. The Actor output tab contains direct Dataset JSON/CSV download links plus the SUMMARY key-value record. Nested match_evidence is serialized as JSON inside a CSV cell.

### Source attribution and reuse

**Source: Tenders Electronic Daily (TED), Publications Office of the European Union**, https://ted.europa.eu.

[Official TED API documentation](https://docs.ted.europa.eu/api/latest/search.html) · [TED Search field list](https://docs.ted.europa.eu/ODS/latest/reuse/field-list.html) · [TED pagination limits](https://docs.ted.europa.eu/ODS/latest/reuse/search-api.html) · [TED legal notice](https://ted.europa.eu/en/legal-notice) · [Apify PPE](https://docs.apify.com/actors/publishing/monetize/pay-per-event)

TED notices can generally be reused for commercial or non-commercial purposes with applicable source attribution and third-party rights. Editorial site content is CC BY 4.0, and official system metadata is CC0 under TED's legal notice. This independently developed Actor is not affiliated with or endorsed by the European Union or TED; it does not use official EU logos.

### Limitations

The Actor does not translate source content, download buyers' attached tender specifications, submit a bid or certify a procurement opportunity. CPV categories may be broad or imperfectly classified. Missing dates and keywords may reflect source data gaps. Always check the original official TED notice and the buyer's portal.

# Actor input Schema

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

2–8 digits. Trailing zeros on an 8-digit code denote the CPV hierarchy (e.g. 42000000 matches 42\*).

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

Literal case-insensitive original-language phrases. Absence never proves a tender is unsuitable.

## `countryCodes` (type: `array`):

Configure buyer countries (iso alpha-3). This parameter bounds official TED data retrieval and output.

## `publicationDateFrom` (type: `string`):

YYYY-MM-DD. If blank: current date minus 14 days.

## `publicationDateTo` (type: `string`):

YYYY-MM-DD. If blank: current date.

## `deadlineFrom` (type: `string`):

YYYY-MM-DD (inclusive, lot-local calendar date).

## `deadlineTo` (type: `string`):

YYYY-MM-DD (inclusive, lot-local calendar date).

## `keywordMatchMode` (type: `string`):

ANNOTATE retains CPV matches even without original-language keyword hits; REQUIRE filters them without asserting non-compliance.

## `includeExpired` (type: `boolean`):

Normally exclude deadlines earlier than the observation time.

## `includeUnknownDeadlines` (type: `boolean`):

Normally exclude null, missing-timezone and date-only deadlines.

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

Configure max unique lot records. This parameter bounds official TED data retrieval and output.

## `maxCostUsd` (type: `number`):

Hard record cap: floor(maxCostUsd / $0.007). Platform run spending limit applies independently.

## `maxNoticesScanned` (type: `integer`):

Configure maximum notices to inspect. This parameter bounds official TED data retrieval and output.

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

Configure maximum ted pages. This parameter bounds official TED data retrieval and output.

## `maxApiRequests` (type: `integer`):

Configure maximum ted http requests. This parameter bounds official TED data retrieval and output.

## `timeBudgetSeconds` (type: `integer`):

Configure time budget, seconds. This parameter bounds official TED data retrieval and output.

## Actor input object example

```json
{
  "cpvCodes": [
    "42000000",
    "43000000"
  ],
  "keywords": [
    "CNC",
    "laser",
    "robot",
    "pump",
    "milling"
  ],
  "countryCodes": [
    "DEU",
    "FRA"
  ],
  "keywordMatchMode": "ANNOTATE",
  "includeExpired": false,
  "includeUnknownDeadlines": false,
  "maxResults": 10,
  "maxCostUsd": 0.07,
  "maxNoticesScanned": 65,
  "maxPages": 5,
  "maxApiRequests": 100,
  "timeBudgetSeconds": 240
}
```

# Actor output Schema

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

No description

## `csv` (type: `string`):

No description

## `summary` (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/newapi17-eu-industrial-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/newapi17-eu-industrial-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/newapi17-eu-industrial-tender-matcher --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,afkdigital/newapi17-eu-industrial-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/iLKIaev0PiMq3LMQv/builds/Q1ZkathBnXjoaIiUh/openapi.json
