# EU Tender Change Intelligence (`atlas-data/eu-tender-change-intelligence`) Actor

Detect and track TED deadline, cancellation, procurement-document, criteria, and other eForms notice changes.

- **URL**: https://apify.com/atlas-data/eu-tender-change-intelligence.md
- **Developed by:** [Atlas](https://apify.com/atlas-data) (community)
- **Categories:** Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-usage

## 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

## EU Tender Change Intelligence

Turn official TED eForms change notices into a durable feed of deadline changes, cancellations,
procurement-document updates, criteria changes, and other amendments.

### Who it is for

The ideal customer is a bid team, supplier, capture manager, proposal consultancy, or procurement
intelligence provider that already tracks opportunities on TED and loses time or revenue when an
amendment is noticed late. The Actor is especially useful for teams monitoring many countries or
CPV families where manual re-checking does not scale.

### Why teams pay

A missed extension can waste a bid that could have been completed. A shortened deadline can remove
the chance to bid entirely. A changed document or criterion can invalidate work already done.
This Actor replaces repeated manual TED checks with normalized, explainable, replay-safe records
that can feed alerts, CRM workflows, data warehouses, and client-facing monitoring products.

### What it detects

TED eForms change notices retain the form type and notice type of the notice being changed. They are
not queried as `form-type=change`. This Actor scans a bounded publication window and identifies a
change only when official field BT-758 (`change-notice-version-identifier`) is non-empty.

It requests the official publication, notice, buyer, CPV, change, document, reason, and tender
deadline fields. TED Search API arrays are flattened. The Actor therefore preserves values as
sorted notice-level sets and never joins buyers, lots, dates, times, sections, or descriptions by
array position.

### Output

Every dataset row uses schema version `1.0.0` and includes:

- stable `changeId`, derived only from the current publication number and referenced prior notice
  identifier set;
- separate `contentDigest` for mutable normalized content;
- publication, title, buyer, country, CPV, form, and notice type;
- preserved BT-758 references, including classified TED publication numbers and opaque version IDs;
- reason codes/descriptions, change descriptions, changed section identifiers, and document-change
  status/dates;
- separate notice-level tender deadline date and time sets;
- relevance score and signals, tracking state, changed fields, attribution, and explicit association
  warnings.

`full` emits snapshot rows. `incremental` emits only new or updated matched changes. `diff` also
emits unchanged matched changes. `maxItems` counts matched change notices, never raw scanned rows.

### Input example

```json
{
  "mode": "incremental",
  "keywords": ["cybersecurity", "SOC"],
  "reasonCodes": ["cor-buy"],
  "buyerCountries": ["DEU", "CZE"],
  "cpvPrefixes": ["72"],
  "publicationFrom": "2026-07-01",
  "publicationTo": "2026-07-27",
  "deadlineFrom": "2026-08-01",
  "includeDocumentChangesOnly": false,
  "language": "eng",
  "maxItems": 500
}
```

All user-controlled strings are normalized, validated, and filtered locally. The TED expert-search
query contains only validated publication dates.

### Reliability model

State is isolated by the canonical filter configuration. Each partial run stores the exact query
window, inbound iteration token, row offset, and a source conflict ledger. Expired tokens replay
from the window start under independent page and row ceilings. Identical source rows are skipped;
a repeated identity with different requested-field content fails closed.

Forward pages and rows have independent caps. TED timeout envelopes, malformed payloads, oversized
pages/tokens, repeated tokens, invalid stored state, state-capacity conflicts, and lease loss all
fail closed. Transient network, rate-limit, and server errors use bounded retries.

Durable state uses immutable snapshots in named KVS
`eu-tender-change-intelligence-state-v1`, serialized by a Request Queue lease in
`eu-tender-change-intelligence-lock-v1`. State admission reserves worst-case progress capacity
before any dataset write. Saved state is capped at 3.5 MB. Writes occur in the order dataset rows,
default-store `OUTPUT`, then durable state, so a state-write failure leaves output replayable.

### Costs and operation

The Actor defaults to 1024 MB memory and uses bounded, sequential TED requests. Cost is driven by
the publication window and safety caps rather than the number of matches. Start with a narrow date
window and use incremental mode for scheduled monitoring. No API key or other secret is required.

Attribution in each record identifies TED, the API endpoint, field reference, and reuse notice.
Consumers remain responsible for verifying bid-critical details against the linked official notice.

### Local development

Requires Node.js 22 or later.

```bash
npm ci
npm run lint
npm run typecheck
npm run build
npm test
npm run smoke:local
npm run smoke:live
npx apify validate-schema .actor/input_schema.json
```

The live smoke test is opt-in through the script, scans bounded recent pages, requires at least one
real BT-758 row, normalizes it, and validates it against the dataset schema.

# Actor input Schema

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

Full emits snapshots; incremental emits new/updated changes; diff also emits unchanged changes.

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

OR match against normalized title, buyer, change description, and reason description.

## `reasonCodes` (type: `array`):

Exact official change-reason-code values, matched locally.

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

TED/ISO alpha-3 country codes.

## `cpvPrefixes` (type: `array`):

Two to eight digits; 72 matches the IT family.

## `publicationFrom` (type: `string`):

Inclusive YYYY-MM-DD; defaults to 30 days before publicationTo.

## `publicationTo` (type: `string`):

Inclusive YYYY-MM-DD; defaults to today.

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

Keep a notice when at least one flattened deadline date satisfies both bounds.

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

Inclusive bound applied to the same individual deadline date as deadlineFrom.

## `includeDocumentChangesOnly` (type: `boolean`):

Require change-procurement-documents to explicitly contain true.

## `requireChangedSections` (type: `boolean`):

Require at least one change-previous-notice-section-identifier.

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

Falls back to English, then the first available language.

## `maxItems` (type: `integer`):

Matched BT-758 change notices, not raw TED rows.

## `relevance` (type: `object`):

Optional explainable positive and negative rules over normalized fields.

## `pageSize` (type: `integer`):

Advanced. Official maximum is 250.

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

Expired-token duplicate replay has an independent hard ceiling.

## `maxScannedItems` (type: `integer`):

Bounds all newly handled rows, including non-change notices.

## `overlapDays` (type: `integer`):

Recheck recent publications for revised source rows.

## `stateRetentionDays` (type: `integer`):

Forget completed content digests older than this.

## `maxStateEntries` (type: `integer`):

Retain the most recently seen change digests.

## Actor input object example

```json
{
  "mode": "incremental",
  "keywords": [],
  "reasonCodes": [],
  "buyerCountries": [],
  "cpvPrefixes": [],
  "includeDocumentChangesOnly": false,
  "requireChangedSections": false,
  "language": "eng",
  "maxItems": 1000,
  "relevance": {
    "include": [],
    "exclude": [],
    "minimumScore": 0,
    "requireIncludeMatch": false
  },
  "pageSize": 250,
  "maxPages": 100,
  "maxScannedItems": 10000,
  "overlapDays": 3,
  "stateRetentionDays": 180,
  "maxStateEntries": 5000
}
```

# Actor output Schema

## `dataset` (type: `string`):

Normalized official TED eForms change notices.

## `summary` (type: `string`):

Counts, partial-progress status, stop reason, query window, and attribution.

# 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("atlas-data/eu-tender-change-intelligence").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("atlas-data/eu-tender-change-intelligence").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 atlas-data/eu-tender-change-intelligence --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,atlas-data/eu-tender-change-intelligence"
        }
    }
}

```

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/IZ26aJHkD81dMPYMY/builds/VUmksJOTLeL8nu6T1/openapi.json
