# Public Tenders Scraper - UK & EU Tenders and Contract Awards (`scrapefy/public-tenders-scraper`) Actor

Search public tenders and contract awards from UK Find a Tender, UK Contracts Finder and EU TED in one feed: keywords, CPV codes, countries, dates. Official open data, no login.

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

## Pricing

Pay per event

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

## Public Tenders Scraper: UK and EU tenders and contract awards

Search **public tenders and contract awards** from three official open-data sources in one feed: **EU TED (Tenders Electronic Daily), UK Find a Tender and UK Contracts Finder**. Add keywords, CPV codes, buyer countries and a date range, and every notice comes back as one clean row: **title, description, buyer, country, value and currency, deadline, CPV codes, procedure, the winning suppliers and award value for contract awards, and a link to the official notice**. Export to JSON, CSV or Excel, or call it from the API.

Use it for **bid and tender monitoring, sales lead generation from new public contracts, competitor and market intelligence (who wins what, for how much), procurement analytics, and datasets for LLM and analytics pipelines**. Works with AI agents through the Apify MCP server. All data comes from the governments' own open-data APIs; no login, no cookies and no proxy are needed.

### Why this one

- **Three official sources, one schema.** EU TED (all EU countries), UK Find a Tender and UK Contracts Finder return the same fields, so you can compare and merge them.
- **Tenders and awards.** Pick open tenders, contract awards (winners and values) or everything.
- **Filter by keyword, CPV code, country and date.** CPV codes ending in zeros match everything below them (72000000 = all IT services).
- **Cheap and predictable.** $0.0015 per notice; `Max notices per source` caps every run.
- **Never fails the whole run for one input.** A bad country code, CPV code, date or source gets its own row with an `error` message and is **not charged**; the rest continue. A `RUN_SUMMARY` record lists notices per source.
- **Polite by design.** Pauses between requests, retries with backoff, honours Retry-After, and stops politely if a server keeps answering 403 or 429.

### Input

| Field | Meaning |
|---|---|
| Sources | `ted`, `findATender`, `contractsFinder` (default all three) |
| Keywords | Optional; a notice matches if it contains any of them |
| CPV codes | Optional, for example `72000000` |
| Buyer countries (TED) | Optional 3-letter ISO codes: `DEU`, `FRA`, `POL`. UK sources run only if `GBR` is listed or the list is empty |
| Notice stage | `all` (default), `tenders`, `awards` |
| Days back / Published from / Published to | Publication window (default last 7 days) |
| Max notices per source | Default 100, max 5,000 |
| Max pages scanned | UK keyword/CPV search reads pages of 100 notices (default 30 pages) |
| Include buyer contact details | Off by default (personal data) |

### Output

**Notice row:** `type: "notice", source, noticeId, ocid, url, title, description, noticeType, status, publishedDate, deadline, buyerName, buyerCountry, buyerLocality, buyerUrl, valueAmount, valueCurrency, cpvCodes[], procurementMethod, category, region, winners[], awardValue, awardCurrency, awardDate, contractStart, contractEnd, suitableForSme, matchedKeyword, scrapedAt` (plus `buyerContactName/Email/Phone` when enabled).

**Error row:** `type: "error", source, input, error, scrapedAt`. See `sample-output.json` for real rows.

Notes on the data:

- Fields are as published by the buyer: deadline, value and CPV are empty when the notice does not contain them (most often in award notices and in Find a Tender).
- TED titles and descriptions are in the buyer's language (English when available). TED `noticeType` is the eForms notice type, for example `cn-standard` (call for competition) or `can-standard` (contract award).
- UK sources cannot filter by words on the server side: with keywords or CPV codes the Actor reads up to `Max pages scanned` pages of 100 notices from your date range and keeps the matches, so use a narrow date range for rare words.
- Keywords and CPV codes together mean both must match.

### Pricing

Pay per event: **$0.0015 per notice** ($1.50 per 1,000 notices) plus a $0.00005 start event per run. Error rows are not charged. A run returning 100 notices costs about 15 cents. See PRICING.md for the benchmark.

### Public data only

The Actor reads only public tender and award notices that governments publish as open data through their official APIs (Find a Tender and Contracts Finder OCDS APIs, TED Search API). It does **not** log in, use cookies, or bypass anything. Notices can contain names of contact persons at public bodies and the names of winning companies or sole traders; contact details are off by default and are personal data under GDPR and similar laws. You are responsible for using the data in line with the sources' licences (UK Open Government Licence, EU reuse policy for TED) and applicable data-protection law.

### Notes and limits

