# UK Public Tenders – Find a Tender & Contracts Finder (OCDS) (`elekezem/uk-tenders-ocds`) Actor

Unofficial. Pull UK public procurement notices from the Find a Tender and Contracts Finder OCDS APIs. Filter by CPV, keyword and stage; incremental mode for daily alerts. Export JSON/CSV/Excel. $2 per 1,000 releases.

- **URL**: https://apify.com/elekezem/uk-tenders-ocds.md
- **Developed by:** [OSEKA Data](https://apify.com/elekezem) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 ocds releases

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 (OCDS)

**Use from an AI agent (MCP):** this unofficial Actor is available as an MCP tool via Apify's hosted MCP server:
`https://mcp.apify.com/?tools=elekezem/uk-tenders-ocds`

- **Claude Desktop / claude.ai:** Settings → Connectors → Add custom connector → Remote MCP server URL = the URL above → Continue → Add, then sign in to Apify (OAuth).
- **Cursor** (`~/.cursor/mcp.json`, or run `apify mcp install cursor`):
  ```json
  { "mcpServers": { "uk-tenders": { "url": "https://mcp.apify.com/?tools=elekezem/uk-tenders-ocds" } } }
  ```
  Using an API token instead of OAuth: add `"headers": { "Authorization": "Bearer <APIFY_TOKEN>" }`.
- Try: *"Find UK IT tenders published in the last 7 days, max 20."*
- Billing: per returned notice (see Pricing). Keep `maxResults` small while testing. Default sample output omits the large `raw` payload (`includeRaw`: `false`).

This Actor is not an official government API. It reads public OCDS feeds from Find a Tender and Contracts Finder.

Pull UK tenders and public procurement notices from [Find a Tender](https://www.find-tender.service.gov.uk/api/1.0/ocdsReleasePackages) and [Contracts Finder](https://www.contractsfinder.service.gov.uk/Published/Notices/OCDS/Search) as [OCDS](https://standard.open-contracting.org/) (Open Contracting Data Standard) JSON — one dataset item per release.

> **Unofficial.** This Actor is not affiliated with, endorsed by, or connected to the UK Government, the Cabinet Office, Crown Commercial Service, Find a Tender Service, or Contracts Finder. It uses no government logos. Notices on the source websites remain the ones to rely on; this output can lag behind them.

### Who it is for

- **Bid consultants** building a UK tenders watch-list by CPV codes, keyword, or procurement stage
- **B2G sales** looking for government contracts UK and B2G leads from published notices
- **Analysts** doing tender monitoring and tracking contract awards
- **Researchers** working with OCDS / Open Contracting data from Find a Tender and Contracts Finder

### 60-second quick start

The default input is already a small sample (`mode`: `sample`, `maxResults`: `10`, both sources, last 7 days, `includeRaw`: `false`). You do not need to edit anything. Sample mode is capped at 10 notices.

1. Open this Actor in [Apify Console](https://console.apify.com/actors/odjUi8d99yfSl3ghv) and click **Start**.
2. Wait for the run to finish (a few requests, at least 2 seconds apart).
3. Open the default dataset. Use the **Overview** view for normalised fields, or **Raw OCDS** for the source release.
4. Export the dataset as JSON, CSV, or Excel from the dataset tab if you want a file.

If one source returns nothing in sample mode, the other source is used to fill up to `maxResults`, so the first run still shows the output shape.

### Input examples

Only fields from this Actor's input schema are used. `cpvCodes` and `keywords` are applied locally after each fetch (the source APIs have no keyword parameter; CPV is a prefix match, so `72` matches `72000000`). `stages` is passed through to each source. Filtered-out releases are not written and not charged.

#### Daily IT bid alerts (CPV 72 / 48)

```json
{
  "sources": ["fts", "contractsFinder"],
  "mode": "incremental",
  "maxResults": 2000,
  "cpvCodes": ["72", "48"],
  "watermarkStoreName": "uk-tenders-ocds-watermarks"
}
```

#### Awards only, for competitor tracking

```json
{
  "sources": ["fts", "contractsFinder"],
  "mode": "incremental",
  "maxResults": 2000,
  "stages": ["award"],
  "watermarkStoreName": "uk-tenders-ocds-watermarks"
}
```

`award` is accepted by both sources. (`implementation` is Contracts Finder only; Find a Tender is skipped if none of the selected stages apply to it.)

#### Keyword search

```json
{
  "sources": ["fts", "contractsFinder"],
  "mode": "incremental",
  "maxResults": 2000,
  "keywords": "cloud hosting",
  "watermarkStoreName": "uk-tenders-ocds-watermarks"
}
```

`keywords` is a case-insensitive substring match against title, description, buyer name, and ocid.

### Output

One dataset item per OCDS release. Fields below match the dataset schema and the FTS fixture used in tests (`tests/fixtures/fts_release_package_sample.json`).

| Field | Meaning |
| --- | --- |
| `source` | `fts` or `contractsFinder`. Cross-source ocids are not comparable. |
| `ocid` | Open Contracting ID. FTS uses `ocds-h6vhtk-`; Contracts Finder uses `ocds-b5fd17-`. |
| `releaseId` | OCDS release `id`. Unique with `ocid` within a source. |
| `date` | Release date from the source. |
| `tag` | OCDS tags, e.g. `tender`, `award`. |
| `title` | Tender title. |
| `description` | Tender description (emails / obvious UK phones in free text are redacted). |
| `status` | Tender status. |
| `buyerName` / `buyerId` | Buyer organisation name and id. |
| `cpvCodes` | CPV codes collected from the release. |
| `valueAmount` / `valueCurrency` | First amount found on tender, award, or contract value. |
| `noticeUrl` | Notice page URL when the source provides one (FTS can also be derived from the release id). |
| `language` | Release language, when present. |
| `ocdsPackageVersion` | Value of the release package `version` field. |
| `license` | Upstream licence URL from the package, when present. |
| `licenseUrl` | [OGL v3.0](https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/) URL. |
| `attribution` | OGL attribution sentence. |
| `unofficialDisclaimer` | Unofficial-status notice stored on every item. |
| `raw` | Present only when `includeRaw` is `true`. Source release after personal contact fields are stripped and emails / obvious phones in free text are redacted. Default and sample output omit this field. |

Short sample (FTS fixture; `raw` omitted, matching the default `includeRaw`: `false`):

```json
{
  "source": "fts",
  "ocid": "ocds-h6vhtk-078a41",
  "releaseId": "098250-2026",
  "date": "2026-10-19T01:21:52+01:00",
  "tag": ["award", "contract"],
  "title": "Medical Insurance Services for British Embassy Doha",
  "status": "complete",
  "buyerName": "Foreign, Commonwealth and Development Office",
  "buyerId": "GB-PPON-PXRR-8771-PHVX",
  "cpvCodes": ["66512200"],
  "valueAmount": 81347.09,
  "valueCurrency": "GBP",
  "noticeUrl": "https://www.find-tender.service.gov.uk/Notice/098250-2026",
  "language": "en",
  "ocdsPackageVersion": "1.1",
  "licenseUrl": "https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/",
  "attribution": "Contains public sector information licensed under the Open Government Licence v3.0."
}
```

Contracts Finder items use the same shape (`source`: `contractsFinder`). Some fields can be `null` when the source omitted them — the CF fixture, for example, has no amount, so `valueAmount` / `valueCurrency` are `null`.

### Incremental mode and Apify schedules

Use this for daily bid alerts (or any other interval you set on the schedule).

1. Set `mode` to `incremental`, keep a stable `watermarkStoreName` (default `uk-tenders-ocds-watermarks`), and raise `maxResults` so a day's new notices fit in one run.
2. Save that input as a task, or use it directly.
3. In Apify Console, create a [Schedule](https://docs.apify.com/platform/schedules) that starts that task (daily is the usual choice for alerts).

How the watermark works:

- Per-source timestamps are stored in the named key-value store (`from`, `completedAt`), plus a bounded list of recently seen `ocid|releaseId` keys.
- The first incremental run with no watermark looks back **24 hours**.
- A 60-second overlap window is applied on completed runs; keys already seen in that window are not pushed or charged again.
- If a run stops early (`maxResults` or the run **max charge**), the watermark is **not** advanced, so the next run retries the same window.
- A Contracts Finder page that yields zero releases does not advance that source's watermark (an empty HTTP 200 can be throttling, not "no notices").

There is no cross-source de-duplication. The same procurement can appear once from Find a Tender and once from Contracts Finder.

### Cost

Pay per event. Live prices are set in Apify Console.

Each OCDS release is charged once **before** it is written to the dataset:

- `ocds-release`: **$2.00 per 1,000** (`$0.002` per release). Used for `dateRange` and `incremental`.
- `ocds-release-trial`: **pending** Console pricing event. Used **only** in `sample` mode (max 10 notices). Until that event exists in Console, sample runs still save results: Apify SDK 4.1.0 does not call the platform charge API for an unknown event (it logs a warning) and still reports `charged_count`; this Actor then writes the item. The same helper already writes the item when `charged_count` is 0 (non-PPE runs).

Actor start (`apify-actor-start`) is `$0.00005` per run (Apify default; not charged from Actor code).

Conversion only: $0.002 per release on `ocds-release`, so 500 releases a day is about $1 a day, or about $30 over 30 days.

Set a maximum cost per run in the run options. When that **max charge** is reached, the Actor stops and does not write further unbilled items.

### Personal data and licence

[OGL v3.0](https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/) does not cover personal data. This Actor minimises contact fields by default:

- Removed from `parties`, `buyer`, and `tender.procuringEntity`: contact-point `name`, `email`, `telephone`, `faxNumber`, and similar keys (`phone`, `mobile`, `givenName`, `familyName`, `contactName`). Organisation-level `email` / `telephone` keys on those objects are also dropped.
- Kept: organisation name, address, identifiers, and `contactPoint.url`.
- In listed free-text fields (for example tender or award `description`), email addresses and obvious UK / `+44` phone numbers are replaced with `[REDACTED_EMAIL]` / `[REDACTED_PHONE]`. Identifier fields are left intact. A value that is entirely one `http(s)` URL is not rewritten.

Free-text redaction is rule-based and can miss unusual formats. Check the data before you republish it.

Contains public sector information licensed under the [Open Government Licence v3.0](https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/). Upstream packages also set `license` to the OGL URL and `publicationPolicy` to [Open Contracting](https://www.gov.uk/government/publications/open-contracting).

### Limitations

- **No cross-source de-duplication.** FTS (`ocds-h6vhtk-…`) and Contracts Finder (`ocds-b5fd17-…`) items are separate. Within one source, duplicates are dropped by `ocid` + release `id`.
- **Data may lag the source.** Output is only as current as the Find a Tender and Contracts Finder OCDS endpoints, and can lag the websites.
- **Attachments and documents are not included.** Notice page URLs may appear in `noticeUrl` and inside `raw`; files behind those links are not downloaded.
- **Rate limits.** Requests are serial, at least 2 seconds apart, at most 100 items per page. FTS `429`/`503` responses are retried after `Retry-After`. Contracts Finder over-limit `403` waits about 5 minutes; two in a row abort the run.
- **OCDS versions.** Contracts Finder packages say `1.1` but follow a 1.0 base with some 1.1 additions. Fields can be missing; both sources are parsed defensively.
- **Scope.** Published notices from the two public OCDS endpoints only. No draft notices, no Contracts Finder OAuth endpoints.
- **Not legal advice.** Check licence terms and personal-data obligations for your own use.

### FAQ and contact

**Is this a UK government product?** No. It is unofficial. Find a Tender and Contracts Finder are named only as the data sources.

**Does a dataset item include personal contact fields?** No. Those keys are dropped or redacted as described above. Organisation names and addresses remain.

**Can I filter by CPV, keyword, and stage?** Yes. `cpvCodes` (prefix), `keywords` (local substring), and `stages` (`planning`, `tender`, `award`, and Contracts Finder `implementation`).

**How do daily bid alerts work?** Incremental mode plus an Apify Schedule, with `maxResults` high enough for the day's volume.

**Will I pay for notices that fail the CPV or keyword filter?** No. A release is charged only if it is accepted and about to be written.

**Where should I report a problem?** Use the **Issues** tab on this Actor's Apify Store page: [apify.com/elekezem/uk-tenders-ocds/issues](https://apify.com/elekezem/uk-tenders-ocds/issues).

# Changelog

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

# Actor input Schema

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

Which official UK sources to query. "fts" = Find a Tender (high-value notices), "contractsFinder" = Contracts Finder (lower-value, England). Default: both.

## `mode` (type: `string`):

"sample" = latest notices from the past 7 days (quick preview, default, max 10). "dateRange" = notices in dateFrom..dateTo (set both). "incremental" = only notices new since the previous incremental run (for scheduled alerts).

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

Required when mode="dateRange". ISO date, e.g. "2026-10-01" or "2026-10-01T00:00:00". Ignored in other modes.

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

Optional end for mode="dateRange", inclusive, ISO date, e.g. "2026-10-07". If omitted, Find a Tender is queried through now; Contracts Finder omits publishedTo so that API defaults to now. Ignored in other modes.

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

Upper limit on returned notices (1–100000, default 10). You pay per returned notice, so start with 10–50. In sample mode this is capped at 10.

## `includeRaw` (type: `boolean`):

If false (default), each item omits the large "raw" OCDS object to save tokens. Set true only when you need the full source release.

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

Optional filter. "tender" = open opportunities to bid on; "award" = contracts already awarded; "planning" = early market engagement; "implementation" = contract changes (Contracts Finder only). Empty = all.

## `keywords` (type: `string`):

Optional single phrase, case-insensitive, matched in title, description and buyer name. Example: "cyber security". Not a boolean query.

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

Optional list of CPV codes or prefixes, e.g. \["72"] = IT services, \["45"] = construction. Matches any.

## `watermarkStoreName` (type: `string`):

Only for mode="incremental". Name of the key-value store remembering the last seen date. Leave default unless running several separate alerts.

## `minRequestIntervalSeconds` (type: `integer`):

Seconds between requests to each source, min 2. Leave default.

## Actor input object example

```json
{
  "sources": [
    "fts",
    "contractsFinder"
  ],
  "mode": "sample",
  "maxResults": 10,
  "includeRaw": false,
  "watermarkStoreName": "uk-tenders-ocds-watermarks",
  "minRequestIntervalSeconds": 2
}
```

# Actor output Schema

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

No description

## `overview` (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 = {
    "sources": [
        "fts",
        "contractsFinder"
    ],
    "mode": "sample",
    "maxResults": 10,
    "includeRaw": false,
    "watermarkStoreName": "uk-tenders-ocds-watermarks",
    "minRequestIntervalSeconds": 2
};

// Run the Actor and wait for it to finish
const run = await client.actor("elekezem/uk-tenders-ocds").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": [
        "fts",
        "contractsFinder",
    ],
    "mode": "sample",
    "maxResults": 10,
    "includeRaw": False,
    "watermarkStoreName": "uk-tenders-ocds-watermarks",
    "minRequestIntervalSeconds": 2,
}

# Run the Actor and wait for it to finish
run = client.actor("elekezem/uk-tenders-ocds").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": [
    "fts",
    "contractsFinder"
  ],
  "mode": "sample",
  "maxResults": 10,
  "includeRaw": false,
  "watermarkStoreName": "uk-tenders-ocds-watermarks",
  "minRequestIntervalSeconds": 2
}' |
apify call elekezem/uk-tenders-ocds --silent --output-dataset

```

## MCP server setup

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

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/odjUi8d99yfSl3ghv/builds/0WLhSfcJmcXAoNFnz/openapi.json
