# UK Public Tenders API: Contracts Finder + Find a Tender (`uk-open-data-tools/uk-tenders-unified`) Actor

Official UK tender and contract award notices from Contracts Finder and Find a Tender in one deduplicated feed. Filter by CPV, value, keyword.

- **URL**: https://apify.com/uk-open-data-tools/uk-tenders-unified.md
- **Developed by:** [UK Open Data Tools](https://apify.com/uk-open-data-tools) (community)
- **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?

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 API: Contracts Finder + Find a Tender in one feed

Get UK public-sector **tender opportunities and contract award notices** as clean JSON, CSV or Excel. The data comes from the two official UK government services:

- **Contracts Finder:** contract opportunities and awards from UK public bodies.
- **Find a Tender (FTS):** the UK's service for higher-value public procurements.

The actor calls the services' official **OCDS APIs** (Open Contracting Data Standard). It does not scrape web pages, needs no login and no proxies, and every row links back to the official notice.

### What it does

- **One feed from both services.** Choose Contracts Finder, Find a Tender, or both.
- **One row per procurement.** A process often has several releases (planning → tender → award). The actor keeps the latest release per OCID, so you don't get duplicate rows for the same procurement.
- **Cross-source duplicate handling.** Some procurements are published on both services. The two services share no identifier, so the actor matches on exact normalised title + buyer. It then either *flags* the pair (default; both rows kept and linked) or *drops* the Find a Tender copy.
- **Filters.** Keywords (title/description), CPV code prefixes, minimum contract value, and a look-back window in days.
- **Privacy built in.** Contact names, emails and phone numbers are removed (see *Privacy* below).
- **Official notice link** on every row (`noticeUrl`).

### Use cases

- Daily bid alerts for your sector. Example: CPV `72` (IT services) or `45` (construction), scheduled every morning.
- Feed a CRM, Slack, Airtable or Google Sheet via Apify integrations.
- Market research on who is buying what, where and at what value.
- Competitor and award monitoring from award notices.
- Data for AI agents and RAG pipelines that need structured UK procurement data.

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `sources` | array | both | `contracts_finder`, `find_a_tender` |
| `daysBack` | integer | 1 | Contracts Finder filters by *published* date and Find a Tender by *updated* date. Use 1 with a daily schedule. |
| `keywords` | array | \[] | Case-insensitive; matches any keyword in the title or description |
| `cpvPrefixes` | array | \[] | e.g. `["72","48"]` |
| `minValue` | integer | 0 | GBP. Notices with no published value are kept. |
| `crossSourceDuplicates` | string | `flag` | `flag`, `drop` or `off` |
| `maxItems` | integer | 500 | Maximum rows returned |
| `maxReleasesScanned` | integer | 20000 | Safety cap per source |

Example:

```json
{
  "sources": ["contracts_finder", "find_a_tender"],
  "daysBack": 7,
  "keywords": ["software", "digital"],
  "cpvPrefixes": ["72", "48"],
  "minValue": 100000,
  "maxItems": 100
}
```

### Output

One row per procurement. Real example from a test run on 16 Oct 2026:

```json
{
  "source": "find_a_tender",
  "sourceLabel": "Find a Tender",
  "noticeId": "097903-2026",
  "ocid": "ocds-h6vhtk-069852",
  "releaseId": "097903-2026",
  "releaseTags": ["tender"],
  "status": "active",
  "title": "AT1452 Civil and Structural Inspections for Luton Airport",
  "description": "To conduct principal inspection of Luton DART civil engineering assets, ...",
  "buyerName": "LONDON LUTON AIRPORT LIMITED",
  "buyerPostcode": "LU2 0LA",
  "buyerRegion": "UKH21",
  "valueAmount": 800000.0,
  "valueCurrency": "GBP",
  "tenderDeadline": null,
  "procurementMethod": "Competitive flexible procedure",
  "cpvCodes": ["71000000"],
  "awardedSupplierCount": 0,
  "publishedDate": "2026-10-16T12:23:18+01:00",
  "noticeUrl": "https://www.find-tender.service.gov.uk/Notice/097903-2026",
  "possibleDuplicateOf": null,
  "licence": "Contains public sector information licensed under the Open Government Licence v3.0"
}
```

Each run also writes a `RUN_SUMMARY` record to the key-value store. It holds the date window, releases fetched per source, the count after dedup, cross-source matches and rows returned.

### Pricing

**Free during launch.** The actor itself charges nothing; you only use your own Apify platform usage (a one-day run costs a fraction of a cent in compute on our tests). Pay-per-result pricing may be introduced later; if so, it will be announced in advance on this page and through Apify's standard notice period, and you can always set a maximum cost per run.

Rough guide: a one-day window returned about 590–620 notices before filtering in our October 2026 tests. A filtered daily alert usually returns far fewer rows.

### Limits and accuracy

- Data is exactly what buyers published. Values, deadlines and CPV codes are missing on some notices. In a 3-day test, about 60% had a value, about 43% had a deadline and about 60% had CPV codes.
- Cross-source matching is deliberately strict, so some true duplicates with different wording are not flagged. Similar titles from the same buyer can occasionally be flagged.
- Find a Tender filters on the *updated* date, so older notices that were amended recently can appear.
- Historical backfills of more than 31 days per run are not supported. Schedule daily runs instead.
- This is an independent tool. It is not affiliated with or endorsed by the Cabinet Office or Crown Commercial Service.

### Data licence

Contains public sector information licensed under the Open Government Licence v3.0 (https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/). Both APIs declare this licence in every response. If you republish the data, keep this attribution.

### Privacy

This actor is designed to output **organisation-level procurement data only**:

- Buyer and supplier `contactPoint` details (names, emails, phones) are never output.
- Email addresses and UK phone numbers inside titles and descriptions are replaced with `[email removed]` / `[phone removed]`.
- Awarded supplier *names* are not output, because some suppliers are sole traders. Only `awardedSupplierCount` is given.
- Free-text descriptions are reproduced as published, after redaction. In rare cases they may still mention a person's name. Do not use this data to profile individuals.

### FAQ

**Is this legal to use commercially?** The source data is published under the Open Government Licence v3.0, which allows commercial reuse with attribution. You are responsible for how you use it.

**Why not just use the free government websites?** You can. This actor saves you combining two services, deduplicating releases, filtering by CPV and value, and removing personal contact details. It delivers clean rows on a schedule into your tools.

**How fresh is the data?** It is fetched live from the official APIs at run time.

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

**Why do some rows have `possibleDuplicateOf`?** The same procurement appears on both services. The field links to the other notice.

**Can I get supplier names?** Not in this version, for privacy reasons. You can open `noticeUrl` to see the official award notice.

**Something broke or a field is wrong?** Open an issue on the actor's Issues tab. We aim to reply within 24 hours.

# Actor input Schema

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

Which official services to query.

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

Notices published (CF) / updated (FTS) in the last N days. Use 1 with a daily schedule.

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

Case-insensitive match on title/description. Empty = all.

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

e.g. 72 (IT services), 45 (construction). Empty = all.

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

Skip notices with a known value below this

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

Maximum notices returned (and charged) in this run.

## `crossSourceDuplicates` (type: `string`):

The two services share no ID; duplicates are detected by exact normalised title + buyer.

## `maxReleasesScanned` (type: `integer`):

Safety cap on API pages fetched.

## `chargePerNotice` (type: `boolean`):

PPE charging switch; off by default (actor is free during launch).

## `includeSupplierNames` (type: `boolean`):

Off by default: supplier names can identify sole traders. Requires board/legal sign-off to enable.

## Actor input object example

```json
{
  "sources": [
    "contracts_finder",
    "find_a_tender"
  ],
  "daysBack": 1,
  "keywords": [],
  "cpvPrefixes": [],
  "minValue": 0,
  "maxItems": 500,
  "crossSourceDuplicates": "flag",
  "maxReleasesScanned": 20000,
  "chargePerNotice": false,
  "includeSupplierNames": false
}
```

# Actor output Schema

## `overview` (type: `string`):

All notices (default dataset), Overview view selected.

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

RUN_SUMMARY record: window, per-source counts, cross-source matches.

# 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": [
        "contracts_finder",
        "find_a_tender"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("uk-open-data-tools/uk-tenders-unified").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": [
        "contracts_finder",
        "find_a_tender",
    ] }

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

```

## MCP server setup

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

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/PdqvDEXWqJQbRA55X/builds/s5e5VunuzxdECaANU/openapi.json
