# EU Public Tenders Feed (TED): New Tenders, Awards & Deadlines (`ftrytech/ted-tenders-feed`) Actor

New EU public tenders from TED's official API: open calls, awards and planning notices filtered by keyword, CPV code and buyer country, with deadline, estimated value, buyer and links. Remembers past runs, so a schedule returns only new notices. For bid teams, sales and AI agents.

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

## Pricing

$2.00 / 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.

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 Public Tenders Feed (TED): New Tenders, Awards & Deadlines

Get new public tenders from **TED (Tenders Electronic Daily)**, the EU's official journal of public procurement, as clean JSON. Each notice comes with its title in your language, the buyer, the country, CPV codes, the submission deadline and the days left, the estimated or awarded value, a description, and links to the notice, its PDF and the submission portal. You can filter by keyword, CPV code, buyer country and notice kind.

**It remembers.** By default each run returns only notices it has not returned before for the same filters. Put it on a daily schedule and you get a clean feed of what TED published since the last run, with no duplicates and no deduplication on your side.

By **FtryTech**.

### What it reads

TED publishes about 3,000 notices each working day from the 27 EU countries, Norway, Switzerland, Iceland, the UK's legacy notices, EU institutions and candidate countries such as Albania, Serbia and Ukraine.

| Notice kind | What you get |
|---|---|
| `open_tenders` (default) | Calls for competition you can still bid on: deadline, estimated value, submission link |
| `awards` | Contract award results: awarded value and currency, winner countries |
| `planning` | Prior information and market-consultation notices, months before the tender |
| `all` | Every kind, including corrections and contract modifications |

**Only the official, documented API.** The Actor calls TED's public Search API (`api.ted.europa.eu/v3/notices/search`), which the EU Publications Office runs for reuse and which needs no key or login. TED's copyright notice says the notices "can be freely reused, for commercial or non-commercial purposes". The Actor uses no browser, no proxy and no scraping of web pages, so runs are fast and cheap. It does not return contact persons, e-mail addresses or winner names.

### Who it is for

- **Bid and proposal teams:** a morning list of new tenders in your CPV codes and countries, with the days left to the deadline.
- **Sales teams selling to the public sector:** see which ministries, cities and hospitals are buying what, and for how much.
- **Market researchers and consultants:** award values by sector and country, and planning notices that show demand months early.
- **Tender newsletters and platforms:** a deduplicated EU feed in one language.
- **AI agents:** one call answers "which new EU tenders match X?". Every input has a default, and an empty answer is an explicit row with a reason, never a silent empty dataset.

### Input

Every field is optional. With no input you get the newest open tenders of the last 3 days.

```json
{
  "noticeKind": "open_tenders",
  "keywords": ["cybersecurity", "penetration test"],
  "cpvCodes": ["72000000"],
  "countries": ["DE", "AT", "NL"],
  "publishedWithinDays": 7,
  "onlyNewNotices": true,
  "stateKey": "my-it-feed",
  "maxNotices": 200,
  "language": "en",
  "includeDescription": true
}
```

| Field | Default | Meaning |
|---|---|---|
| `noticeKind` | `open_tenders` | `open_tenders`, `awards`, `planning` or `all`. |
| `keywords` | none | Keep notices that contain any of these words or phrases (TED full-text search, all languages). |
| `cpvCodes` | none | Keep notices under these CPV codes or anything below them. `72000000`, `72000000-5` and the prefix `72` all work. |
| `countries` | none | Buyer countries as ISO codes: `DE`, `FRA`, `NOR`, ... |
| `publishedWithinDays` | `3` | Read notices published in the last N days, today included. |
| `onlyNewNotices` | `true` | Skip notices an earlier run already returned for the same filters. |
| `stateKey` | `default` | The memory name. Give each schedule or client its own. |
| `maxNotices` | `50` | The most notice rows per run (up to 10,000), newest first. |
| `language` | `en` | The language for titles, buyer names and descriptions where TED has it (24 EU languages). |
| `includeDescription` | `true` | Add the procedure description (up to 10,000 characters). |

### Output

Each row has a `type`.

**`notice`**

```json
{
  "type": "notice",
  "noticeId": "736964-2026",
  "publishedAt": "2026-10-23",
  "noticeKind": "open_tenders",
  "noticeType": "cn-standard",
  "title": "Albania – Miscellaneous computer equipment – “Purchase of Electronic Equipment, for the National Authority for Cybersecurity”.",
  "titleLanguage": "eng",
  "buyerName": "Central Purchasing Operator (OBP)",
  "buyerCountry": "ALB",
  "cpvCodes": ["30236000"],
  "mainCpv": "30236000",
  "contractNature": "supplies",
  "procedureType": "open",
  "deadline": "2026-11-20",
  "deadlineType": "tenders",
  "daysToDeadline": 27,
  "estimatedValue": 459035.66,
  "estimatedValueCurrency": "EUR",
  "awardedValue": null,
  "awardedValueCurrency": null,
  "noticeUrl": "https://ted.europa.eu/en/notice/-/detail/736964-2026",
  "pdfUrl": "https://ted.europa.eu/en/notice/736964-2026/pdf",
  "xmlUrl": "https://ted.europa.eu/en/notice/736964-2026/xml"
}
```

