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

Monitor one stable EU TED query. Receive a paid first-run baseline, then only newly observed procurement notices after complete checks. Each row includes evidence, confidence, gaps, review action and billing truth. Partial, failed, quiet and withheld outcomes are free and never advance the watch.

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

## Pricing

from $8.50 / 1,000 delivered tender baseline or new notices

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

## Tender Alert — Official EU TED Opportunity Watch

Turn a stable keyword or CPV watch into a review-ready stream of newly observed EU procurement notices, with complete-source gating, primary evidence, confidence, explicit gaps, billing truth, and a manual bid-review action.
**Built for:** Small businesses, bid teams, procurement consultants, agencies, software vendors, construction suppliers, export teams, and data teams that need a narrow repeatable EU opportunity watch without re-reading the same TED notices every day.
**Commercial unit:** one delivered baseline or newly observed tender row. **Live pricing contract:** $0.01 per delivered `result-found` row plus the configured start event (starting at $0.005). The Apify pricing panel is authoritative for the buyer's current tier. Invalid input, source failure, incomplete snapshots, free watch-status rows, duplicates, budget-withheld rows, and ambiguous delivery are not newly delivered paid opportunities.

![Tender Alert — Official EU TED Opportunity Watch: query to review-ready opportunity evidence](https://raw.githubusercontent.com/TimmyZinin/apify-actor-assets/835cb06dd010a2eb2708dcb632a5b61384e2b11c/commercial115/tender-alert/readme-hero.webp)

### What this Actor sells

A procurement search and an opportunity monitor are not the same product. Search answers “what is visible now?” A monitor must also prove what it checked before, whether the current source window is comparable, which identities are genuinely new to the watch, what was withheld, and whether delivery and billing settled. Without those controls, a daily export can repeatedly sell the same notices or silently lose an opportunity when a page, budget, or source call fails.
Provide one or more TED expert queries or keywords, an optional ISO-3 buyer country, a stable watch name, a per-query result limit, and a cap on newly delivered notices per run.
The Actor queries the official keyless TED Search API directly. It rejects malformed query outcomes, requires every requested query to complete before advancing state, reads the buyer-scoped watch baseline, and compares stable publication identities. The first complete run creates a baseline. Later complete runs deliver only publication identities absent from that baseline. A partial or failed snapshot creates a free advisory and never advances the baseline. If budget or `max_items` withholds a notice, that identity stays new for a future run.
The useful output is a defensible human-review queue. Open the primary TED notice, verify fit and deadline, qualify the opportunity with a human, and keep submission or no-bid decisions outside the Actor. The Actor never submits a bid, contacts a buyer, certifies eligibility, predicts an award, estimates profit, changes a CRM disposition, or converts missing evidence into a negative conclusion.

#### Buyer-visible outcomes

- One stable named watch per market thesis, geography, product, service, or CPV scope.
- An explicit first-run baseline instead of pretending every current notice is newly discovered.
- Only newly observed TED publication identities on later complete runs.
- Primary notice URLs and source coverage retained with the result.
- Evidence confidence separated from commercial attractiveness and award probability.
- Free partial/source advisories that never advance the stored baseline.
- Held-back identities preserved as new when `max_items` or run budget stops delivery.
- One KVS `OUTPUT` receipt reconciling request, source, Dataset, paid/free/withheld, failure, and replay state.
- A bounded manual next action with `safeToAutomate=false`.

### Good fit

- Watching one product, service, CPV family, geography, or buyer-country opportunity thesis repeatedly.
- Feeding a CRM or review queue with newly observed TED notices instead of full repeated search exports.
- Giving a small bid team a transparent baseline and a bounded daily or weekly opportunity delta.
- Keeping source completeness, evidence confidence, data gaps, action priority, and billing attached to every row.

### Not a fit

- Predicting contract awards, buyer intent, eligibility, win probability, margin, legal compliance, or the quality of a bid.
- Monitoring every possible TED notice with an unbounded crawl; the query and per-run limits define coverage.
- Submitting bids, downloading private tender-room documents, contacting buyers, or taking any external action.
- Treating a missing notice in a partial snapshot as deleted, cancelled, or irrelevant.
  Use a separate workflow for qualification, tender-room access, internal approvals, cost modelling, partner selection, bid writing, legal review, and submission. Keeping discovery evidence separate from business action makes the boundary reviewable and prevents a source lookup from becoming an unapproved external decision.

### Evidence-to-decision workflow

![Tender Alert — Official EU TED Opportunity Watch: complete-source watch workflow](https://raw.githubusercontent.com/TimmyZinin/apify-actor-assets/835cb06dd010a2eb2708dcb632a5b61384e2b11c/commercial115/tender-alert/readme-workflow.webp)

1. Define one narrow opportunity thesis as keywords or TED expert-query/CPV expressions.
2. Give that thesis a stable watch name so its state cannot collide with another market segment.
3. Run the Actor once to establish a complete bounded baseline; do not treat current notices as newly discovered on that first run.
4. Schedule later runs at a cadence appropriate to the market and deadline sensitivity.
5. Require every query to complete; route partial/source failures to retry without changing the baseline.
6. Compare stable TED publication identities with the last successfully stored watch baseline.
7. Deliver and charge only the explicit baseline or newly observed notice rows; leave held-back identities new.
8. Read KVS `OUTPUT` and reconcile Dataset, paid/free counts, withholding, source failures, and replay safety.
9. Open each primary notice and let a human qualify requirements, eligibility, fit, economics, deadline, and bid/no-bid action.

### Official source and reuse boundary

The runtime calls `https://api.ted.europa.eu/v3/notices/search`, an official European Union Search API intended for published-notice analysis and reuse and available without authentication. Notice metadata is retained with primary TED URLs. The Actor does not log into a buyer portal, submit a bid, access private documents, or use an unofficial mirror.
TED states that procurement notices can generally be reused for commercial or non-commercial purposes unless otherwise noted; editorial content is CC BY 4.0 and system metadata is CC0. Preserve attribution, indicate changes, do not reuse TED/SIMAP logos without consent, and review any third-party or identifiable-person content separately.
The Actor attributes the source in its public documentation and preserves primary notice URLs. It does not copy TED/SIMAP logos into its assets. Technical access and general reuse permission do not remove a buyer’s responsibility to review specific third-party material, personal data, confidentiality, retention, local procurement rules, and downstream use. Keep only fields needed for the opportunity-review purpose.

#### What source-complete means

`sourceComplete: true` means every requested query returned a response that the runtime could classify as either valid tender rows or an explicit clean no-match envelope. It does not mean the queries cover the entire procurement market. Coverage is still bounded by query syntax, country, per-query limit, the source index, publication timing, and the run schedule.
`sourceComplete: false` means at least one requested query failed, returned an error row, produced an empty or malformed envelope, or otherwise could not be classified. That run cannot prove a clean delta. It produces a free advisory, leaves the last complete baseline untouched, and tells automation whether the condition is retryable.

### State and baseline contract

Every `watch_name` owns an isolated stored baseline in the buyer’s Actor storage context. Reuse the name only when the query meaning remains the same. A different product, geography, CPV scope, buyer cohort, or material query change should use a different name; otherwise two incompatible opportunity theses share state and the delta loses meaning.
The first complete run stores the current bounded identities and delivers a `baseline` row. That row is useful: it proves initialization, records source coverage, and prevents the Actor from falsely marketing all current notices as newly published. Later complete runs compare publication identities. Existing identities are not re-sold. New identities become paid `new_tender` rows. This version does not claim to detect every amendment to an existing publication number.
Baseline movement follows delivery. If a paid push is confirmed, that identity can enter the baseline. If budget, `max_items`, source failure, storage error, or ambiguous delivery blocks a row, the Actor does not silently mark it as seen. A later compatible run can still deliver it. This ordering is the difference between a monitor and a lossy scheduled search.

### Input

Use **Try for free** on Apify Store or send the same JSON through API, Task, schedule, webhook, Make, n8n, Zapier, or a standard Apify MCP integration.

```json
{
  "queries": [
    "classification-cpv=72000000"
  ],
  "country": "DEU",
  "watch_name": "de-it-services",
  "limit": 20,
  "max_items": 20
}
```

#### Input fields

| Field | Buyer responsibility |
|---|---|
| `queries` | One to twenty keywords or TED expert-query expressions. Remove empty and duplicate expressions; keep the business meaning stable across a watch. |
| `country` | Optional ISO-3 buyer-country filter such as `DEU` or `FRA`; not a free-text location. |
| `watch_name` | Stable state namespace for exactly one intended query scope. Use a new name when that meaning changes. |
| `limit` | Per-query bounded result window, 1–100. A larger value expands the observed window but is never “all TED.” |
| `max_items` | Maximum new paid rows delivered in one run, 1–200. Additional new identities stay new for later delivery. |

#### Query design guide

1. Start with a business decision: which opportunities would a human actually review?
2. Prefer a narrow CPV or expert expression to a generic word that creates a noisy baseline.
3. Add a buyer country only when geographic delivery or eligibility makes it meaningful.
4. Choose a per-query limit large enough for the expected publication volume between runs.
5. Name the watch after the stable thesis, not after today’s date or one temporary campaign.
6. Run once, inspect the baseline and source coverage, then schedule the same bounded contract.
7. Version the watch name when query semantics materially change; do not splice incompatible history.

### Output stores

The default Dataset contains buyer-facing baseline, new-tender, watch-status, withheld, and failure advisory rows. The default Key-Value Store record `OUTPUT` is the run receipt. It records requested/unique/duplicate queries, attempted/successful/failed source work, detected identities, delivered/paid/free/withheld rows, source failures, partial state, budget stop, fatal error, replay safety, the watch name, and the Dataset URL.
Do not infer success from exit code or Dataset count alone. A quiet complete run can contain one free status row. A source failure can also contain one free row. An ambiguous paid delivery can require reconciliation. Read `OUTPUT` first, branch on terminal/failure state, then consume Dataset rows according to `recordType`.

#### Core fields

| Field | Meaning |
|---|---|
| `baselineKey` | Buyer-defined stable watch identity used for isolated state and downstream joins. |
| `publicationNumber` | Stable TED publication identity for a newly observed notice. |
| `title / buyer / buyerCountry` | Notice and contracting-body context returned by the official search response. |
| `cpv` | Published CPV evidence when returned; not an independently inferred market classification. |
| `deadline / publicationDate` | Published timing context that must be checked in the current primary notice. |
| `url` | Primary TED notice link for human verification. |
| `sourceStatus / sourceComplete` | Whether every requested query produced a classifiable snapshot. |
| `queriesRequested / Succeeded / Failed` | Run-level source-coverage counts copied to the row. |
| `recordType / schemaVersion` | Stable semantic row family and additive contract version. |
| `entityId / observedAt` | Stable downstream identity and Actor observation time. |
| `confidenceScore / confidenceBand` | Confidence in the evidence path, never win probability or commercial attractiveness. |
| `sourceEvidence / freshness` | Official review pointers and point-in-time observation metadata. |
| `change` | Whether a publication identity was first observed after the stored watch baseline. |
| `dataGaps / negativeSignals` | Coverage and interpretation limits that must remain visible downstream. |
| `recommendedAction / actionPriority` | Bounded human-review routing label, not an automatic bid decision. |
| `failureType / retryable / partial` | Machine-readable failure and handling truth. |
| `billing` | Whether this exact delivered row was linked to the paid result event. |
| `safeToAutomate` | Always false for bidding, outreach, eligibility, award, or legal action. |

### Field-by-field review guide

#### `baselineKey`

**Meaning:** Buyer-defined stable watch identity used for isolated state and downstream joins.
**Review rule:** Keep it with source status, observation time, watch identity and primary evidence. Confirm the value is type-correct and source-supported; never replace a missing value with a guess or CRM conclusion.
**Automation rule:** Use it for routing and evidence display, never to authorize outreach, eligibility, spend, bid submission, legal conclusions, or an award forecast.

#### `publicationNumber`

**Meaning:** Stable TED publication identity for a newly observed notice.
**Review rule:** Keep it with source status, observation time, watch identity and primary evidence. Confirm the value is type-correct and source-supported; never replace a missing value with a guess or CRM conclusion.
**Automation rule:** Use it for routing and evidence display, never to authorize outreach, eligibility, spend, bid submission, legal conclusions, or an award forecast.

#### `title / buyer / buyerCountry`

**Meaning:** Notice and contracting-body context returned by the official search response.
**Review rule:** Keep it with source status, observation time, watch identity and primary evidence. Confirm the value is type-correct and source-supported; never replace a missing value with a guess or CRM conclusion.
**Automation rule:** Use it for routing and evidence display, never to authorize outreach, eligibility, spend, bid submission, legal conclusions, or an award forecast.

#### `cpv`

**Meaning:** Published CPV evidence when returned; not an independently inferred market classification.
**Review rule:** Keep it with source status, observation time, watch identity and primary evidence. Confirm the value is type-correct and source-supported; never replace a missing value with a guess or CRM conclusion.
**Automation rule:** Use it for routing and evidence display, never to authorize outreach, eligibility, spend, bid submission, legal conclusions, or an award forecast.

#### `deadline / publicationDate`

**Meaning:** Published timing context that must be checked in the current primary notice.
**Review rule:** Keep it with source status, observation time, watch identity and primary evidence. Confirm the value is type-correct and source-supported; never replace a missing value with a guess or CRM conclusion.
**Automation rule:** Use it for routing and evidence display, never to authorize outreach, eligibility, spend, bid submission, legal conclusions, or an award forecast.

#### `url`

**Meaning:** Primary TED notice link for human verification.
**Review rule:** Keep it with source status, observation time, watch identity and primary evidence. Confirm the value is type-correct and source-supported; never replace a missing value with a guess or CRM conclusion.
**Automation rule:** Use it for routing and evidence display, never to authorize outreach, eligibility, spend, bid submission, legal conclusions, or an award forecast.

#### `sourceStatus / sourceComplete`

**Meaning:** Whether every requested query produced a classifiable snapshot.
**Review rule:** Keep it with source status, observation time, watch identity and primary evidence. Confirm the value is type-correct and source-supported; never replace a missing value with a guess or CRM conclusion.
**Automation rule:** Use it for routing and evidence display, never to authorize outreach, eligibility, spend, bid submission, legal conclusions, or an award forecast.

#### `queriesRequested / Succeeded / Failed`

**Meaning:** Run-level source-coverage counts copied to the row.
**Review rule:** Keep it with source status, observation time, watch identity and primary evidence. Confirm the value is type-correct and source-supported; never replace a missing value with a guess or CRM conclusion.
**Automation rule:** Use it for routing and evidence display, never to authorize outreach, eligibility, spend, bid submission, legal conclusions, or an award forecast.

#### `recordType / schemaVersion`

**Meaning:** Stable semantic row family and additive contract version.
**Review rule:** Keep it with source status, observation time, watch identity and primary evidence. Confirm the value is type-correct and source-supported; never replace a missing value with a guess or CRM conclusion.
**Automation rule:** Use it for routing and evidence display, never to authorize outreach, eligibility, spend, bid submission, legal conclusions, or an award forecast.

#### `entityId / observedAt`

**Meaning:** Stable downstream identity and Actor observation time.
**Review rule:** Keep it with source status, observation time, watch identity and primary evidence. Confirm the value is type-correct and source-supported; never replace a missing value with a guess or CRM conclusion.
**Automation rule:** Use it for routing and evidence display, never to authorize outreach, eligibility, spend, bid submission, legal conclusions, or an award forecast.

#### `confidenceScore / confidenceBand`

**Meaning:** Confidence in the evidence path, never win probability or commercial attractiveness.
**Review rule:** Keep it with source status, observation time, watch identity and primary evidence. Confirm the value is type-correct and source-supported; never replace a missing value with a guess or CRM conclusion.
**Automation rule:** Use it for routing and evidence display, never to authorize outreach, eligibility, spend, bid submission, legal conclusions, or an award forecast.

#### `sourceEvidence / freshness`

**Meaning:** Official review pointers and point-in-time observation metadata.
**Review rule:** Keep it with source status, observation time, watch identity and primary evidence. Confirm the value is type-correct and source-supported; never replace a missing value with a guess or CRM conclusion.
**Automation rule:** Use it for routing and evidence display, never to authorize outreach, eligibility, spend, bid submission, legal conclusions, or an award forecast.

#### `change`

**Meaning:** Whether a publication identity was first observed after the stored watch baseline.
**Review rule:** Keep it with source status, observation time, watch identity and primary evidence. Confirm the value is type-correct and source-supported; never replace a missing value with a guess or CRM conclusion.
**Automation rule:** Use it for routing and evidence display, never to authorize outreach, eligibility, spend, bid submission, legal conclusions, or an award forecast.

#### `dataGaps / negativeSignals`

**Meaning:** Coverage and interpretation limits that must remain visible downstream.
**Review rule:** Keep it with source status, observation time, watch identity and primary evidence. Confirm the value is type-correct and source-supported; never replace a missing value with a guess or CRM conclusion.
**Automation rule:** Use it for routing and evidence display, never to authorize outreach, eligibility, spend, bid submission, legal conclusions, or an award forecast.

#### `recommendedAction / actionPriority`

**Meaning:** Bounded human-review routing label, not an automatic bid decision.
**Review rule:** Keep it with source status, observation time, watch identity and primary evidence. Confirm the value is type-correct and source-supported; never replace a missing value with a guess or CRM conclusion.
**Automation rule:** Use it for routing and evidence display, never to authorize outreach, eligibility, spend, bid submission, legal conclusions, or an award forecast.

#### `failureType / retryable / partial`

**Meaning:** Machine-readable failure and handling truth.
**Review rule:** Keep it with source status, observation time, watch identity and primary evidence. Confirm the value is type-correct and source-supported; never replace a missing value with a guess or CRM conclusion.
**Automation rule:** Use it for routing and evidence display, never to authorize outreach, eligibility, spend, bid submission, legal conclusions, or an award forecast.

#### `billing`

**Meaning:** Whether this exact delivered row was linked to the paid result event.
**Review rule:** Keep it with source status, observation time, watch identity and primary evidence. Confirm the value is type-correct and source-supported; never replace a missing value with a guess or CRM conclusion.
**Automation rule:** Use it for routing and evidence display, never to authorize outreach, eligibility, spend, bid submission, legal conclusions, or an award forecast.

#### `safeToAutomate`

**Meaning:** Always false for bidding, outreach, eligibility, award, or legal action.
**Review rule:** Keep it with source status, observation time, watch identity and primary evidence. Confirm the value is type-correct and source-supported; never replace a missing value with a guess or CRM conclusion.
**Automation rule:** Use it for routing and evidence display, never to authorize outreach, eligibility, spend, bid submission, legal conclusions, or an award forecast.

### Pricing, delivery, and budget truth

This Actor uses pay per event: $0.01 per delivered `result-found` row plus the configured start event (starting at $0.005). A result event represents one actually delivered baseline or newly observed tender row, not one query, one source request, one page, one failed attempt, or one row the Actor merely found internally.

1. Apify charges the configured start event according to allocated memory and the buyer’s tier.
2. The Actor checks that the commercial result event is priced and that ordinary Dataset writes are not silently billable.
3. It checks remaining run budget before spending on the TED scan when no result could be sold afterward.
4. It performs paid Dataset delivery through the linked `result-found` event inside a serialized money-critical section.
5. A positive charged count proves current-row delivery; an event-limit signal can still describe the final successfully charged row.
6. A zero charged count, budget stop, or thrown push never becomes a paid success.
7. Free status, partial, source-error, storage-error, duplicate, and withheld advisories do not use `result-found`.
8. KVS `OUTPUT` lets the operator reconcile Dataset and event counts before downstream action or retry.
   If delivery/charge state is unknown, `replaySafe` becomes false and the run is fatal. Do not blindly retry that input. Inspect the original run, Dataset, KVS and settled event ledger first. A second run can duplicate cost or state when the first outcome is unknown.

### API integration

Keep `APIFY_TOKEN` in a secret manager or environment variable. Never paste it into public input, a Dataset row, an issue, or a screenshot.

#### cURL

```bash
curl -sS -X POST \
  "https://api.apify.com/v2/acts/zinin~tender-alert/runs?waitForFinish=180" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  --data-binary @public-task.json
```

After the run is terminal, use `defaultKeyValueStoreId` to read `OUTPUT` and `defaultDatasetId` to read rows.

#### JavaScript

```javascript
import { ApifyClient } from 'apify-client';
import input from './public-task.json' with { type: 'json' };

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('zinin/tender-alert').call(input);
const receipt = await client.keyValueStore(run.defaultKeyValueStoreId).getRecord('OUTPUT');
if (!receipt?.value) throw new Error('Missing OUTPUT receipt');
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log({ receipt: receipt.value, rows: items });
```

#### Python

```python
import json
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ['APIFY_TOKEN'])
with open('public-task.json', encoding='utf-8') as handle:
    actor_input = json.load(handle)
run = client.actor('zinin/tender-alert').call(run_input=actor_input)
receipt = client.key_value_store(run['defaultKeyValueStoreId']).get_record('OUTPUT')
if not receipt: raise RuntimeError('Missing OUTPUT receipt')
rows = list(client.dataset(run['defaultDatasetId']).iterate_items())
print({'receipt': receipt['value'], 'rows': rows})
```

### Automation patterns

#### Daily bid-review queue

Schedule one stable watch per narrow opportunity thesis. On `COMPLETE`, create review tasks only for `recordType: new_tender`. Include title, buyer, country, deadline, primary URL, observation time, confidence, gaps, source status, run ID and watch name. Keep baseline and no-change status visible in operational logs but do not create false opportunities from them.

#### CRM enrichment

Upsert by `entityId` or TED `publicationNumber`, not by title. Store the raw row before mapping to your CRM. Use a separate human-controlled field for qualification, owner, fit, no-bid reason and submission status. Never overwrite source evidence with internal opinion.

#### n8n, Make, or Zapier

Wait for terminal completion. Read `OUTPUT`. Route invalid input to repair, retryable source failure to a bounded retry policy, replay-unsafe failure to manual reconciliation, and complete new-tender rows to review. Do not branch only on Dataset length.

#### Data warehouse

Retain raw JSON, source observation time, watch contract, baseline identity, Dataset/run IDs and schema version. Model confidence separately from value or priority. Recalculate freshness when queried; `fresh_at_run_time` is not a permanent property.

#### Agent or MCP workflow

Expose this schema-bound Actor through the standard Apify API or MCP server. Require the agent to cite the primary notice, state source completeness, enumerate gaps, and ask for human approval before outreach, bid drafting, spend, submission, or legal interpretation.

### Small-business playbooks

#### Software vendor

Use a precise IT-services CPV and a buyer country you can serve. Let the first run establish the bounded market. Review later rows against product fit, required certifications, framework structure, deadline, language, delivery geography and consortium needs. The Actor discovers evidence; it does not prove that your company qualifies.

#### Construction or facilities supplier

Use one watch per service family and geography. Keep a per-query limit compatible with publication volume. Review lots, place of performance, site requirements, financial capacity, insurance, safety, timelines and subcontracting in the primary notice. Do not infer these from title or CPV alone.

#### Agency or procurement consultant

Create isolated watch names for each client and scope; do not mix clients in one baseline. Export the evidence row with attribution and the current notice link, then add your own human qualification. Keep customer access controls and retention separate from public source availability.

#### Export team

Use country and CPV filters to reduce noise, but verify language, cross-border eligibility, standards, delivery, tax, guarantees and local representation manually. A public EU notice is not proof that an overseas supplier can bid.

#### Market research

Use complete no-change and new-identity counts as operational watch evidence, not as an exhaustive demand index. Search-window limits, schedules, query changes and amendments can bias longitudinal analysis. For market sizing, build a separate documented bulk-reuse pipeline.

### Security and privacy

The Actor uses fixed official TED endpoints, limited Actor permissions and no caller-supplied URL. Inputs define query text and state names, not network destinations. It requires no TED login, cookie, API key, email account or private tender-room credential.
Public procurement notices can still include names and contact details. Collect only what your stated purpose needs, configure Apify retention and access controls, restrict exports, avoid combining public contacts into an indiscriminate outreach list, and review applicable privacy and procurement rules. Public visibility is not consent to automated marketing.
Treat `watch_name`, query scope and stored baseline as operational business data. Do not put customer secrets, prices, confidential strategy, personal notes, credentials or signed storage URLs into them. Use Apify secrets for tokens in downstream workflows.

### Honest limitations

- Coverage is bounded by the submitted queries, country, per-query limit, schedule, and current TED availability.
- The product detects newly observed publication identities; it does not diff every amendment or field revision on an existing notice.
- The first complete run creates a baseline and therefore cannot prove which current notices are new to the buyer.
- A source-complete no-change result means no new identity inside this bounded watch, not that no relevant opportunity exists in TED.
- Published notice data can be corrected, translated, superseded, cancelled, or supplemented after this observation.
- CPV, buyer, title, country, dates, and URLs are source evidence, not guarantees of eligibility, scope, budget, fit, or award outcome.
- A successful process exit is insufficient for automation; consumers must reconcile KVS `OUTPUT`, Dataset rows, and PPE events.
  Additional interpretation rules:
- Newly observed means absent from this compatible stored baseline, not necessarily first publication anywhere.
- No new identity means no new identity in this bounded complete snapshot, not no opportunity in the market.
- Confidence describes evidence-path support, not eligibility, value, quality, urgency, or probability of award.
- A primary URL is a review pointer; the current notice and procurement documents remain authoritative.
- A deadline field can be missing, translated, amended, timezone-sensitive or superseded; verify before planning.
- The Actor does not interpret exclusions, licenses, sanctions, beneficial ownership, competition law, tax, labour, security, technical standards, or local procurement rules.
- The recommended action is a routing label and never proof that an action occurred.

### Production checklist

- \[ ] Each watch name maps to one stable query meaning and customer context.
- \[ ] Queries and country are valid TED expressions for the intended market.
- \[ ] Per-query limit covers expected volume between scheduled runs.
- \[ ] First-run baseline is reviewed and not counted as a newly published opportunity.
- \[ ] Only source-complete snapshots can advance the baseline.
- \[ ] Held-back identities remain eligible for later delivery.
- \[ ] Primary TED URL is opened before qualification or bid action.
- \[ ] Confidence and commercial priority are not conflated.
- \[ ] Dataset rows reconcile with KVS `OUTPUT` and PPE events.
- \[ ] Replay-unsafe runs go to manual reconciliation, not automatic retry.
- \[ ] `safeToAutomate=false` survives every export and integration.
- \[ ] Attribution, retention, privacy, and access controls match the downstream use.

### Troubleshooting

#### The first run did not return “new” tenders

Expected. It creates a complete bounded baseline. Schedule the same watch again to detect identities that were absent from that reference set.

#### A run returned a free partial advisory

At least one query was unavailable or unclassifiable. Read `sourceFailureReasons`, keep the old baseline, and retry only if `retryable` is true. Do not convert it to a clean no-change event.

#### Relevant notices are missing

Review expert-query syntax, CPV hierarchy, country, publication timing, per-query limit, schedule frequency and current TED availability. The Actor is bounded and does not claim exhaustive market coverage.

#### The same notice appeared twice downstream

Upsert by `entityId` or `publicationNumber`, inspect whether watch meaning changed, and reconcile the run receipt. Title-based deduplication is unsafe.

#### More notices were detected than delivered

Read `withheldRowCount`, `budgetStopped`, `max_items`, and the free advisory. Undelivered new identities are intentionally excluded from the baseline so they can surface later.

#### Dataset and KVS disagree

Stop automatic processing. Preserve the original run, inspect Dataset, `OUTPUT`, logs and settled charged events, and do not retry if `replaySafe` is false.

### FAQ

#### Is this the official TED source?

Yes. The runtime calls the official `api.ted.europa.eu` Search API and retains primary `ted.europa.eu` notice links.

#### Why is the first run a paid baseline row?

A stateful delta needs an explicit complete reference set. The row proves the watch was initialized and prevents current notices from being falsely sold as newly discovered.

#### What happens if one query fails?

The entire snapshot is treated as incomplete. The Actor emits a free advisory, does not claim a clean delta, and does not advance the baseline.

#### Can a held-back tender disappear forever?

No. The baseline advances only for identities actually delivered and charged. A notice withheld by `max_items` or budget remains new for a later run.

#### Does no new tender mean no market demand?

No. It means this bounded watch observed no identity new to its stored baseline in this run.

#### Can this decide whether we should bid?

No. It routes primary evidence to a human. Eligibility, economics, capacity, legal review, deadline, and bid strategy remain outside the Actor.

#### Can I use it with n8n, Make, Zapier, or an agent?

Yes. Wait for terminal completion, read KVS `OUTPUT`, branch on explicit status, then fetch Dataset rows. Keep `safeToAutomate=false` for external action.

#### What should I retain for audit?

Keep input, watch name, query scope, limits, observation time, source status, primary notice URL, Dataset row, KVS `OUTPUT`, run ID, and human disposition together.

### Support

For a reproducible issue provide the run ID, Actor version, sanitized input, watch name, query count, source status, Dataset row count, `OUTPUT` status and whether the run was baseline, complete delta, partial, withheld or reconciliation. Never send an API token, private export, signed storage URL or confidential bid material.
Tender Alert — Official EU TED Opportunity Watch is deliberately conservative: it sells a bounded official-source delta that can be explained and audited. It does not sell false certainty about a market, a buyer, eligibility, or an award.

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

## `watch_name` (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`):

Maximum number of official TED notices requested per query, per run. Higher expands the bounded observed window but never means exhaustive market coverage.

## `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"
  ],
  "country": "DEU",
  "watch_name": "de-it-services",
  "limit": 20,
  "max_items": 20
}
```

# Actor output Schema

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

API URL for the default Dataset rows produced by this run.

## `output` (type: `string`):

API URL for the decision, delivery and billing reconciliation receipt stored at KVS key OUTPUT.

# 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"
    ],
    "watch_name": "apify-daily-test",
    "limit": 25
};

// 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"],
    "watch_name": "apify-daily-test",
    "limit": 25,
}

# 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(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 '{
  "queries": [
    "classification-cpv=72000000"
  ],
  "watch_name": "apify-daily-test",
  "limit": 25
}' |
apify call zinin/tender-alert --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,zinin/tender-alert"
        }
    }
}
```

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/6Cd3RrzTJuGZKm9Ni/builds/lorprngfPKd2LLWZI/openapi.json
