# UK Tenders: Find a Tender + Contracts Finder (`homelabmobile/uk-tenders`) Actor

UK public procurement notices from Find a Tender and Contracts Finder official OCDS APIs, merged into one schema and deduplicated by OCID. Filter by date, stage, keyword, CPV.

- **URL**: https://apify.com/homelabmobile/uk-tenders.md
- **Developed by:** [Home Lab Mobile](https://apify.com/homelabmobile) (community)
- **Categories:** Business, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.00 / 1,000 results

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

## UK Public Tenders (Find a Tender + Contracts Finder)

Get UK public procurement notices (tenders, pipeline notices, contract awards) from both official
government portals, **Find a Tender** and **Contracts Finder**, as one clean JSON dataset with one
row per procurement process (OCID), newest first. Data comes from the portals' public OCDS APIs; no
login, account or API key is needed.

### What it does

- Reads both official UK portals in one run and merges them into one harmonised schema: title,
  description, buyer, CPV codes, regions, values, deadlines, lots, awards and winning suppliers.
- Deduplicates by OCID. A process that had several notices in your window (e.g. a tender and two
  updates) is one row; `noticeIds` lists every notice, `sources` says which portal(s) published it.
- Filters by date window (`daysBack` or `publishedFrom`/`publishedTo`), stage (planning, tender,
  award, implementation), keywords and CPV code prefixes.
- Strips personal data: buyer contact names, emails and phone numbers are never output, and email
  addresses and phone numbers inside titles and descriptions are redacted.
- You pay only for valid rows; malformed rows are dropped and not charged. If the APIs return
  nothing for a wide window, the run fails instead of returning an empty dataset.

### Input

| Field | Type | Required | Description |
|---|---|---|---|
| `daysBack` | integer | no | Notices from the last N days (default 3, max 90). Ignored if `publishedFrom` is set. |
| `publishedFrom` | string | no | Window start, `YYYY-MM-DD` or ISO 8601 date-time (UTC). |
| `publishedTo` | string | no | Window end, `YYYY-MM-DD` (whole day) or ISO 8601 date-time. Default: now. |
| `keyword` | string | no | Keep notices whose title or description contains all these words, case-insensitive. |
| `stages` | array | no | Any of `planning`, `tender`, `award`, `implementation`. Empty = all. |
| `cpvPrefixes` | array | no | Keep notices with a CPV code starting with any of these, e.g. `["72"]` (IT services). |
| `sources` | array | no | `find-a-tender` and/or `contracts-finder`. Empty = both. |
| `maxResults` | integer | no | Stop after this many rows (default 50, max 1,000). Caps the cost of a run. |

Open tenders from the last 3 days:

```json
{ "daysBack": 3, "stages": ["tender"], "maxResults": 50 }
```

IT-services notices (CPV 72) mentioning "cloud" in the first week of October 2026:

```json
{ "publishedFrom": "2026-10-01", "publishedTo": "2026-10-07", "keyword": "cloud", "cpvPrefixes": ["72"], "maxResults": 200 }
```

#### Calling from an AI agent (MCP)

This Actor works as a tool through the [Apify MCP server](https://mcp.apify.com). Example tool call:

```json
{ "actor": "homelabmobile/uk-tenders", "input": { "daysBack": 7, "stages": ["tender"], "keyword": "software", "maxResults": 10 } }
```

### Output

One JSON object per procurement process (OCID). A real row from a staging run (lots shortened):

```json
{
    "ocid": "ocds-h6vhtk-0785d5",
    "sources": ["find-a-tender"],
    "noticeId": "096735-2026",
    "noticeIds": ["096735-2026", "096286-2026", "096189-2026"],
    "noticeUrl": "https://www.find-tender.service.gov.uk/Notice/096735-2026",
    "stage": "tender",
    "releaseTags": ["tenderUpdate"],
    "title": "High Quality Cafe Furniture",
    "description": "High quality contemporary external cafe furniture comprising chairs and matching \ntables, suitable for installation within a publicly accessible outdoor space and \nintended to remain outdoors throughout the year. See documents for details.",
    "status": "active",
    "buyerName": "Awdurdod Parc Cenedlaethol Eryri",
    "buyerId": "GB-PPON-PMBN-3325-GJRQ",
    "buyerLocality": "Gwynedd",
    "buyerPostalCode": "LL48 6LF",
    "buyerCountry": "United Kingdom",
    "buyerWebsite": "http://www.eryri.llyw.cymru",
    "mainCpvCode": "39100000",
    "mainCpvDescription": "Furniture",
    "cpvCodes": ["39100000"],
    "regions": ["UK"],
    "valueAmount": 12500,
    "valueAmountGross": 15000,
    "valueMinAmount": null,
    "currency": "GBP",
    "procurementMethod": "open",
    "procurementMethodDetails": "Below threshold - open competition",
    "mainProcurementCategory": "goods",
    "suitableForSme": null,
    "suitableForVcse": null,
    "publishedDate": "2026-10-13T16:06:24.000Z",
    "tenderDeadline": "2026-11-09T12:00:00.000Z",
    "contractStart": null,
    "contractEnd": null,
    "lots": [{ "id": "1", "title": null, "description": null, "status": "active", "valueAmount": 12500, "currency": "GBP" }],
    "awards": [],
    "scrapedAt": "2026-10-14T02:47:59.340Z"
}
```

Award rows fill `awards` with the award date, value, and winning supplier organisation names and
ids (e.g. `GB-COH-<Companies House number>`). Suppliers that may be individuals are only counted, in
`suppliersRedacted`. All dates are UTC ISO 8601. Every field is described
in the dataset schema.

Each run also writes `RUN_SUMMARY` (status, counts) and `SOURCE_STATS` (pages and releases read per
portal, and any portal error) to its key-value store.

### Pricing

Pay per event:

| Event | Price | Per 1,000 |
|---|---|---|
| Result (one valid row = one OCID) | $0.003 | $3.00 |

Plus Apify's standard Actor start charge. A typical run of 50 open tenders costs about $0.15.

### Licence and attribution

Notice data is published by the Cabinet Office (Find a Tender) and Crown Commercial Service
(Contracts Finder) under the Open Government Licence v3.0. If you reuse the data, include this
attribution:

> Contains public sector information licensed under the Open Government Licence v3.0.
> Source: Find a Tender (https://www.find-tender.service.gov.uk) and Contracts Finder
> (https://www.contractsfinder.service.gov.uk), © Crown copyright.

Licence text: https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/.
Some Find a Tender notices are based on the TED XML schema (© European Union). This Actor is not
endorsed by the Cabinet Office or Crown Commercial Service.

### Known limits

- **Different date fields.** Contracts Finder filters on publication date; Find a Tender only
  filters on "last updated", so an old notice updated inside your window is included.
  `publishedDate` is the date of the newest release for that OCID.
- **Cross-portal duplicates are rare.** The two portals issue different OCID prefixes
  (`ocds-b5fd17-` Contracts Finder, `ocds-h6vhtk-` Find a Tender), so a notice posted on both usually
  appears as two rows. Deduplication mainly collapses several notices for the same process.
- **Filters run after download.** Neither API filters by keyword or CPV, and Find a Tender's own
  stage filter drops many notices, so those filters run on every downloaded notice. A rare keyword
  over a long window reads many pages; each portal is capped at 40 pages (4,000 notices) per run.
- **Regions differ by portal.** Contracts Finder gives region names (e.g. London); Find a Tender gives
  NUTS/ITL codes (e.g. UKI). Values on award notices are on the award, not the tender.
- **Speed.** The portals rate-limit their APIs. The Actor makes one request at a time, at most one
  every 10 seconds to Find a Tender and every 2 seconds to Contracts Finder, and honours `Retry-After`.
  A 500-row run takes about 1–3 minutes. If a portal blocks the run, its rows are skipped and the
  run reports it in `SOURCE_STATS`; the run fails only if both portals fail.
- **Only organisations are named as suppliers.** A winning supplier's name and id are output only if
  it has a company, charity or public-body ID, or a legal-form name (Ltd, PLC, LLP, CIC, University,
  Council and so on). Others may be sole traders trading under their own name, so they are counted in
  `suppliersRedacted` instead. Some small firms without a legal-form name are left out as a result.
  No supplier addresses or contact details are output.
- Public, logged-out API data only. It does not log in or bypass any access control.

### Support

Open an issue on the Actor's Issues tab. We reply within 2 business days.

# Actor input Schema

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

Return notices published or updated in the last N days (ending now). Ignored when publishedFrom is set. Example: 3.

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

Optional start of the window, as YYYY-MM-DD or an ISO 8601 date-time in UTC, e.g. 2026-10-01. Overrides daysBack. Find a Tender filters on its 'last updated' date, Contracts Finder on its publication date.

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

Optional end of the window, as YYYY-MM-DD (inclusive, whole day) or an ISO 8601 date-time in UTC. Defaults to now.

## `keyword` (type: `string`):

Optional. Keep only notices whose title or description contains ALL of these words (case-insensitive), e.g. "cyber security". Leave empty for all notices.

## `stages` (type: `array`):

Optional. Keep only these procurement stages: planning (pipeline / prior information), tender (open opportunities), award (contract awards), implementation (contract changes; Contracts Finder only). Empty = all stages.

## `cpvPrefixes` (type: `array`):

Optional. Keep only notices with at least one CPV code starting with any of these digits, e.g. \["72"] for IT services or \["45"] for construction work. Empty = all.

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

Which portals to read: find-a-tender (Find a Tender, mostly above-threshold) and/or contracts-finder (Contracts Finder, mostly lower-value England notices). Empty = both.

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

Stop after this many notices (unique OCIDs). You are charged per result, so this caps the cost of a run.

## Actor input object example

```json
{
  "daysBack": 3,
  "maxResults": 20
}
```

# Actor output Schema

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

One JSON object per result; fields are described in the dataset schema.

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

RUN_SUMMARY: status, counts and the silent-failure verdict for this run.

# 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 = {
    "daysBack": 3,
    "maxResults": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("homelabmobile/uk-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 = {
    "daysBack": 3,
    "maxResults": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("homelabmobile/uk-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 '{
  "daysBack": 3,
  "maxResults": 20
}' |
apify call homelabmobile/uk-tenders --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,homelabmobile/uk-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/hfSdEwoU9dZnDT1ZX/builds/2LfuyfLol1zlycAZB/openapi.json
