# EU Tenders (TED) for Central & Eastern Europe → JSON (`quietpinelabs/ted-cee-notices`) Actor

Public procurement notices from TED (Tenders Electronic Daily) for PL, CZ, SK, HU, SI, HR, RO, BG, AT, LT, LV, EE as clean JSON: buyer, CPV, values, deadlines, winners. Official TED API v3, personal contact data removed by default, incremental mode.

- **URL**: https://apify.com/quietpinelabs/ted-cee-notices.md
- **Developed by:** [Matus Holec](https://apify.com/quietpinelabs) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 1,000 tender 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

## EU tender notices from TED for Central & Eastern Europe as clean JSON

Get public-procurement notices published on **TED – Tenders Electronic Daily** (the Supplement to the Official Journal of the EU) for buyers in twelve Central and Eastern European countries as flat, validated JSON: buyer, title, CPV codes, place of performance, estimated value, deadline, winners and award value, with deep links to the HTML, XML and PDF notice. The actor uses the official TED Search API v3 (no scraping, no API key), maps eForms fields to one stable schema, requests no personal contact data unless you opt in, and in `incremental` mode returns only notices published since the previous run.

Default countries: Poland, Czechia, Slovakia, Hungary, Slovenia, Croatia, Romania, Bulgaria, Austria, Lithuania, Latvia, Estonia (`POL CZE SVK HUN SVN HRV ROU BGR AUT LTU LVA EST`). Any other ISO country code works too.

### Who uses it

- **Bid managers and sales teams** — a daily feed of new contract notices by CPV and country, pushed to Slack, a CRM or a sheet via Make, Zapier or n8n.
- **Procurement analysts and compliance teams** — award notices with winners and values for market-share, pricing and vendor-risk analysis.
- **Data journalists and researchers** — who buys what from whom across CEE, with publication numbers to cite.

### Use cases

**1. New construction tenders in Czechia and Slovakia, last 7 days (default range):**

```json
{ "countries": ["CZ", "SK"], "cpv": ["45"], "noticeTypes": ["cn-standard"] }
```

Contract notices whose CPV starts with 45, with estimated value and submission deadline.

**2. Who won IT contracts in Poland in September 2026:**

```json
{ "countries": ["POL"], "cpv": ["72"], "noticeTypes": ["can-standard"], "dateFrom": "2026-09-01", "dateTo": "2026-09-30", "maxRecords": 5000 }
```

Award notices with `winners[]` (name, country, national ID) and `awardValue`.

**3. Daily delta for a buyer (schedule as a task):**

```json
{ "countries": ["CZE"], "name": "Praha", "mode": "incremental" }
```

Only notices published since the previous run whose buyer name contains "Praha".

### Input

| Field | Type | Default | Meaning |
|---|---|---|---|
| `countries` | string\[] | 12 CEE countries | Buyer country, ISO alpha-2 or alpha-3 (`buyer-country IN (…)`). |
| `cpv` | string\[] | `[]` | CPV codes; 8 digits = exact, shorter = prefix (`45` → `45*`). |
| `noticeTypes` | string\[] | `[]` = all | eForms notice types: `cn-standard`, `can-standard`, `pin-only`, `can-modif`, `cn-social`, `veat`, … |
| `placeOfPerformance` | string\[] | `[]` | Country or NUTS codes (`CZ06`, `PL9`), also `anyw`, `anyw-eea`. |
| `name` | string | – | Words that must appear in the buyer name (`buyer-name ~ ("…")`, stemmed, all words). |
| `dateFrom`, `dateTo` | date | last `daysBack` days / none | Publication-date range. |
| `daysBack` | integer | `7` | Default lower bound when `dateFrom` is empty. TED publishes Mon–Fri. |
| `scope` | `ALL` / `ACTIVE` / `LATEST` | `ALL` | `ACTIVE` = deadline not passed; `LATEST` = today's OJ S edition only. |
| `includeContactPersons` | boolean | `false` | Opt-in `contacts[]` with the buyer's contact-point name, e-mail and phone (see *Data protection*). |
| `mode` | `full` / `incremental` | `full` | See *Incremental mode*. |
| `maxRecords` | integer | `1000` | Stop after this many notices. |
| `icos`, `region` | – | ignored | TED has no IČO lookup (use `name`) and no region filter (use `placeOfPerformance`). |

### Output

One `TenderNotice` per notice. Real award notice from the default input (local run on 2026-10-18; description shortened):

```jsonc
{
  "publicationNumber": "715145-2026",
  "publicationDate": "2026-10-16",
  "ojsNumber": "201/2026",
  "noticeType": "can-standard", "formType": "result", "procedureType": "open",
  "title": "Czechia – Software support services – Poskytování provozní podpory a dalších služeb pro systém Manuscriptorium",
  "description": "Předmětem plnění veřejné zakázky je poskytování provozní podpory a dalších služeb pro systém Manuscriptorium, …",
  "buyer": { "name": "Národní knihovna České republiky", "country": "CZ", "id": "00023221", "city": "Praha", "url": "https://www.nkp.cz/" },
  "cpv": ["72261000"],
  "placeOfPerformance": ["CZ010", "CZE"],
  "contractNature": "services",
  "estimatedValue": { "amount": 4350000, "currency": "CZK" },
  "winners": [ { "name": "aSolution s.r.o.", "country": "CZ", "id": "26398591" } ],
  "awardValue": { "amount": 1042678, "currency": "CZK" },
  "links": {
    "html": "https://ted.europa.eu/en/notice/-/detail/715145-2026",
    "xml": "https://ted.europa.eu/en/notice/715145-2026/xml",
    "pdf": "https://ted.europa.eu/en/notice/715145-2026/pdf"
  },
  "provenance": {
    "source": "ted",
    "sourceName": "TED – Tenders Electronic Daily, Publications Office of the European Union",
    "sourceUrl": "https://ted.europa.eu/en/notice/-/detail/715145-2026",
    "licence": "Source: TED, Publications Office of the European Union (https://ted.europa.eu), reused under Commission Decision 2011/833/EU. …",
    "retrievedAt": "2026-10-18T01:57:45.860Z", "sourceUpdatedAt": "2026-10-16"
  }
}
```

Contract notices (`formType: "competition"`) carry `deadline` (earliest lot deadline) and an empty `winners`. `title` and `description` are English when TED has it, otherwise the buyer country's language. `buyer.id` is the national registration number (IČO, NIP, CUI…). `estimatedValue` is omitted when only several lot values exist (no silent summing). Run statistics, including the exact TED query, are in the key-value store record `STATS`.

### Pricing in plain words

You pay **$2 per 1,000 notices returned**, plus $0.00005 per run start. Empty results are free — a weekend run that finds no new notices costs nothing. On the free plan ($5 monthly credit) that is about 2,400 notices a month. Bronze, Silver and Gold volume discounts are configured on the store page. The run stops gracefully at `maxRecords` or your maximum total charge; in incremental mode the remainder is picked up by the next run.

### Limits & fair use

- TED's fair-usage policy allows **700 requests per minute per IP** and 600 visualisations or downloads in 6 minutes. This actor sends **2 requests per second, 1 at a time**, 250 notices per request, and never downloads notice XML or PDF — a run of 10,000 notices needs about 41 requests. Persistent 429, 403, 5xx or a maintenance page fail the run with a clear message; nothing is charged in that case.
- Query syntax is validated first (`checkQuerySyntax`); a bad field or value fails the run with TED's own message.
- `ITERATION` paging (Elasticsearch point-in-time) has no cap on total results; tens of thousands of notices are possible but take time (about 500 notices per second).
- `name` is a buyer-name search with TED's `~` operator (stemming, all words must match), not a full-text search.

### Incremental mode

The cursor is kept in the run's default key-value store under `CURSOR` **and mirrored to the named key-value store `ted-cee-notices-cursor`** (override with the `cursorStore` input, one per watch-list), so it survives across runs and tasks. Format: `{ "value": { "lastPublicationDate": "2026-10-16", "seenNumbersOnThatDate": ["715119-2026", "…"] }, "updatedAt": "…" }`. The next run queries `publication-date >= lastPublicationDate` sorted ascending and skips the publication numbers already returned on that date. The cursor advances only over records actually pushed, so hitting `maxRecords` or the budget never loses notices. A first incremental run behaves like `full` with your date range. Changing filters between runs does not reset the cursor; delete `CURSOR` to start over. TED notices are immutable — corrections arrive as new notices (`can-modif`, `corr`) with new publication numbers.

### Data protection

TED publishes data about economic entities (contracting authorities and tenderers) under Commission Decision 2011/833/EU, but notices also contain contact persons, e-mails, phone numbers and sometimes beneficial-owner and jury-member names. This actor:

- **requests none of those fields from TED by default** — only 33 organisation-level fields; a key deny-list scrub is applied to every record as a last line of defence;
- reduces a **winner name that looks like a natural person** (two or three capitalised words, no digits, legal-form or organisation words) to initials, keeping the identifier and country — a conservative heuristic for sole traders, not a guarantee;
- with **`includeContactPersons: true`** adds the buyer's published contact point (name, e-mail, phone) to `contacts[]`; you become the controller of that personal data, so enable it only with a lawful basis such as contacting the authority about the tender;
- stores no personal data on the developer's side — datasets live in your Apify storage; only the cursor and counters are kept.

Lawful basis for the processing inside a run is Art. 6(1)(f) GDPR — legitimate interest in machine-readable procurement data for due diligence, market intelligence and journalism. You are the controller of what you download. Requests concerning notice content go to the Publications Office (op-copyright@publications.europa.eu) or the buyer named in the notice; `links.html` opens the notice. This section describes the actor's behaviour and is not legal advice.

### FAQ

**Why did my run return nothing?** TED publishes Monday to Friday; with `daysBack` under 3 or `scope: LATEST` a weekend run can be legitimately empty — and free. Check `STATS.totalMatching` and the exact query.

**Can I search by winner or by IČO/NIP?** Not in this version. Filter award notices by country, CPV and date, then match `winners[].id` in your own pipeline; `icos` is ignored.

**Where is the full tender text and the documents?** Use `links.html`, `links.xml` or `links.pdf`. The actor does not download them (they count against TED's 600 downloads per 6 minutes).

**Why do some notices lack `buyer.id`, `estimatedValue` or `winners`?** Only eForms-era fields are used; legacy pre-2024 notices and notices with several lot values leave those fields empty rather than guessing.

**A winner appears as initials, e.g. "J. N.".** The name matched the natural-person heuristic (sole trader). The identifier and country are kept so you can still match the company.

**Who invoices me?** Apify. Usage is billed to your Apify account; the developer never sees your data or payment details.

### Source & licence

Source: TED, Publications Office of the European Union (https://ted.europa.eu), reused under Commission Decision 2011/833/EU. Modified: normalised to JSON, personal contact data removed. Not endorsed by the Publications Office; TED logo not used.

Legal notice: https://ted.europa.eu/en/legal-notice. Fair-usage policy: https://ted.europa.eu/en/news/fair-usage-policy-on-ted. Each record carries the attribution in `provenance.licence`. Not affiliated with the Publications Office of the European Union.

### Changelog / support

- 0.1 — first public release: TED API v3 search for 12 CEE countries, CPV/notice-type/place filters, award data, incremental mode.

Questions, bugs and feature requests: use the **Issues** tab of this actor.

Documentation drafted with AI assistance and reviewed by the publisher before publication.

# Actor input Schema

## `countries` (type: `array`):

ISO 3166-1 codes of the buyer's country (alpha-2 `CZ` or alpha-3 `CZE`). Empty = all 12 CEE countries: POL CZE SVK HUN SVN HRV ROU BGR AUT LTU LVA EST.

## `cpv` (type: `array`):

Common Procurement Vocabulary codes. Full 8-digit codes match exactly; shorter values are prefixes (`45` = all construction work, `7132` → `7132*`).

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

eForms notice-type codes, e.g. `cn-standard` (contract notice), `can-standard` (contract award), `pin-only`, `can-modif`, `cn-social`. Empty = all types.

## `placeOfPerformance` (type: `array`):

Country codes (alpha-2/alpha-3) or NUTS codes (e.g. `CZ06`, `PL9`) of the place of performance. Empty = no filter.

## `name` (type: `string`):

Words that must appear in the buyer (contracting authority) name, e.g. `Praha` or `Ministerstvo`.

## `dateFrom` (type: `string`):

Publication date lower bound (YYYY-MM-DD or DD.MM.YYYY). Default: today minus `daysBack` days.

## `dateTo` (type: `string`):

Publication date upper bound (YYYY-MM-DD or DD.MM.YYYY). Default: no upper bound.

## `daysBack` (type: `integer`):

Used only when `dateFrom` is empty: search notices published in the last N days. TED publishes Mon–Fri, so keep ≥ 3 to always get results.

## `scope` (type: `string`):

`ALL` = every notice; `ACTIVE` = notices whose deadline has not passed; `LATEST` = only today's OJ S edition (date filters are then applied only if you set them).

## `includeContactPersons` (type: `boolean`):

Adds a `contacts[]` block with the buyer's contact point name, e-mail and phone as published on TED. You become the data controller for this personal data — enable only with a lawful basis.

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

`full` returns everything matching; `incremental` returns only notices published after the previous run (cursor `CURSOR` in the key-value store: last publication date + numbers already returned that day).

## `maxRecords` (type: `integer`):

Stop after this many notices (also protects your budget).

## `icos` (type: `array`):

Common field of the CEE actor family; TED has no IČO lookup, so this field is ignored here. Filter by `name` or search award notices by winner in the TED XML instead.

## `region` (type: `string`):

Common field of the CEE actor family; use `placeOfPerformance` (NUTS codes) instead.

## `cursorStore` (type: `string`):

Named key-value store that keeps the incremental-mode cursor across runs (default `ted-cee-notices-cursor`). Use a different name per watch-list.

## Actor input object example

```json
{
  "countries": [
    "CZE",
    "SVK"
  ],
  "daysBack": 7,
  "scope": "ALL",
  "includeContactPersons": false,
  "mode": "full",
  "maxRecords": 20
}
```

# Actor output Schema

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

All records returned by this run (full JSON).

## `overview` (type: `string`):

No description

## `stats` (type: `string`):

Records requested, returned and charged, plus source request counts.

# 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 = {
    "countries": [
        "CZE",
        "SVK"
    ],
    "maxRecords": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("quietpinelabs/ted-cee-notices").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 = {
    "countries": [
        "CZE",
        "SVK",
    ],
    "maxRecords": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("quietpinelabs/ted-cee-notices").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 '{
  "countries": [
    "CZE",
    "SVK"
  ],
  "maxRecords": 20
}' |
apify call quietpinelabs/ted-cee-notices --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,quietpinelabs/ted-cee-notices"
        }
    }
}
```

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/o7UQaww6hVVa6WAY0/builds/g4JdAx2R9M1CqZhEK/openapi.json