Rows also carry `lotTitles`, `buyerCity`, `buyerWebsite`, `frameworkAgreement`, `placeOfPerformance` (NUTS regions), `winnerCountries` (awards), `description`, `submissionUrl`, `documentsUrl` and `officialLanguage`.

**`no_results` (free):** one row when the run returns no notice. Its `reason` is one of `no_matching_notices`, `all_seen_before`, `invalid_filter`, `query_rejected` or `fetch_failed`. `message` says what to change, and `query` shows the TED query that ran. You are never charged for these rows.

### Pricing (pay per event)

| Event | Price |
|---|---|
| Tender notice (one `notice` row) | $0.002 ($2 per 1,000 notices) |
| `no_results` rows | free |

Apify also charges its standard tiny run-start fee. Notices skipped because an earlier run returned them are not charged. The run stops cleanly at your spending limit, and notices it did not return stay new for the next run.

### Tips

- **Daily tender alert:** set your `cpvCodes` and `countries`, keep `onlyNewNotices` on, schedule the Actor daily, and connect the dataset to Slack, e-mail, Sheets or a webhook.
- **Award research:** `noticeKind: "awards"`, `onlyNewNotices: false`, `publishedWithinDays: 365` and a CPV code give a year of award values for a sector.
- **Early warning:** `noticeKind: "planning"` shows buyers who plan to tender soon.
- To find CPV codes, search the CPV list on the EU's SIMAP site, or give keywords instead.

### For AI agents (MCP)

- Call it with no input, or with only `keywords` or `cpvCodes`. Every other input has a default.
- Read `type` first. A `no_results` row explains an empty answer in `reason` and `message`.
- Set `onlyNewNotices: false` for a one-off question, so an earlier run's memory does not hide notices.
- `daysToDeadline` answers "can we still bid?" without date arithmetic.

### Limits

- Only TED. National notices below the EU thresholds, which countries publish only on their own portals, are not in TED.
- Descriptions are usually only in the notice's original language. Titles are in every EU language.
- Estimated values appear only when the buyer publishes them. Lots in mixed currencies are not summed.
- Winner names are left out on purpose, because a winner can be a private person. Use `xmlUrl` for the full notice.

Questions, or a portal you need? Open an issue on the Actor's Issues tab.

# Actor input Schema

## `noticeKind` (type: `string`):

open_tenders: calls for competition you can still bid on. awards: contract award results (awarded value, winner countries). planning: prior information and market-consultation notices. all: every kind, including changes and modifications.

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

Keep notices whose text contains any of these words or phrases, in any official language (TED full-text search), e.g. software, cybersecurity, "medical devices". Empty keeps every notice.

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

Keep notices classified under these CPV codes or anything below them, e.g. 72000000 (IT services), 45000000 (construction), 33100000 (medical equipment). 72000000-5 and a short prefix such as 72 also work. Empty keeps every sector.

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

Keep notices from buyers in these countries: ISO codes such as DE, FR, NOR or POL. Empty keeps every country.

## `publishedWithinDays` (type: `integer`):

Read notices TED published in the last N days, today included. TED publishes about 3,000 notices each working day.

## `onlyNewNotices` (type: `boolean`):

Return only notices this Actor has not returned before for the same filters and memory name. Each scheduled run then returns only what TED published since. Off returns every match each time.

## `stateKey` (type: `string`):

Which memory of already-returned notices to use. Give each schedule or client its own name so they do not share one. Letters, digits and hyphens.

## `maxNotices` (type: `integer`):

The most notice rows written (and billed) in one run, newest first.

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

The language for titles, buyer names and descriptions where TED has it; otherwise English, otherwise the notice's own language. TED translates every title into all EU languages; descriptions are usually only in the original language.

## `includeDescription` (type: `boolean`):

Add the procedure's description (up to 10,000 characters). Off keeps rows small; the price is the same.

## Actor input object example

```json
{
  "noticeKind": "open_tenders",
  "keywords": [
    "software"
  ],
  "cpvCodes": [],
  "countries": [],
  "publishedWithinDays": 3,
  "onlyNewNotices": true,
  "stateKey": "default",
  "maxNotices": 50,
  "language": "en",
  "includeDescription": true
}
```

# Actor output Schema

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

The run's rows in the default dataset, as JSON.

# 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 = {
    "keywords": [
        "software"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("ftrytech/ted-tenders-feed").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 = { "keywords": ["software"] }

# Run the Actor and wait for it to finish
run = client.actor("ftrytech/ted-tenders-feed").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 '{
  "keywords": [
    "software"
  ]
}' |
apify call ftrytech/ted-tenders-feed --silent --output-dataset

```

## MCP server setup

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

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/YXOb1Vb2mReELoPuf/builds/WQ18hk7agjXI0AtTl/openapi.json
