# UK Government Tenders — Find a Tender & Contracts Finder API (`plumbline-data/uk-public-tenders`) Actor

UK government tenders, contract opportunities and awards from Find a Tender + Contracts Finder in one de-duplicated feed. Filter by keyword, CPV, value, region, SME; get deadlines, buyer contacts, values, winners. Official open data (OGL). Daily tender alerts.

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

## Pricing

from $3.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.
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

## UK Government Tenders & Contracts — Find a Tender + Contracts Finder in one feed

### What does UK Public Tenders do?

**UK Public Tenders** gives you **every UK public-sector tender, contract opportunity and contract award** from the government's two official portals — **Find a Tender** (the UK's central procurement platform — since the Procurement Act 2023 took effect in February 2025 it carries new notices above *and* below threshold) and **Contracts Finder** (England's long-running portal for lower-value contracts and procurements begun under the old rules) — in **one clean, de-duplicated table**. Filter by **keyword, CPV code, value, region, stage and SME suitability**, and get each notice's **deadline, days left, buyer name and contact, estimated value, CPV codes, lots, awarded suppliers and links to tender documents**.

It uses the official **OCDS open-data APIs** published under the **Open Government Licence** — no login, no scraping of web pages. The Actor handles the APIs' strict rate limits, paging, version de-duplication and the messy OCDS JSON for you.

**Try it:** type a keyword like `roofing` or `cyber security` and click **Start**.

### Why use this UK tender scraper?

- 🇬🇧 **Both portals in one feed** — Find a Tender + Contracts Finder, harmonised into the same fields
- 🔎 **Real filters** — keywords (title + description), exclude words, CPV prefixes (`4526` = roof works), min/max value, regions (NUTS/ITL), SME/VCSE-suitable only, open only
- 🧭 **Notice stages** — tenders (opportunities), planning/pre-market engagement, awards, contract changes, cancellations
- ⏳ **Bid-ready** — `tenderDeadline`, `daysToDeadline`, `isOpen`, buyer email/phone, document links
- 🏆 **Award intelligence** — winning suppliers and awarded values for competitor tracking
- 🧹 **De-duplicated** — the latest version of each procurement only (amendments update, not duplicate)
- 🐢 **Rate-limit aware** — waits out the portals' `429 Retry-After` instead of failing

### Who uses it?

- **Suppliers and SMEs** — a daily email/Slack/Sheets feed of new tenders in your trade and region
- **Bid writers and consultants** — monitor opportunities for many clients at once
- **Sales teams** — find public buyers spending on your category, and who they bought from before
- **Market analysts and journalists** — analyse public spending by sector, buyer or supplier

### How to get UK public tenders

1. Enter **keywords** (any match) and/or **CPV codes**.
2. Pick **notice stages** (default: *tender* = live opportunities) and a **date range** (`daysBack`, or exact from/to dates).
3. Optional: set **min value**, **regions**, **only open** or **SME suitable only**.
4. Click **Start**, then export to CSV/Excel/JSON — or **schedule a daily run** and connect it to Google Sheets, Slack, email, Make or Zapier.

### Input example

```json
{
  "keywords": ["roof", "cladding"],
  "excludeKeywords": ["framework extension"],
  "cpvCodes": ["4526"],
  "noticeStages": ["tender"],
  "sources": ["findATender", "contractsFinder"],
  "daysBack": 7,
  "minValue": 50000,
  "onlyOpen": true,
  "includeBuyerContact": true
}
```

### Output example

```json
{
  "source": "Find a Tender",
  "noticeId": "094348-2026",
  "ocid": "ocds-h6vhtk-0781db",
  "stage": "tender",
  "title": "Supply and instal doors to Hexton Barn, SG5 3HX",
  "description": "Supply and installation of roller shutter and pedestrian doors with infill cladding to barn at Manor Farm, Hexton, SG5 3HX. Site visit required. ...",
  "status": "active",
  "publishedDate": "2026-10-06T15:03:12+01:00",
  "tenderDeadline": "2026-11-13T23:59:59Z",
  "isOpen": true,
  "daysToDeadline": 38,
  "buyerName": "Natural England",
  "buyerAddress": "Seacole Building, 2 Marsham Street, London, SW1P 4DF, United Kingdom",
  "buyerRegion": "UKI32",
  "buyerEmail": "(official procurement contact published in the notice)",
  "mainCategory": "works",
  "procurementMethod": "open",
  "procurementMethodDetails": "Below threshold - open competition",
  "cpvCode": "45213100",
  "cpvDescription": "Construction work for commercial buildings",
  "valueAmount": 18000.0,
  "currency": "GBP",
  "contractStart": "2026-12-07T00:00:00Z",
  "contractEnd": "2027-01-23T23:59:59Z",
  "deliveryRegions": ["UKH"],
  "lotsCount": 1,
  "awardedSuppliers": [],
  "noticeUrl": "https://www.find-tender.service.gov.uk/Notice/094348-2026",
  "documentUrls": ["https://www.find-tender.service.gov.uk/Notice/Attachment/A-23660", "..."]
}
```

Real notice from a test run on 6 Oct 2026 (abbreviated). See the *Output* tab for all fields. Award notices add `awardedSuppliers` and `awardedValue`. A `RUN_SUMMARY` record in the key-value store shows how many notices were scanned per source.

### How much does it cost?

**$4 per 1,000 tender notices** saved (pay per result) plus Apify's tiny start fee. You only pay for notices that pass your filters — scanned-but-filtered notices are free.

| Job | Notices | Cost |
|---|---|---|
| Daily feed, one trade, all of UK | 10–40/day | ~$1–5/month |
| All new tenders this week, all sectors | ~1,500 | ~$6 |
| One year of awards in your CPV category | ~3,000 | ~$12 |

### Tips

- **SME-suitable only** relies on the suitability flag that buyers set. Contracts Finder fills it in reliably; many Find a Tender notices leave it blank, so this filter returns mostly Contracts Finder notices.
- CPV filtering matches the main CPV code **and** all additional codes on the notice.
- Values come straight from the buyer's notice. Occasionally they're placeholders (e.g. £1), so sanity-check before you rank by value.

### Speed and limits

The government APIs allow only a small burst of requests before asking clients to wait ~2 minutes. The Actor waits politely (up to **Max rate-limit waiting**, default 15 minutes per run) and then stops cleanly with what it has. Keep runs short and frequent: a **daily schedule with `daysBack: 1–2`** usually finishes in 1–3 minutes. Long historic pulls take longer — set a narrow keyword/CPV filter or a bigger waiting budget.

### Use it via API (Python, JavaScript, Make, Zapier, n8n)

Run the Actor and get results in one HTTP call (replace `YOUR_TOKEN` with your [Apify API token](https://console.apify.com/settings/integrations)):

```bash
curl -X POST "https://api.apify.com/v2/acts/plumbline-data~uk-public-tenders/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"keywords":["cyber security"],"daysBack":7}'
```

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run = client.actor("plumbline-data/uk-public-tenders").call(run_input={"keywords":["cyber security"],"daysBack":7})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
```

You can also **schedule** it (e.g. daily at 7 am) in Apify Console and send results to Google Sheets, Slack, a webhook or your database with built-in integrations.

### FAQ

#### Is this legal? Where is the data from?

From the UK government's official Find a Tender and Contracts Finder OCDS APIs, published under the **Open Government Licence v3.0**, which permits commercial reuse with attribution ("Contains public sector information licensed under the Open Government Licence v3.0").

#### What about buyer contact details and GDPR?

`buyerEmail` / `buyerPhone` are the official procurement contact points that public bodies publish in each notice so suppliers can ask questions and bid. Contact person names are **off by default** (`includeContactNames`), and you can switch contacts off entirely (`includeBuyerContact: false`). Some contact emails belong to named officers, so use them only for procurement-related communication, in line with UK GDPR and PECR.

#### Why do I see fewer results than on the website?

Check stage and date filters. By default only *tender* notices from the last 7 days are returned, and only the latest version of each procurement.

#### What are CPV codes?

The EU/UK Common Procurement Vocabulary. Prefixes match whole families: `72` = IT services, `4526` = roof works, `79` = business services.

#### Does it include Scotland, Wales and Northern Ireland?

It includes everything published to Find a Tender, which covers England, Wales and Northern Ireland under the Procurement Act 2023 plus above-threshold notices from Scottish authorities (and Contracts Finder for England). Notices published *only* on Public Contracts Scotland, Sell2Wales or eTendersNI (e.g. some devolved below-threshold contracts) are not included yet.

#### Other Actors by Plumbline Data

- [LEI Lookup & Company Search (GLEIF)](https://apify.com/plumbline-data/gleif-lei-lookup) — verify the buyers and suppliers you find here
- [Sitemap URL Extractor](https://apify.com/plumbline-data/sitemap-url-extractor)

Missing a field or a portal? Open an issue on the **Issues** tab.

# Changelog

This Actor's version history is a separate document: https://apify.com/plumbline-data/uk-public-tenders/changelog.md

# Actor input Schema

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

Keep notices whose title or description contains at least one of these words/phrases (case-insensitive). Matching starts at a word boundary, so "roof" finds roof, roofs and roofing but not waterproofing. E.g. roof, cyber security, catering.

## `excludeKeywords` (type: `array`):

Drop notices containing any of these words.

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

Match the main or any additional CPV code by prefix. Examples: 45 = construction work, 4526 = roof works, 72 = IT services, 79 = business services, 85 = health & social.

## `noticeStages` (type: `array`):

tender = open opportunities incl. updates/amendments (default); planning = pipeline / prior information; award = awards & contracts (who won, for how much); implementation = contract details (Contracts Finder); cancellation = cancelled tenders.

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

Find a Tender = the UK's central platform (all new Procurement Act 2023 notices, above and below threshold, plus older high-value notices). Contracts Finder = England's portal for lower-value and legacy notices. Both by default.

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

When 'Published from' is empty, look back this many days. Great for daily/weekly scheduled alerts.

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

Start date (YYYY-MM-DD). Find a Tender filters by last-updated date, Contracts Finder by published date.

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

End date (YYYY-MM-DD, inclusive). Empty = now.

## `minValue` (type: `integer`):

Drop notices with an estimated value below this (notices without a value are dropped too when set).

## `maxValue` (type: `integer`):

Drop notices above this value.

## `regions` (type: `array`):

Match delivery region, NUTS code, buyer town or postcode, e.g. London, North West, UKD, Manchester, BS1.

## `onlyOpen` (type: `boolean`):

Keep only tenders whose submission deadline has not passed.

## `smeSuitableOnly` (type: `boolean`):

Keep only notices the buyer marked as suitable for small and medium-sized enterprises.

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

Stop after this many matching notices. 0 = no limit.

## `latestVersionOnly` (type: `boolean`):

When a notice was updated several times in the range, return only its latest version.

## `includeBuyerContact` (type: `boolean`):

The procurement contact point the buyer published for suppliers.

## `includeContactNames` (type: `boolean`):

Also include the named individual where the buyer published one. Off by default (personal data - only enable if you have a lawful basis).

## `windowDays` (type: `integer`):

Long ranges are fetched in windows of this many days (newest first) so one API hiccup never loses the whole run.

## `delayBetweenRequestsMs` (type: `integer`):

Pause between API pages. The two UK services share one per-IP rate limit (~1 request/second); keep this at 1000+.

## `maxRetries` (type: `integer`):

Retries for timeouts, 429 and 5xx with exponential backoff.

## `useServerSideStageFilter` (type: `boolean`):

The official APIs can pre-filter by stage, but in our tests (Oct 2026) Find a Tender's filter returned only 7 of 66 tender notices for a day. Off by default: we fetch everything in the window and filter locally, which is complete but uses more requests.

## `maxRateLimitWaitSecs` (type: `integer`):

When the UK API rate-limits (429 + Retry-After, usually 120 s), the Actor pauses and continues. After this much total waiting it stops cleanly and returns what it has.

## Actor input object example

```json
{
  "keywords": [
    "roof"
  ],
  "excludeKeywords": [],
  "cpvCodes": [],
  "noticeStages": [
    "tender"
  ],
  "sources": [
    "findATender",
    "contractsFinder"
  ],
  "daysBack": 7,
  "regions": [],
  "onlyOpen": false,
  "smeSuitableOnly": false,
  "maxResults": 0,
  "latestVersionOnly": true,
  "includeBuyerContact": true,
  "includeContactNames": false,
  "windowDays": 7,
  "delayBetweenRequestsMs": 1100,
  "maxRetries": 5,
  "useServerSideStageFilter": false,
  "maxRateLimitWaitSecs": 900
}
```

# Actor output Schema

## `tenders` (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": [
        "roof"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("plumbline-data/uk-public-tenders").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": ["roof"] }

# Run the Actor and wait for it to finish
run = client.actor("plumbline-data/uk-public-tenders").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": [
    "roof"
  ]
}' |
apify call plumbline-data/uk-public-tenders --silent --output-dataset

```

## MCP server setup

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

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/t0ljOWGuy4LOcKaoA/builds/zPAZkQCcscrCdMioF/openapi.json
