# Public Tenders Search: EU TED & UK Procurement API (`plainrows/public-tenders-search`) Actor

Find public tenders, government contracts, contract notices and award winners from EU TED, UK Find a Tender and UK Contracts Finder in one search: keywords, CPV, countries, value in EUR, deadlines, winners, official links. Only-new-notices mode for tender alerts. Pay per notice.

- **URL**: https://apify.com/plainrows/public-tenders-search.md
- **Developed by:** [PlainRows](https://apify.com/plainrows) (community)
- **Categories:** Lead generation, Business, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

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

## Public Tenders Search: EU TED & UK Procurement API

Search **public procurement notices from the EU and the UK in one run**: open **tenders and contract notices**, **contract awards with winners and values**, and **prior information / pipeline notices**, from **TED (Tenders Electronic Daily)**, **UK Find a Tender** and **UK Contracts Finder**. Filter by keywords, **CPV codes**, buyer countries, publication date, **value in euros** and open deadlines.

Built for **B2B sales teams, bid consultants and market analysts** who track government contracts: one clean row per notice with buyer, CPV labels, estimated and awarded values, deadline, winners and the official link. It uses the **official open APIs** of the three platforms (no browser, no proxies), removes duplicates published on two UK platforms, and **you only pay for notices returned**.

### Typical uses

- **Bid opportunity alerts**: every new call for tenders in your sector (CPV) and countries, still open for bids, sent daily to Sheets, Slack or your CRM. With **Only new notices** on, each notice is delivered and billed once.
- **Competitor and market intelligence**: who wins which contracts, at what value, for which buyers; awarded values converted to EUR for comparison across countries.
- **Lead generation**: award winners are companies that just got funded work (subcontracting, suppliers, financing); buyers with prior information notices are about to buy.
- **Pipeline research**: prior information and market engagement notices, months before the tender.
- **AI agents and automations**: a typed tool for "find tenders about X in Y" in n8n, Make, Zapier or any MCP client.

### Sources

| Source | Coverage | How it is searched |
|---|---|---|
| **TED** (`ted`) | All notices published in the Supplement to the Official Journal of the EU: EU and EEA buyers above the EU thresholds, plus some neighbouring countries | TED's own search engine (full text in all languages, CPV, country, notice type, dates) |
| **UK Find a Tender** (`uk-find-a-tender`) | UK notices above the procurement thresholds (Procurement Act 2023 and older regimes, incl. Scotland) | The run reads the official OCDS feed day by day, newest first (last 31 days at most), and matches your keywords in title, description, lots, buyer and CPV labels |
| **UK Contracts Finder** (`uk-contracts-finder`) | Lower-value public contracts, mainly in England, and many above-threshold UK notices | Contracts Finder's search API, then the same keyword rule |

Find a Tender has no search engine and throttles heavy readers (it asks to wait 2 minutes after a few dozen pages): a run reads it for up to about 3 minutes, which normally covers 7 to 10 days of notices. If the range could not be read completely, the run says from which date it was searched; the other two sources always cover the whole range.

A notice found on both UK platforms is returned **once** (Find a Tender version) with `alsoPublishedOn: ["uk-contracts-finder"]`. A notice matching several of your keywords is returned and billed once.

### What you get for each notice

| Field | Description |
|---|---|
| `source`, `noticeId`, `noticeUrl` | Platform, notice number and the **official notice page** |
| `noticeType`, `noticeTypeCode` | `contract-notice`, `award`, `prior-information` or `other`, and the code as published (`cn-standard`, `can-standard`, `UK4`, `Contract/Awarded`...) |
| `title`, `description` | Title and description in the notice language (description up to 2,000 characters, e-mail addresses removed) |
| `buyerName`, `buyerCity`, `buyerCountry` | Contracting authority, city and ISO country code |
| `procedureType`, `contractType` | Open, restricted, negotiated, competitive flexible...; `supplies`, `services` or `works` |
| `mainCpvCode`, `mainCpvLabel`, `cpvCodes`, `cpvLabels` | CPV codes with their **English labels** |
| `estimatedValue`, `currency` | Estimated value excluding VAT, as published |
| `awardValue`, `awardCurrency`, `winnerNames` | Awarded value and **winning companies** (awards) |
| `valueEur` | Estimated (or awarded) value converted to EUR with the ECB reference rate of the day (approximate, for filtering and sorting) |
| `publicationDate`, `submissionDeadline` | Publication date and deadline for tenders or requests to participate (earliest lot) |
| `lotsCount`, `documentsUrl` | Number of lots and the procurement documents / e-tendering portal link when published |
| `procurementId`, `language`, `alsoPublishedOn` | Procedure ID linking notices of the same procurement, notice language, other platforms with the same notice |
| `searchKeyword`, `observedAt` | Your keyword and when the notice was read |

Missing values are `null`, never guessed. Notices are sorted newest first; on the same day the sources alternate.

### Input

```json
{
  "keywords": ["software", "\"cyber security\""],
  "noticeTypes": ["contract-notice", "award"],
  "countries": ["FR", "DE", "BE", "GB"],
  "cpvCodes": ["72000000", "48000000"],
  "publishedWithinDays": 14,
  "minValueEur": 100000,
  "openForBidsOnly": false,
  "maxResults": 200
}
```

- **keywords**: one search per line; **all words must appear** (`cloud hosting`), or put the line in double quotes for an **exact phrase**. TED searches the notice in every language it is published in, so English words also find notices whose CPV labels or translated titles match; add local-language words (`logiciel`, `Software`, `oprogramowanie`) for better recall. Leave empty to get **every notice** matching the filters.
- **noticeTypes**: `contract-notice` (open calls for tenders, default), `award` (winners and values), `prior-information` (planned procurements, market engagement), `other` (modifications, direct-award intentions, withdrawals).
- **countries**: buyer countries as two-letter codes (`FR`, `DE`, `IT`, `ES`, `PL`, `NL`, `GB`...). TED covers EU/EEA buyers, `GB` uses the UK sources. Empty = all.
- **cpvCodes**: a code covers its sub-codes (`72000000` = all IT services, `79340000` = advertising and marketing, `79400000` = consulting). Check digits (`72000000-8`) are accepted.
- **Dates**: `publishedWithinDays` (default 7) or `publishedFrom` / `publishedTo` (`YYYY-MM-DD`). Find a Tender is read over the last 31 days of the range at most (see Sources).
- **minValueEur / maxValueEur**: compares the estimated value (or the awarded value when there is no estimate), converted to EUR. Notices without a published value are left out when a value filter is set.
- **openForBidsOnly**: keeps contract notices whose submission deadline is still ahead.
- **sources**: any of `ted`, `uk-find-a-tender`, `uk-contracts-finder` (default: all three).
- **maxResults**: notices per keyword, 1 to 5,000 (default 100), newest first across sources.
- **includeDescription** (default true).
- **onlyNewNotices** (optional, monitoring): return only notices never delivered to you before. See below.
- **monitorName** (optional): name of the key-value store in your account that remembers delivered notices (default `plainrows-tenders-monitor`).

Keywords without any notice get one free row with `found: false` and a `note`.

### Tender alerts: get only new notices

Turn on **Only new notices** (`"onlyNewNotices": true`), keep `publishedWithinDays` a little longer than your schedule (for example 3 days for a daily run) and add an Apify **schedule**. The first run returns the matching notices and remembers their official IDs in a key-value store of your own Apify account; each later run returns **and bills only notices it has never delivered**, whatever the keyword that finds them. A keyword with nothing new gets a free row with `notFoundReason: "no-new-notices"`.

- Each run checks up to `maxResults` of the newest notices per keyword: keep it high enough to hold everything published between two runs.
- Notices are recognized by their official ID. A tender listed on two UK platforms counts once when both copies show up in the same run; if the second copy only appears in a later run, it can be delivered once more.
- Use a different **monitorName** per alert (for example one per client or sector) to keep separate histories, or a new name to start over.

### Output example

Real row from a run on 2026-10-07 (input `{"keywords": ["software"], "noticeTypes": ["contract-notice", "award"]}`; description shortened here):

```json
{
  "searchKeyword": "software",
  "found": true,
  "source": "ted",
  "noticeId": "692610-2026",
  "procurementId": "4451b1cf-57ce-4a21-bfeb-0a164e5abbfd",
  "noticeType": "contract-notice",
  "noticeTypeCode": "cn-standard",
  "title": "Renovación tecnológica balanceadores de enaire",
  "buyerName": "Dirección Económico-Financiera de ENAIRE",
  "buyerCity": "Madrid",
  "buyerCountry": "ES",
  "procedureType": "Open",
  "contractType": "supplies",
  "mainCpvCode": "32571000",
  "mainCpvLabel": "Communications infrastructure",
  "cpvCodes": [
    "32571000"
  ],
  "cpvLabels": [
    "Communications infrastructure"
  ],
  "estimatedValue": 609878.23,
  "currency": "EUR",
  "awardValue": null,
  "awardCurrency": null,
  "valueEur": 609878,
  "winnerNames": [],
  "publicationDate": "2026-10-07",
  "submissionDeadline": "2026-11-10T23:59:00+01:00",
  "lotsCount": 1,
  "noticeUrl": "https://ted.europa.eu/en/notice/-/detail/692610-2026",
  "documentsUrl": "https://contrataciondelestado.es/wps/poc?uri=deeplink:detalle_licitacion&idEvl=XKMnaxJytG3I8aL3PRS10Q%3D%3D",
  "language": "es",
  "description": "Actualmente la red de Sistemas de Información de ENAIRE (RSINA) está utilizando equipos controladores de distribución de aplicaciones (Application Delivery controllers ¿ ADC) del fabricante F5 para el balanceo a nivel de red, transporte y...",
  "alsoPublishedOn": [],
  "observedAt": "2026-10-07T17:05:11.436Z",
  "schemaVersion": "1.0"
}
```

### Pricing

Pay per notice returned, no subscription. See the **Pricing** tab for current prices: one event per **notice found**, cheaper on higher Apify plans, plus a small fixed **start fee per run** (Apify's "Actor start" event), which pays for reading UK Find a Tender (up to 3 minutes per run, whatever the number of notices). Schedule one run with many keywords rather than one run per keyword. CPV labels, values in EUR, winners, deadlines and descriptions are all included in that price (no "details" or "attachments" add-on).

Keywords without results, notices removed by your filters, duplicates found on two platforms or for two keywords, and input mistakes cost nothing beyond the start fee. A run whose keywords keep finding nothing stops early and says so. The run never spends more than the maximum cost you set.

**Free Apify plan:** each run returns up to 20 notices for up to 2 keywords, so you can check the output. A paid Apify plan removes these limits.

### Use it from code, automations or AI agents

**API (one call, results returned directly):**

```bash
curl -X POST "https://api.apify.com/v2/acts/plainrows~public-tenders-search/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"keywords": ["software"], "noticeTypes": ["contract-notice"], "countries": ["FR", "BE"], "publishedWithinDays": 7, "openForBidsOnly": true}'
```

**n8n, Make, Zapier:** use the Apify integration, pick `plainrows/public-tenders-search`, schedule it daily with `publishedWithinDays: 1` and push new notices to Google Sheets, Airtable, Slack or your CRM (`source` + `noticeId` is a stable key for de-duplication).

**AI agents (MCP):** add `https://mcp.apify.com?tools=plainrows/public-tenders-search` to Claude, Cursor or any MCP client. The agent gets a typed tool and pays per result.

### What this is not

- **Not US federal (SAM.gov), not national-only portals** (BOAMP, Vergabe portals, PLACSP...) for notices below the EU thresholds. TED holds the above-threshold notices of all EU countries.
- **No tender documents**: the row links to the official notice and, when published, to the documents portal. Documents and bid submission stay on the buyer's platform.
- **No personal data**: contact persons, e-mails and phone numbers are not exported; e-mail addresses are removed from descriptions; winner names that are clearly individuals (sole traders) are left out.
- Values are as published by buyers (sometimes rounded, framework maximums or missing); `valueEur` is an approximate conversion.
- Not affiliated with the Publications Office of the EU, the Cabinet Office or Crown Commercial Service.

### Data licences

- TED notices: "the procurement notices ... can be freely reused, for commercial or non-commercial purposes" ([TED legal notice](https://ted.europa.eu/en/legal-notice)). Source: TED, Publications Office of the European Union.
- UK notices: Contains public sector information licensed under the [Open Government Licence v3.0](https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/) (Find a Tender, Contracts Finder).
- CPV labels: eForms SDK codelists, Publications Office of the EU, CC BY 4.0. Exchange rates: ECB euro reference rates.

### Run summary

Each run stores a summary in the key-value store (`OUTPUT`): sources searched and any source that failed, the date range, requests per source, Find a Tender notices scanned, notices removed by filters, duplicates across sources and keywords, the exchange-rate date, invalid or duplicate keywords and anything skipped because of the free-plan or budget limit. If one source is temporarily down, the others are still delivered and the run says so.

### FAQ

**How do I find public tenders in my sector?**
Enter keywords and/or **cpvCodes** (for example `72000000` for IT services), choose buyer **countries**, and run: one row per notice with buyer, value, deadline and link.

**Which sources are covered?**
EU TED (Tenders Electronic Daily), UK Find a Tender and UK Contracts Finder, in one run.

**Can I see who won a contract?**
Yes: set **noticeTypes** to `award` to get winners and awarded values.

**Can I keep only tenders still open?**
Yes: **openForBidsOnly** keeps contract notices whose submission deadline is still ahead.

**Are US (SAM.gov) or national-only portals included?**
No: TED holds the above-threshold notices of all EU countries; national portals for smaller contracts are not covered.

### More PlainRows tools

Same pay-per-result pricing, same clean JSON, built to chain together in n8n, Make, Zapier or AI agents.

- **SEO and keywords:** [Keyword Search Volume, CPC & Difficulty](https://apify.com/plainrows/keyword-search-volume) · [Keyword Ideas Generator](https://apify.com/plainrows/keyword-ideas-generator) · [Google Autocomplete & People Also Ask](https://apify.com/plainrows/google-autocomplete-questions) · [Google Trends Explorer](https://apify.com/plainrows/google-trends-explorer) · [Website Organic Traffic & Top Keywords](https://apify.com/plainrows/domain-seo-overview) · [Bulk Domain Authority Checker](https://apify.com/plainrows/domain-authority-checker) · [Backlink Checker](https://apify.com/plainrows/backlink-checker) · [Broken Link Checker](https://apify.com/plainrows/broken-link-checker) · [AI Search Visibility Tracker (ChatGPT, Gemini, AI Mode)](https://apify.com/plainrows/ai-search-brand-visibility)
- **Leads and companies:** [Google Maps Scraper](https://apify.com/plainrows/google-maps-businesses) · [Website Technology Checker](https://apify.com/plainrows/website-tech-stack) · [ATS Jobs Scraper (Greenhouse, Lever, Ashby, SmartRecruiters)](https://apify.com/plainrows/ats-job-postings) · [Google Jobs Scraper](https://apify.com/plainrows/google-jobs-scraper) · [Google Ads Transparency Scraper](https://apify.com/plainrows/google-ads-transparency) · [Company Registry Lookup (SIRENE, LEI, Brreg)](https://apify.com/plainrows/company-registry-lookup)
- **E-commerce and travel:** [Amazon Product Search Scraper](https://apify.com/plainrows/amazon-product-search) · [Google Shopping Prices Scraper](https://apify.com/plainrows/google-shopping-prices) · [Google Hotels Prices Scraper](https://apify.com/plainrows/google-hotels-prices)
- **Content and media:** [Google News Scraper](https://apify.com/plainrows/google-news-scraper) · [YouTube Search Scraper](https://apify.com/plainrows/youtube-search-scraper) · [Google Images Scraper](https://apify.com/plainrows/google-images-scraper) · [Website Screenshot API](https://apify.com/plainrows/website-screenshot) · [App Reviews Scraper (Google Play & App Store)](https://apify.com/plainrows/app-reviews-scraper)

### Support

Missing a source, a field or found a bug? Open an issue on this page. Issues are read and answered.

# Actor input Schema

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

One search per line, matched in the notice text (title, description, buyer, CPV labels). All words must appear; put a line in double quotes for an exact phrase ("cyber security"). TED also matches translations and words in the notice's own language, so add local-language terms for better coverage (e.g. logiciel, Software). Leave empty to get every notice matching the filters below. Up to 100 keywords.

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

contract-notice = open calls for tenders (bid opportunities); award = contract awards with winners and values; prior-information = planned procurements and market engagement; other = modifications, direct-award intentions (VEAT), withdrawals.

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

Two-letter codes of the buyer's country, e.g. FR, DE, IT, ES, PL, NL, GB. Empty = all countries. TED covers EU/EEA buyers (and some neighbours); GB uses the two UK sources. Supported: AT, BE, BG, HR, CY, CZ, DK, EE, FI, FR, DE, GR, HU, IE, IT, LV, LT, LU, MT, NL, PL, PT, RO, SK, SI, ES, SE, IS, LI, NO, CH, GB, AL, BA, ME, MK, RS, XK, MD, UA, GE, TR, AD, MC, SM, AM, AZ.

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

Common Procurement Vocabulary codes; a code covers its sub-codes (72000000 = all IT services, 79340000 = advertising and marketing). Check digits like "-8" are accepted. A notice matches when any of its CPV codes matches.

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

Ignored when "Published from" is set.

## `publishedFrom` (type: `string`):

First publication date (YYYY-MM-DD). UK Find a Tender is searched over the last 31 days of the range at most.

## `publishedTo` (type: `string`):

Last publication date (YYYY-MM-DD). Default: today.

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

Keeps notices whose estimated value (or awarded value when there is no estimate) is at least this amount, converted to euros with the ECB reference rates of the day. Notices without a published value are removed when a value filter is set.

## `maxValueEur` (type: `integer`):

Upper bound, same rules as the minimum value.

## `openForBidsOnly` (type: `boolean`):

Keeps contract notices whose submission deadline is in the future.

## `sources` (type: `array`):

Official platforms to search. Empty = all three.

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

Newest first, across all sources. Each notice returned is billed; see Pricing.

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

Adds the notice description (up to 2,000 characters, e-mail addresses removed).

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

Return only notices this tool has never delivered to you (whatever the keyword). The first run returns the matching notices and remembers them; later runs (for example a daily schedule) return and bill only new notices.

## `monitorName` (type: `string`):

Name of the key-value store in your Apify account that remembers delivered notices (default "plainrows-tenders-monitor"). Use a different name per alert to keep separate histories, or a new name to start over. Lowercase letters, digits and hyphens.

## Actor input object example

```json
{
  "keywords": [
    "software"
  ],
  "noticeTypes": [
    "contract-notice"
  ],
  "countries": [],
  "publishedWithinDays": 7,
  "openForBidsOnly": false,
  "sources": [
    "ted",
    "uk-find-a-tender",
    "uk-contracts-finder"
  ],
  "maxResults": 50,
  "includeDescription": true,
  "onlyNewNotices": false
}
```

# Actor output Schema

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

No description

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

No description

# 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"
    ],
    "noticeTypes": [
        "contract-notice"
    ],
    "countries": [],
    "publishedWithinDays": 7,
    "maxResults": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("plainrows/public-tenders-search").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"],
    "noticeTypes": ["contract-notice"],
    "countries": [],
    "publishedWithinDays": 7,
    "maxResults": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("plainrows/public-tenders-search").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"
  ],
  "noticeTypes": [
    "contract-notice"
  ],
  "countries": [],
  "publishedWithinDays": 7,
  "maxResults": 50
}' |
apify call plainrows/public-tenders-search --silent --output-dataset

```

## MCP server setup

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

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/h1XDLCQ6bEdAoUDBz/builds/C9hSmlYM4lVBrtDao/openapi.json
