# UK Tenders Scraper: Contracts Finder + Find a Tender Feed (`appropriate_songbird/uk-public-tenders-feed`) Actor

Scrape UK government tenders and contract awards from Contracts Finder and Find a Tender in one clean feed. Filter by keyword, CPV, value, region and SME suitability. Daily only-new alerts. From $0.003 per notice.

- **URL**: https://apify.com/appropriate_songbird/uk-public-tenders-feed.md
- **Developed by:** [Puneet](https://apify.com/appropriate_songbird) (community)
- **Categories:** Business, Lead generation, Marketing
- **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

## UK Tenders Scraper: Contracts Finder + Find a Tender Feed

**Every new UK public sector tender, in one clean feed, no API key needed.** This Actor merges the UK government's two procurement services, Contracts Finder and Find a Tender, into one consistent schema. It filters them to what you sell, removes cross-posted duplicates, and on scheduled runs gives you only the notices you haven't seen yet.

### Who it's for

- **SMEs and suppliers** bidding for council, NHS, central government, university and housing contracts.
- **Bid writers and consultants** who track opportunities for several clients.
- **Sales and BD teams** who pipe new tenders into a CRM, Slack, email digests or Google Sheets.
- **Analysts** following who buys what, and who wins (contract-award stage).

### Why this one

- **Both UK services in one schema.** Contracts Finder (England, lower-value and central-government notices) and Find a Tender (UK-wide, above-threshold notices), with the same field names for both.
- **Cross-posting duplicates removed.** When a buyer posts the same notice on both services, you get it once.
- **Only new since last run.** Each notice is delivered once per filter set, so a daily schedule gives you a daily digest.
- **Filters that matter to bidders:** keywords, CPV prefixes, value range, region (ITL/NUTS code, town or postcode area), SME suitability and deadline.
- **Clean on privacy.** Named officers and phone numbers are stripped. A buyer email is kept only if it is a shared mailbox such as `procurement@…`.
- **Polite and resilient.** Requests are sequential, the services' `Retry-After` is honoured, and if one service is down you still get the other's results.

### Quick start (under a minute)

1. Click **Try for free** and leave the prefilled input: keywords `software` and `digital`, last 7 days, up to 50 notices.
2. Press **Start**. The first run returns real open tenders in seconds.
3. Export as CSV, Excel or JSON, or schedule it daily. With *Only new since last run* on, every later run returns only notices you haven't seen.

### How much does it cost?

You pay $0.005 per run plus $0.003 per notice delivered. Platform compute is included.

| Use | Notices | Cost |
|---|---|---|
| First test run (prefilled input) | up to 50 | $0.02 to $0.16 |
| Daily digest for one sector | 15 new a day, 22 weekdays | about $1.10 a month |
| Weekly sweep for three clients | 3 runs x 60 notices | about $0.60 a week |
| One-off market scan, last 90 days | 1,000 notices | about $3.00 |

For comparison, other UK tender scrapers on the Store charge from about $0.005 to $0.02 per record, and tender-alert services cost tens of pounds a month. Set *maximum cost per run* to cap spend.

### Sample output

```json
{
  "source": "findATender",
  "ocid": "ocds-h6vhtk-0787f6",
  "releaseId": "097200-2026",
  "stage": "tender",
  "releaseTags": ["tender"],
  "title": "Stop Smoking Medication Framework",
  "description": "West Northamptonshire Council intends to establish a new framework for the provision of Varenicline or Cytisine…",
  "buyerName": "West Northamptonshire Council",
  "buyerLocality": "Northampton",
  "buyerRegion": "UKF24",
  "buyerPostcodeArea": "NN",
  "buyerWebsite": "https://www.westnorthants.gov.uk/",
  "buyerContactEmail": "procurement@westnorthants.gov.uk",
  "deliveryRegions": ["UKF24"],
  "valueAmount": 309871,
  "valueCurrency": "GBP",
  "cpvCodes": ["85149000"],
  "mainCpvDescription": "Pharmacy services",
  "mainProcurementCategory": "services",
  "procedureType": "Competitive procedure with negotiation",
  "suitableForSme": null,
  "suitableForVcse": null,
  "status": "active",
  "publishedDate": "2026-10-14T15:41:00+00:00",
  "tenderDeadline": "2026-11-11T12:00:00+00:00",
  "daysUntilDeadline": 27,
  "contractStart": null,
  "contractEnd": null,
  "noticeUrl": "https://www.find-tender.service.gov.uk/Notice/097200-2026",
  "awardSupplierNames": [],
  "awardSuppliersWithheld": 0,
  "licence": "Open Government Licence v3.0",
  "licenceUrl": "https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/",
  "attribution": "Contains public sector information licensed under the Open Government Licence v3.0.",
  "deltaKey": "findATender:ocds-h6vhtk-0787f6:tender"
}
```

The dataset has two table views, **Notices** and **Awards**. Export to JSON, CSV or Excel, or connect it to Zapier, Make, Google Sheets or webhooks.

### Inputs

| Field | What it does | Example |
|---|---|---|
| `sources` | `contractsFinder`, `findATender` (default both) | `["findATender"]` |
| `stages` | `planning`, `tender`, `award` (default `tender`) | `["tender", "award"]` |
| `keywords` / `excludeKeywords` | Match any / exclude any, in title and description | `["software", "cloud"]` |
| `cpvPrefixes` | CPV code prefixes | `["72", "48"]` |
| `minValueGbp` / `maxValueGbp` | Value range (notices without a published value are kept) | `50000` |
| `regions` | ITL/NUTS code prefix, buyer town, or postcode area | `["UKI", "Manchester", "BN"]` |
| `suitableForSmeOnly` | Only notices flagged suitable for SMEs | `true` |
| `publishedWithinDays` | Look-back window, 1–90 days | `3` |
| `closingAfter` | Deadline on or after this date | `2026-11-01` |
| `dedupeAcrossSources` | Drop cross-posted duplicates (default on) | `true` |
| `onlyNewSinceLastRun` | Delta mode (default on) | `true` |
| `includeDescription` | Include notice description (default off; emails and phone numbers scrubbed) | `true` |
| `maxResults` | Cap, newest first | `500` |

**Tip:** save a Task per client or per sector and schedule it every weekday morning. With delta mode on, each run returns only the new notices.

### Pricing (pay per event)

| Event | Price |
|---|---|
| Run started | $0.005 |
| Notice delivered | $0.003 (= $3 per 1,000) |

Example: a weekday schedule returning 15 new notices a day costs about $0.05 a day, roughly **$1 a month**. Set *maximum cost per run* to cap spend; the Actor stops cleanly at the cap. Platform compute is included.

### Data source and licence

- **Sources:** the open OCDS APIs of **Contracts Finder** (`contractsfinder.service.gov.uk`) and **Find a Tender** (`find-tender.service.gov.uk`), the UK government's public procurement notice services.
- **Licence:** Contains public sector information licensed under the **Open Government Licence v3.0** (https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/). Every output item carries `licence`, `licenceUrl` and `attribution` fields, so the attribution travels with the data. When you republish the data, keep that attribution.
- Only notice facts and links are output. No logos, crests or tender documents are copied or re-hosted.

### Privacy and legal note

- **Named contact persons and telephone numbers are removed** from every notice. `buyerContactEmail` is filled only when the address is a shared role mailbox (procurement@, tenders@, commercial@ …). Personal-looking addresses are dropped.
- **Descriptions are opt-in.** When enabled, personal-looking email addresses and phone numbers inside the free text are replaced with `[email removed]` / `[phone removed]`. Free text can still mention people by name, so review it before republishing.
- **Award suppliers** are shown when they are registered companies (Companies House ID) or the name carries an organisation marker (Ltd, LLP, Council, Trust …). Other names, possibly sole traders, are withheld, and `awardSuppliersWithheld` counts them.
- This is an independent tool. It is **not affiliated with or endorsed by the Cabinet Office, Crown Commercial Service, Contracts Finder or Find a Tender.** Always check the notice page before bidding.

### Reliability

- Sequential, rate-limited requests with backoff on 429/5xx (honours `Retry-After`).
- If one service fails, the run still delivers the other service's notices and the status message says which service failed.
- Malformed records are skipped and counted, never fatal.
- Every run writes a `RUN_SUMMARY` record (releases read, matched, duplicates removed, contacts removed, delivered). A persistent run-health counter (runs started / succeeded / failed / unfinished, plus the last error) is kept in your account's `ukptf-state` key-value store.
- A run with nothing new **succeeds** with zero items.

### FAQ

**What's the difference between the two services?** Find a Tender carries higher-value notices from across the UK. Contracts Finder carries lower-value notices too, mainly from England. Many notices appear on both, and this Actor merges them.

**How far back can I go?** Up to 90 days per run. For older history, run once with a long window and keep the dataset.

**Do I need a Contracts Finder or Find a Tender account or API key?** No. The Actor reads the public open-data APIs.

**Can I track awarded contracts and competitors?** Yes. Add `award` to `stages` to get contract awards with supplier names (registered companies only).

**Related tools by the same author:** [EU Tenders (TED) Feed](https://apify.com/appropriate_songbird/eu-tenders-ted-feed) for EU notices, [SAM.gov Opportunities Feed](https://apify.com/appropriate_songbird/federal-contract-opportunities-feed) for U.S. federal bids.

**Issues or feature requests?** Use the Issues tab. Issues are answered within a few days.

# Actor input Schema

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

Which UK government procurement services to read.

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

Which notice stages to return.

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

Matched in title and description, case-insensitive.

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

Drop notices whose title/description contains any of these.

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

e.g. 72 (IT services), 48 (software), 7932 (market research).

## `minValueGbp` (type: `integer`):

Notices without a published value are kept.

## `maxValueGbp` (type: `integer`):

Upper value limit. Notices without a published value are kept.

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

ITL/NUTS code prefix (e.g. UKI = London, UKM = Scotland, UKL = Wales), buyer town, or postcode area (e.g. BN).

## `suitableForSmeOnly` (type: `boolean`):

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

## `publishedWithinDays` (type: `integer`):

Look-back window. Contracts Finder filters by publish date, Find a Tender by last-updated date.

## `closingAfter` (type: `string`):

YYYY-MM-DD. Notices without a deadline are excluded when set.

## `dedupeAcrossSources` (type: `boolean`):

When the same buyer posts the same title on both services, keep the Find a Tender version.

## `onlyNewSinceLastRun` (type: `boolean`):

Each notice is delivered once per filter set (state kept in your own account).

## `includeDescription` (type: `boolean`):

Adds the notice description. Personal-looking emails and phone numbers are removed from it; free text can still mention people, so it is off by default.

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

Cap on delivered notices, newest first.

## Actor input object example

```json
{
  "sources": [
    "contractsFinder",
    "findATender"
  ],
  "stages": [
    "tender"
  ],
  "keywords": [
    "software",
    "digital"
  ],
  "suitableForSmeOnly": false,
  "publishedWithinDays": 7,
  "dedupeAcrossSources": true,
  "onlyNewSinceLastRun": true,
  "includeDescription": false,
  "maxResults": 50
}
```

# Actor output Schema

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

Dataset of UK tender and award notices matching the input filters.

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

Counts for this run (scanned, matched, delivered, skipped).

# 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": [
        "software",
        "digital"
    ],
    "publishedWithinDays": 7,
    "maxResults": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("appropriate_songbird/uk-public-tenders-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 = {
    "keywords": [
        "software",
        "digital",
    ],
    "publishedWithinDays": 7,
    "maxResults": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("appropriate_songbird/uk-public-tenders-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 '{
  "keywords": [
    "software",
    "digital"
  ],
  "publishedWithinDays": 7,
  "maxResults": 50
}' |
apify call appropriate_songbird/uk-public-tenders-feed --silent --output-dataset

```

## MCP server setup

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