# UK Unified Tender Feed — Find a Tender + Contracts Finder (`civicrows/uk-unified-tender-feed`) Actor

One deduplicated feed of UK public procurement notices across multiple official portals, in a single consistent schema. Organisation-level data only, no personal data.

- **URL**: https://apify.com/civicrows/uk-unified-tender-feed.md
- **Developed by:** [Levi Corteen](https://apify.com/civicrows) (community)
- **Categories:** Developer tools, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $15.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/platform/actors/running/actors-in-store#pay-per-event

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

## UK Unified Tender Feed — Find a Tender + Contracts Finder, Deduplicated

One deduplicated feed of UK public procurement notices from **two official
government sources**, in a single consistent schema. Built for pipelines, not
dashboards: no seat, no login, no contract — pay per run.

### What you get

Every notice as one flat row with the same field names regardless of which
portal it came from, plus the same procurement **merged** when it appears on both:

`source` · `seenInSources` · `ocid` · `noticeId` · `sourceUrl` · `stage` · `title` ·
`description` · `procurementCategory` · `cpvCode` · `cpvDescription` · `cpvDivision` ·
`buyerName` · `buyerRegion` · `buyerPostcode` · `valueAmount` · `valueCurrency` ·
`publishedDate` · `deadlineDate` · `contractStartDate` · `contractEndDate` ·
`supplierNames` · `awardValueAmount` · `isFramework`

### Coverage — stated plainly

| Source | Covered |
|---|---|
| Find a Tender (Central Digital Platform) | ✅ |
| Contracts Finder | ✅ |
| Public Contracts Scotland | ❌ not yet |
| Sell2Wales | ❌ not yet |
| eTendersNI | ❌ not yet |

**Two of the UK's five procurement portals.** If you need all five today, this
Actor is not yet the right tool and you should look elsewhere — several
subscription products cover more. Scotland is next on the roadmap.

### What this is, and is not

This data is **free and public**, published by the UK government under the
[Open Government Licence v3.0](http://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/).
You can call the official APIs yourself:

- [Find a Tender API documentation](https://www.find-tender.service.gov.uk/Developer/Documentation)
- [Contracts Finder API documentation](https://www.contractsfinder.service.gov.uk/apidocumentation/home)

**If you only need one source, use the official API directly — it is free and it
is excellent.** This Actor is a convenience layer for the case where the official
APIs stop being convenient:

- The two APIs use **different date parameters that mean different things** —
  `updatedFrom` (last-updated) vs `publishedFrom` (publication date)
- Different ocid schemes, different release-id conventions, different pagination
- Values sometimes on the tender, sometimes only per-lot
- **No shared identifier**, so the same procurement appears twice with no way to
  tell it is the same contract
- Contracts Finder signals rate limiting with **HTTP 403 and an HTML body**, not 429

This Actor absorbs all of that and hands you rows.

### Deduplication

The same procurement is frequently advertised on more than one portal, with
different wording — "Cardiff Council" vs "The City of Cardiff Council",
"Provision of School Catering Services" vs "School Catering Services (Provision
of)". There is no shared ID.

We match on normalised buyer + normalised title, keep the record from the more
authoritative source, record every portal it appeared in under `seenInSources`,
and backfill any field the winning row was missing.

**You are not charged for duplicates that get removed.** Set
`includeDuplicates: true` if you would rather see every portal's copy separately.

### No personal data

Organisation-level data only. Contact names, emails, telephone and fax numbers
present in the source feeds are **stripped before anything is written**, and an
automated guard **fails the run** if any personal data survives. That guard is
covered by tests that run on every change.

If you need named procurement officers, this Actor will never give you them.
That is a deliberate design decision, not a gap.

### Pricing

Pay-per-event: a small charge when a run starts, plus a per-notice charge for
each **unique** notice returned. No subscription, no seat, no minimum.

### Typical uses

- Nightly sync of new notices into a CRM or lead-scoring model
- Filtering by CPV division and contract value to a specific market
- Building a historical dataset of awards and suppliers
- Monitoring named buyers without paying for a per-seat platform

### Input

| Field | Default | Notes |
|---|---|---|
| `sources` | both | Which portals to pull from |
| `dateFrom` / `dateTo` | last 7 days | Applied as last-updated on Find a Tender, publication date on Contracts Finder — the closest equivalent each API offers |
| `stages` | all | `planning`, `tender`, `award`, `implementation` (last is Contracts Finder only, filtered automatically) |
| `cpvDivisions` | all | Two-digit CPV codes, e.g. `45` construction, `85` health |
| `minValue` | none | Notices with **no stated value are always kept** — a missing figure is not evidence of a small contract |
| `includeDuplicates` | `false` | Keep every portal's copy separately |
| `maxItems` | 1000 | Hard cap, applied **after** deduplication |

### Limitations — read before buying

- **Two of five UK portals.** Scotland, Wales and Northern Ireland not yet included.
- **No personal or contact data**, ever.
- Coverage and accuracy are those of the source APIs; we normalise, we do not correct.
- Contracts Finder volume is **declining structurally** — since the Procurement
  Act 2023 commenced on 24 February 2025, new notices publish to Find a Tender.
- Not a bid-management platform, not an alerting tool. It returns rows.

### What a row actually looks like

Three real rows, trimmed to the columns most people use:

| publishedDate | title | buyerName | valueAmount | deadlineDate | cpvDescription |
|---|---|---|---|---|---|
| 2026-07-29 | Provision of Highways Maintenance Services | Lancashire County Council | 4500000 | 2026-09-15 | Highway maintenance work |
| 2026-07-28 | Grounds Maintenance Services 2027-2031 | Preston City Council | 500000 | 2026-08-29 | Planting and maintenance of green areas |
| 2026-07-30 | Supply of Laboratory Consumables | NHS Lancashire Teaching Hospitals | 250000 | — | Laboratory reagents |

Every row also carries `ocid`, `noticeId`, `sourceUrl`, `stage`, `description`,
`procurementCategory`, `cpvCode`, `cpvDivision`, `buyerRegion`, `buyerPostcode`,
`valueCurrency`, `contractStartDate`, `contractEndDate`, `supplierNames`,
`awardValueAmount` and `isFramework`.

Export as JSON, CSV, Excel or feed it straight through the Apify API.

### Questions people ask before running it

**Is this data free elsewhere?**
Yes. It is published by the UK government under the Open Government Licence and
the official APIs are linked above. If you only need one source and you are happy
calling an API, do that. This Actor is for when that stops being convenient.

**How fresh is it?**
As fresh as the source. Notices appear here as soon as they appear on the portal.
Run it on a daily or weekly schedule to pick up new notices automatically.

**How far back can I go?**
Set the date window as far back as you like. Bear in mind older windows return
more rows, and you are charged per row, so use `maxItems` to cap it.

**Does it include contact details for the buyer?**
No, deliberately. Contact names, emails and phone numbers are stripped before
anything is written, and an automated guard stops the run if any survive. If you
need named contacts, this is not the right tool and never will be.

**What does a typical run cost?**
The default settings return 250 notices. Scale from there — you are charged per
row returned, so a small window costs very little.

**Can I filter to my sector?**
Yes. Use `cpvDivisions` with two-digit CPV codes — `45` construction, `85` health
and social care, `72` IT services, `79` business services, `77` grounds and
agriculture. Combine with `minValue` to drop contracts below your threshold.

**Why are some notices missing a value?**
Buyers do not always publish one. Those rows are kept deliberately: a missing
figure is not evidence of a small contract, and dropping them would lose real
opportunities.

**Can I get this as a proper API instead of an Actor run?**
Every Apify Actor is callable via the Apify API, so you can trigger a run and
collect the dataset programmatically.

**What if it breaks?**
Raise an issue on the Actor. It is built on official documented APIs rather than
scraped HTML, so it breaks far less often than a typical Store listing — and
there is a published maintenance runbook behind it.

### Data licence and attribution

Contains public sector information licensed under the
[Open Government Licence v3.0](http://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/).
Source: Find a Tender and Contracts Finder, Cabinet Office / Crown Copyright.
Attribution is required if you republish.

***

*UK government contracts data · public sector tender alerts · procurement
opportunities feed · Find a Tender API · Contracts Finder API · OCDS notices ·
public sector bid leads · UK tender data for CRM · government contract awards
dataset*

# Actor input Schema

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

Which official portals to pull from. More sources means more duplicates removed. Public Contracts Scotland covers the devolved Scottish regime, which is outside the Procurement Act 2023 and is not published on Find a Tender.

## `dateFrom` (type: `string`):

Start of the window (ISO 8601). Defaults to 7 days ago. Applied as last-updated on Find a Tender and as publication date on Contracts Finder — the closest equivalent each API offers.

## `dateTo` (type: `string`):

End of the window (ISO 8601). Defaults to now.

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

Which stages to return. Leave empty for all. Note: 'implementation' exists only on Contracts Finder and is dropped automatically for Find a Tender, so selecting it will not cause an error.

## `cpvDivisions` (type: `array`):

Two-digit CPV divisions to keep, e.g. 45, 85. Empty = all.

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

Notices with no stated value are always kept.

## `includeDuplicates` (type: `boolean`):

Off by default. Turn on only if you want to see every portal's copy of the same procurement separately.

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

HARD CAP on results. Default 250 keeps a first run inside the $5/month credit that Apify free-plan users get, so the Actor can be trialled at no cost. Raise it for production pulls. Also protects run time and unit economics.

## `maxRequestsPerSource` (type: `integer`):

Second safety cap, applied per source and on pagination rather than results. Protects both your unit economics and the source APIs from an unbounded run.

## `maxRunSeconds` (type: `integer`):

Wall-clock budget. The run stops cleanly and returns what it has collected rather than exceeding this. Default 240s, chosen to stay inside Apify's 5-minute quality-check window even if a source imposes a long rate-limit wait.

## Actor input object example

```json
{
  "sources": [
    "find-a-tender",
    "contracts-finder",
    "public-contracts-scotland"
  ],
  "stages": [],
  "cpvDivisions": [],
  "includeDuplicates": false,
  "maxItems": 250,
  "maxRequestsPerSource": 150,
  "maxRunSeconds": 240
}
```

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("civicrows/uk-unified-tender-feed").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("civicrows/uk-unified-tender-feed").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 '{}' |
apify call civicrows/uk-unified-tender-feed --silent --output-dataset

```

## MCP server setup

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

```

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/KU9JL9tkLx9ZnCLUT/builds/k3FNq4wj54IjRFlbN/openapi.json