- Not an official API client of the governments; formats can change without notice.
- TED keyword search is full text on the server side; UK keyword search is a contains-check on title, description and buyer name.
- Only above-threshold and published notices exist in these sources; national portals below EU thresholds are not covered.

### FAQ

**How much does it cost?** $0.0015 per notice plus $0.00005 per run. 1,000 notices = $1.50. Error rows are free.

**Do I need an API key or account?** No.

**Can I get only contract awards with winners?** Yes: set Notice stage to `awards`.

**Which countries does TED cover?** All EU member states plus EEA and some others; filter with 3-letter codes such as DEU, FRA, ITA.

**Which export formats are available?** JSON, CSV, Excel, XML, HTML or RSS from the Console or API, or integrations such as Make, Zapier, n8n and Google Sheets.

**Something broke or a field is missing?** Open an issue on the Actor's Issues tab with the run link.

### Use with AI agents / MCP

Agents can call this Actor through the [Apify MCP server](https://mcp.apify.com). Let the agent find it with the `search-actors` tool ("public tenders scraper UK EU") and call it by name:

- Actor: `santis_hermes/public-tenders-scraper`
- Example prompt: "Find open UK and EU tenders mentioning software published in the last 7 days."
- Minimal input:

```json
{
  "sources": ["ted", "findATender", "contractsFinder"],
  "keywords": ["software"],
  "noticeStage": "tenders",
  "daysBack": 7,
  "maxItems": 100
}
```

### Feedback

If this saved you time, a rating on the Store page helps others find it. If it did not work for you, please open an issue on the Issues tab with the run link and I will look at it.

# Actor input Schema

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

Which official sources to search: ted (EU Tenders Electronic Daily, all EU countries), findATender (UK above-threshold notices) and contractsFinder (UK notices, lower values too). Leave all three for one combined feed.

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

Optional words or phrases, for example 'cloud hosting' or 'road maintenance'. A notice matches if it contains any of them. TED searches the full notice text; for the UK sources the title, description and buyer name are checked. Leave empty to get everything in the date range.

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

Optional Common Procurement Vocabulary codes, for example 72000000 (IT services) or 45000000 (construction). A code ending in zeros also matches everything below it. Combined with keywords, a notice must match both.

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

Optional 3-letter ISO country codes of the buying authority, for example DEU, FRA, ITA, POL. Applies to TED. UK sources are included only if GBR is listed or this is empty.

## `noticeStage` (type: `string`):

Tenders returns notices that invite bids, awards returns notices that name the winning suppliers and the contract value, and everything returns both plus other notice types.

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

Publication window in days counted back from today. Ignored when 'Published from' is set.

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

Optional first publication date, format YYYY-MM-DD, for example 2026-10-01. Overrides 'Days back'.

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

Optional last publication date, format YYYY-MM-DD. Default is today.

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

Maximum number of notices returned from each source. You are charged per notice returned, so this keeps the cost of a run predictable.

## `maxScanPages` (type: `integer`):

The UK sources cannot filter by words on their side, so the actor reads pages of 100 notices and filters them. This caps how many pages are read per UK source when keywords or CPV codes are set (30 pages = 3,000 notices).

## `includeContacts` (type: `boolean`):

Adds the contact person name, email and phone that UK buyers publish in their notices. Off by default because these are personal data; only enable it if you have a lawful reason to process them.

## Actor input object example

```json
{
  "sources": [
    "ted",
    "findATender",
    "contractsFinder"
  ],
  "keywords": [
    "software"
  ],
  "cpvCodes": [],
  "countries": [],
  "noticeStage": "all",
  "daysBack": 7,
  "maxItems": 100,
  "maxScanPages": 30,
  "includeContacts": false
}
```

# Actor output Schema

## `tenders` (type: `string`):

All notices as a table with the key columns.

## `awards` (type: `string`):

Notices with the winning suppliers and values.

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

Every row with all fields.

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

Notices per source, errors, requests made and charged events.

# 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",
        "findATender",
        "contractsFinder"
    ],
    "keywords": [
        "software"
    ],
    "cpvCodes": [],
    "countries": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapefy/public-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",
        "findATender",
        "contractsFinder",
    ],
    "keywords": ["software"],
    "cpvCodes": [],
    "countries": [],
}

# Run the Actor and wait for it to finish
run = client.actor("scrapefy/public-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",
    "findATender",
    "contractsFinder"
  ],
  "keywords": [
    "software"
  ],
  "cpvCodes": [],
  "countries": []
}' |
apify call scrapefy/public-tenders-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapefy/public-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/BCKtMLmLf4rDwCmFe/builds/LdCRxLL90V23ZE0tM/openapi.json
