# EU & UK Public Tenders - TED, Find a Tender & Contracts Finder (`borasipahi/eu-uk-tenders-scraper`) Actor

Procurement notices from EU TED, UK Find a Tender and Contracts Finder in one normalised schema: CPV, country, deadline, EUR value, buyer, winner. Contact details stripped by default. Handles TED's full corpus.

- **URL**: https://apify.com/borasipahi/eu-uk-tenders-scraper.md
- **Developed by:** [Bora Sipahi](https://apify.com/borasipahi) (community)
- **Categories:** Business, Lead generation, News
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-usage

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

Public procurement in Europe is published in three places a bidder has to watch at once: **TED** (Tenders Electronic Daily, every above-threshold notice in the EU and EEA), **Find a Tender** (UK above-threshold) and **Contracts Finder** (UK below-threshold and central government). Each has its own API, its own schema and its own quirks. This Actor reads all three and returns one flat, consistent record per notice: what is being bought, by whom, in which country, under which CPV code, for how much in EUR, with what deadline, and who won if it is an award.

It is built for the daily job a bid team or a procurement-intelligence product actually runs: filter by CPV and country, keep only open contract notices with a deadline still ahead, deliver only what is new since yesterday.

### What makes it different

- **Whole corpus, not the first 15,000.** TED's search API stops at 15,000 results unless you use its iteration mode. This Actor uses iteration mode, so a wide query or a historical backfill returns everything.
- **One schema across EU and UK.** UK data is published in the Open Contracting Data Standard, TED in its own eForms field set. Both are mapped to the same fields, with values converted to EUR for comparison.
- **Multilingual handled.** TED publishes titles and buyer names per language. You choose a language; the Actor falls back to English and then to the notice's own language, so `title` is always a string.
- **Personal data stripped by default.** Notices contain officials' e-mail addresses and phone numbers, and TED even carries beneficial-owner data. The Actor never outputs beneficial-owner fields or contact-person names, and only outputs the buyer organisation's e-mail and phone when you switch **Include buyer contact details** on.
- **Polite to the registers.** TED throttles on concurrency; the Actor uses one connection at about one request a second with backoff, which still moves hundreds of notices a second.

### How to use it

1. Pick **Sources**. All three are on by default.
2. Narrow with **Notice types** (open contract notices, awards, prior information, modifications), **CPV codes** (prefixes like `72` for IT or full codes), **Countries** and a **Search query**.
3. Set the **Published from / to** window. Default is the last 7 days.
4. To drop expired tenders, set **Submission deadline from** to today.
5. Run, then download JSON, CSV or Excel, or read the dataset through the API.

For a daily feed, turn on **Incremental mode**, add a Schedule, and connect a Slack, e-mail or webhook integration. Only notices not delivered before are charged and returned.

### Output fields

| Field | Meaning |
| --- | --- |
| `source`, `sourceAttribution` | `ted`, `uk-find-a-tender` or `uk-contracts-finder`, plus the attribution the register requires |
| `noticeId`, `publicationNumber`, `ocid` | Stable ids; `ocid` links the stages of one procedure |
| `noticeType`, `noticeTypeRaw` | `contract-notice`, `contract-award`, `prior-information`, `modification`, and the register's own code |
| `title`, `descriptionSnippet` | Title in your chosen language and the first 300 characters of the description |
| `buyerName`, `buyerCountry`, `buyerCity`, `buyerNutsCode`, `buyerWebsite` | The contracting authority (ISO alpha-3 country) |
| `cpvCodes[]`, `cpvMain`, `contractNature`, `procedureType`, `legalBasis` | Classification |
| `estimatedValue`, `estimatedValueCurrency`, `estimatedValueEur` | Estimated value as published and converted to EUR |
| `awardedValue`, `awardedValueCurrency`, `awardedSupplierName`, `awardedSupplierCountry` | Award details when present |
| `publishedDate`, `dispatchDate`, `submissionDeadline`, `contractStartDate`, `contractEndDate` | ISO timestamps |
| `placeOfPerformanceCountries[]`, `lotsCount`, `ojsNumber`, `language` | Extras |
| `noticeUrl` | Link to the official notice page |
| `buyerEmail`, `buyerPhone` | Only when **Include buyer contact details** is on |
| `description`, `lots[]`, `documentUrls[]` | Only when **Include full notice** is on |
| `scrapedAt` | Run timestamp |

### Pricing

- `notice-record`, once per notice row saved. Filtered-out and already-delivered notices are never charged.
- `notice-detail`, once per row when **Include full notice** is on: full description, every lot with its own value and deadline, document links.

Set **Maximum notices** to cap a run. No platform usage is passed on to you.

### Input example

```json
{
    "sources": ["ted", "uk-find-a-tender"],
    "noticeTypes": ["contract-notice"],
    "cpvCodes": ["72", "48"],
    "countries": ["DE", "NL", "GB"],
    "deadlineFrom": "2026-09-07",
    "minValueEur": 100000,
    "language": "en",
    "includeFullNotice": true,
    "incremental": true,
    "maxItems": 500
}
```

### Output example

```json
{
    "source": "ted",
    "sourceAttribution": "Source: Tenders Electronic Daily (TED), © European Union, 1998-2026, https://ted.europa.eu. Reused under Commission Decision 2011/833/EU.",
    "noticeId": "ted:596460-2026",
    "publicationNumber": "596460-2026",
    "noticeType": "contract-notice",
    "title": "Projekt 1034: Technische Umsetzung einer SBOM für KI (SBOM4AI)",
    "buyerName": "Bundesamt für Sicherheit in der Informationstechnik",
    "buyerCountry": "DEU",
    "buyerCity": "Bonn",
    "cpvCodes": ["72000000"],
    "cpvMain": "72000000",
    "procedureType": "open",
    "estimatedValue": 250000,
    "estimatedValueCurrency": "EUR",
    "estimatedValueEur": 250000,
    "publishedDate": "2026-09-01T00:00:00.000Z",
    "submissionDeadline": "2026-10-02T00:00:00.000Z",
    "noticeUrl": "https://ted.europa.eu/en/notice/-/detail/596460-2026",
    "scrapedAt": "2026-09-07T01:00:00.000Z"
}
```

### Data sources and reuse

- **TED** procurement notices may be freely reused for commercial and non-commercial purposes under Commission Decision 2011/833/EU, with acknowledgement of the source. Every TED row carries that acknowledgement in `sourceAttribution`. TED and EU logos are not reused. Only the public search API is used; the Actor does not crawl the TED website or its bulk packages.
- **Find a Tender** and **Contracts Finder** publish under the Open Government Licence v3.0. Every UK row carries the required statement.
- Reuse rights are not a licence to process personal data. Officials' names, beneficial-owner fields and contact-person details are never output; organisational e-mail and phone are opt-in.

### Related Actors

- Combine with a CPV-to-keyword mapping or an LLM step downstream to score notices for fit.
- Use `ocid` to join a contract notice with its later award in your own database.

### FAQ

**Why did TED return fewer notices than the UK sources?** Filters are applied on the TED server, so you only receive matching notices. UK filtering happens in the Actor after download, which is why the run log shows many filtered rows for the UK registers.

**Can I get the full text of the tender documents?** No. `documentUrls` links to the buyer's own procurement portal where the documents live; those portals are outside the scope of this Actor.

**How accurate is the EUR conversion?** It uses recent reference rates and is meant for filtering and sorting, not accounting. The original amount and currency are always included.

**Something broke?** Open an issue on the Actor's Issues tab. All three APIs are checked several times a day and the Actor is updated when a register changes its payload.

# Actor input Schema

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

Which registers to read. TED covers all EU and EEA above-threshold procurement; Find a Tender covers UK above-threshold; Contracts Finder covers UK below-threshold and central government.

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

Keep only these stages. Contract notices are open opportunities; awards tell you who won and for how much; prior information notices are early warnings.

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

Common Procurement Vocabulary prefixes, e.g. \["72"] for IT services, \["45"] for construction, \["72200000"] for an exact code. A notice matches if any of its CPV codes starts with one of these.

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

ISO country codes, two or three letters, e.g. \["DE", "FR", "GB"]. Matched against the buyer's country.

## `searchQuery` (type: `string`):

Full-text keywords. On TED this is sent to the register's own search; on the UK sources it is matched against title and description.

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

Earliest publication date, YYYY-MM-DD. Defaults to 7 days ago.

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

Latest publication date, YYYY-MM-DD. Defaults to today.

## `deadlineFrom` (type: `string`):

Keep only notices whose submission deadline is on or after this date, YYYY-MM-DD. Use today's date to drop expired tenders.

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

Keep only notices whose estimated or awarded value, converted to EUR, is at least this. Notices with no value are dropped when this is set.

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

Two-letter code for the language to use when a TED field exists in several languages. Falls back to English, then the notice's own language.

## `includeContactDetails` (type: `boolean`):

Add the buyer organisation's e-mail address and telephone number. Off by default because these fields often identify individual officials. Contact person names and beneficial-owner data are never included.

## `includeFullNotice` (type: `boolean`):

Add the full description, every lot with its own value and deadline, and document links. Charged as notice-detail on top of notice-record.

## `maxItems` (type: `integer`):

Stop after this many notices. Bounds your cost.

## `incremental` (type: `boolean`):

Remember every notice already delivered and skip it next time. Use with a daily schedule.

## `incrementalStoreName` (type: `string`):

Key-value store that holds delivered notice ids.

## Actor input object example

```json
{
  "sources": [
    "ted",
    "uk-find-a-tender",
    "uk-contracts-finder"
  ],
  "noticeTypes": [
    "contract-notice"
  ],
  "cpvCodes": [
    "72"
  ],
  "countries": [],
  "language": "en",
  "includeContactDetails": false,
  "includeFullNotice": false,
  "maxItems": 100,
  "incremental": false,
  "incrementalStoreName": "eu-uk-tenders-seen"
}
```

# Actor output Schema

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

No description

## `noticesCsv` (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 = {
    "sources": [
        "ted",
        "uk-find-a-tender",
        "uk-contracts-finder"
    ],
    "noticeTypes": [
        "contract-notice"
    ],
    "cpvCodes": [
        "72"
    ],
    "maxItems": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("borasipahi/eu-uk-tenders-scraper").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 = {
    "sources": [
        "ted",
        "uk-find-a-tender",
        "uk-contracts-finder",
    ],
    "noticeTypes": ["contract-notice"],
    "cpvCodes": ["72"],
    "maxItems": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("borasipahi/eu-uk-tenders-scraper").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 '{
  "sources": [
    "ted",
    "uk-find-a-tender",
    "uk-contracts-finder"
  ],
  "noticeTypes": [
    "contract-notice"
  ],
  "cpvCodes": [
    "72"
  ],
  "maxItems": 100
}' |
apify call borasipahi/eu-uk-tenders-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,borasipahi/eu-uk-tenders-scraper"
        }
    }
}

```

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/XLHSRN15I1CcThXFi/builds/iknQkDVXrFJhNRZHu/openapi.json
