# EU Public Procurement Notices — TED tenders as clean JSON (`eudatafeeds/eu-procurement-notices-ted`) Actor

Open EU public tenders from TED (Tenders Electronic Daily): one flat JSON row per procurement notice, filtered by CPV code, buyer country, value and date. Contact persons removed. Pay per notice returned.

- **URL**: https://apify.com/eudatafeeds/eu-procurement-notices-ted.md
- **Developed by:** [EU Data Feeds](https://apify.com/eudatafeeds) (community)
- **Categories:** Business, Developer tools, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 notice returneds

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?

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

Every public tender above the EU thresholds is published on **TED** (Tenders Electronic Daily), the
official EU public procurement journal. The official API is an eForms tree: multilingual arrays, three
different date formats, and personal data mixed into the contact blocks. This Actor turns TED into one
flat row per procurement notice that you can drop straight into a spreadsheet, a database or an LLM
pipeline.

Its sibling Actor, **EU Public Procurement Contract Awards (TED)**, answers the other half of the
question: what was actually bought, from where, and for how much against the estimate.

### Who buys this

- **Bid teams** running a daily tender watch on their CPV codes and countries.
- **Procurement and GovTech products** that need an EU tender feed without maintaining an eForms parser.
- **Consultancies and analysts** sizing public-sector demand by CPV division or member state.
- **Sales teams** building a public-sector pipeline from open contract notices.

### Coverage (measured, not claimed)

A 1,000-notice sample of the last 7 days, taken 2026-09-05, carried buyers from **all 27 EU member
states** plus Norway, Iceland, Switzerland and Albania — 31 countries in one week's window. Coverage is
exactly TED's coverage: if a notice is not in TED, it is not here.

**What the default run returns, and how to narrow it.** TED's daily flow is roughly half open calls and
half already-decided contracts. In an EU-wide 250-notice sample of the last 7 days (2026-09-08):
`cn-standard` 112, `can-standard` 107, `can-modif` 15, `cn-social` 5, `pin-only` 4, `can-social` 3,
`pin-rtl` 2, `cn-desg` 1, `veat` 1 — so **about 47 % are open calls, 50 % are award notices** and the
rest are prior-information notices. `noticeTypes` is left empty by default so nothing is hidden from you.
For open calls you can still bid on, set it explicitly:

```json
{ "noticeTypes": ["cn-standard", "cn-social", "cn-desg"] }
```

If you want the award side instead, the sibling Actor `eu-contract-awards-ted` is built for it and adds
the awarded value, the pre-tender estimate and the ratio between them.

**`tenderDeadline` fill rate: 100 % of open calls** (49 of 49 `cn-*` rows in the CZ/SK CPV-72 window,
measured 2026-09-08). Award and prior-information notices have no submission deadline by nature and
carry `null`. Where a notice sets a different deadline per lot, the row reports the **earliest** one.

**`tenderDeadline` comes in two ISO-8601 shapes and both are deliberate:** a bare calendar date
(`2026-10-15`) where TED publishes only a day, and a full instant (`2026-10-16T23:59:59+02:00`) where TED
publishes a cut-off time. Measured 2026-09-08 across 135 filled deadlines: 98 bare dates (73 %) and 37
timestamps (27 %). The offset is **not** trimmed off the second shape, because a submission cut-off at
23:59:59+02:00 is not the same instant as one at 23:59:59 UTC and dropping the offset would silently move
it. Parse with any ISO-8601 reader and both shapes work; parse with a date-only reader and 27 % of rows
will fail on you.

**`estimatedValue` fill rate: 86 % of open calls** (42 of 49 `cn-*` rows in the same window, measured
2026-09-08). Where a notice publishes only per-lot budgets and no procurement total, the row reports the
**sum across lots** — verified against TED's own procedure-level total, which equalled that sum on every
multi-lot notice carrying both. Lots in different currencies are never added: that row reports `null`
rather than a number that is true of nothing. The remaining 7 of 49 publish no value at any level in TED,
so the column is empty because the source is, and it is left empty rather than estimated.

### What you get

One row per notice:

| Field | Example |
|---|---|
| `publicationNumber` | `00512345-2026` |
| `publicationDate` | `2026-09-01` |
| `noticeType` | `cn-standard` |
| `title` | `Supply of laboratory equipment` |
| `buyerName` | `Univerzita Karlova` |
| `buyerCountry` | `CZE` |
| `cpvCodes` | `["38000000", "38900000"]` |
| `estimatedValue` / `currency` | `1250000` / `CZK` (procurement total, or the sum across lots) |
| `tenderDeadline` | `2026-10-15` or `2026-10-16T23:59:59+02:00` (earliest across lots; `null` on award notices) |
| `placeOfPerformance` | `["CZ010"]` |
| `noticeUrl` / `pdfUrl` | links to the official notice |

Dates are normalized to plain ISO dates, CPV code lists are deduplicated, and titles are taken from the
English language version where TED publishes one.

### Input

| Option | Meaning |
|---|---|
| `days` | Lookback window, 1–365. Default 7. |
| `cpv` | CPV codes or prefixes — `72000000` for IT services, or just `45` for all construction. |
| `countries` | Buyer country codes: `CZ`, `DE`, `PL`, … |
| `noticeTypes` | e.g. `cn-standard` (contract notice), `can-standard` (contract award). |
| `minValueEur` | Only notices at or above this estimated value. |
| `maxResults` | Hard cap on rows returned — and therefore on what you pay. |

Leave everything empty and you get the last 7 days of EU procurement notices across the whole EU.

### Running it on a schedule

Set `days` to match your interval (a daily tender watch wants `days: 1`), add your CPV codes and
countries, and put the Actor on an Apify schedule. Each run writes a dataset you can pull from the
Apify API, export as JSON/CSV/Excel, or push onward with an integration.

### Pricing

Pay per event, and both numbers are here so you never have to open the Pricing tab to budget a run:

| Event | Price | When it fires |
|---|---|---|
| Actor start | $0.005 | Once per run, never more. |
| Notice returned | $0.002 | Once per procurement notice actually pushed to the dataset. |

A default run (`maxResults: 100`) therefore costs **$0.005 + 100 x $0.002 = $0.205** at most. `maxResults`
is a hard cap on rows and so is your hard cap on spend: 500 notices is $1.005, 5,000 is $10.005.

A run that finds nothing costs $0.005 — the start event only. Charging happens **after** each row is
pushed, so a run that fails halfway never bills you for data you did not receive, and the `RUN_SUMMARY`
record written at the end tells you exactly how many rows you were charged for.

### Personal data

TED notices contain named contact persons at the contracting authority. **This Actor removes them.**
Contact names, e-mail addresses and phone numbers are dropped in code before anything is written to the
dataset, and free-text fields are scrubbed for inlined addresses. You get organisations, not people.
The open-data licence permits reuse; it does not waive the GDPR, so the personal data simply never
leaves the pipeline.

### Source and attribution

Source: Tenders Electronic Daily (TED), ted.europa.eu, © European Union. Editorial content reused under
CC BY 4.0, metadata under CC0 1.0. Data normalized and filtered by this Actor; personal data removed.
**This is not an official EU publication** — for anything legally binding, use the linked original notice.

### Support and refunds

E-mail support, first reply within 1 business day. Not happy? Email us within 14 days for a full refund.

### Limits

- Coverage and freshness are whatever TED publishes; if a notice is not in TED, it is not here.
- Below-threshold contracts that never reach TED are out of scope — national portals are not read.
- The Actor reads the public TED Search API. Very large windows across all of the EU take longer and
  cost more — narrow by CPV or country first.
- No human consulting, no custom fields on request, no SLA beyond the reply time above.

### FAQ

**Does this cover all 27 EU member states?** Yes — see the measured coverage above. TED is the single
EU-wide publication point for above-threshold public procurement.

**How is this different from scraping ted.europa.eu myself?** You would be parsing eForms: multilingual
arrays, per-lot repetition, two date shapes and contact blocks you have to strip for GDPR. This Actor is
that parser, already written and tested, with the personal data removed before it reaches you.

**Can I get contract awards as well?** Yes — the sibling Actor **EU Public Procurement Contract Awards
(TED)** returns award notices with the awarded value, the pre-tender estimate and their ratio.

**Do I get the winner's name?** Not from this Actor. Award notices are handled by the sibling Actor,
which also omits winner names for GDPR reasons; the official notice at `noticeUrl` carries them.

# Actor input Schema

## `days` (type: `integer`):

How far back to search, counted from today. 1-365.

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

CPV codes or prefixes, e.g. 72000000 for IT services or just 45 for construction. Empty = all sectors.

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

Countries of the contracting authority. Two-letter (CZ, DE, PL) or TED's three-letter (CZE, DEU, POL) codes both work. An unknown code fails the run instead of being ignored. Empty = all countries.

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

TED notice types. Empty = all types, which is about 47 % open calls and 50 % already-awarded contracts (EU-wide 7-day sample, 2026-09-08). For open calls you can still bid on, use cn-standard, cn-social, cn-desg. For awards, the sibling Actor eu-contract-awards-ted carries the awarded value and the pre-tender estimate.

## `minValueEur` (type: `integer`):

Only notices with an estimated total value at or above this amount. Notices without a published value are excluded when this is set.

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

Upper bound on notices returned. You are charged per notice returned, so this is also your cost cap.

## Actor input object example

```json
{
  "days": 7,
  "cpv": [
    "72000000"
  ],
  "countries": [
    "CZ"
  ],
  "noticeTypes": [],
  "maxResults": 100
}
```

# Actor output Schema

## `notices` (type: `string`):

All notices returned by this run, one row each. Field definitions are in the dataset schema.

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

Row count, the TED attribution line and confirmation that personal data was removed.

# 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 = {
    "cpv": [
        "72000000"
    ],
    "countries": [
        "CZ"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("eudatafeeds/eu-procurement-notices-ted").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 = {
    "cpv": ["72000000"],
    "countries": ["CZ"],
}

# Run the Actor and wait for it to finish
run = client.actor("eudatafeeds/eu-procurement-notices-ted").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 '{
  "cpv": [
    "72000000"
  ],
  "countries": [
    "CZ"
  ]
}' |
apify call eudatafeeds/eu-procurement-notices-ted --silent --output-dataset

```

## MCP server setup

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

```

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/QLxwNx2OgycoUOuEj/builds/DKHetbFsmZqcgt0Nc/openapi.json
