datagrit

datagrit › Public procurement › UK Contract Expiry Radar - Recompete Leads

Public procurement

UK Contract Expiry Radar - Recompete Leads

UK public contracts ending soon with incumbent supplier, buyer, value and contact - recompete leads from Contracts Finder award notices.

Run it on Apify StoreUse the APIfrom $3.50 per 1,000 results + $10 per run · no code needed
from $3.50 per 1,000 results + $10 per runpay only for expiring contracts you get
JSON · CSV · Excelexport or call via API
Scheduled runsdaily or weekly feeds with Apify schedules
v0.11updated 2026-10-05

UK Contract Expiry Radar turns Contracts Finder award notices (the UK government portal for contracts, without Find a Tender) into a list of public contracts that are about to end. For every contract it returns the incumbent supplier (with Companies House number), the buying organisation, the award value, the contract start and end dates, and how many days are left. Filter by expiry window, CPV code, keyword and value, then export the result as JSON, CSV or Excel, call it through the Apify API, or plug it into n8n, Make and AI agents through MCP.

Who is it for?

How to use it

  1. Set the expiry window: for example 0 to 12 months to list contracts that end within a year, or 6 to 18 months to skip the ones already too close to tender.
  2. Optionally add keywords (title, description, buyer or supplier), CPV prefixes (for example 72 for IT services or 45 for construction) and a minimum value.
  3. Choose how far back to read award notices, then run the Actor.
  4. Download the dataset or fetch it from the API.

Contracts Finder publishes roughly one to two hundred award notices per working day (1 to 2 API requests) and very few at weekends (0 to 9 per day), so a longer lookback window reads more notices and takes longer. The default is 7 days with a 240-second time budget; raise both together for a longer scan.

What data do you get?

Each record includes the notice title and description, buyer name, locality and website, incumbent supplier with Companies House number and company size, all suppliers for multi-supplier awards, award value and currency, award and publication dates, contract start and end dates, days and months to expiry, contract length, main and additional CPV codes, procurement method, a framework call-off flag taken from the procurement procedure field, a separate flag for call-off wording in the text, an extension-clause flag, SME suitability, a link to the original notice and a dispute marker (recordDisputed) for the rare award that was published twice with conflicting values. The buyer contact email is off by default and can be switched on in the input.

Is it legal to use this data?

The Actor reads only information the UK government publishes openly under the Open Government Licence through the Contracts Finder API. It does not log in or bypass any access control. Contact details, if you enable them, are personal data in some cases: you are responsible for using them in line with applicable data protection law. This description is not legal advice.

Output fields

Every result is one flat record, so it drops straight into a spreadsheet, a database or a CRM.

FieldTypeDescriptionExample
querystringThe keyword from your input that matched this contract; null when you used no keywords.software
idstringStable identifier of the award (OCDS award id). One notice can hold several awards. Empty only on the single status row emitted when nothing matched.ocds-b5fd17-82384ae3-342e-49e5-aba3-f07a7b4a2787-1
ocidstringOpen Contracting ID of the whole procurement process, shared by all its notices.ocds-b5fd17-82384ae3-342e-49e5-aba3-f07a7b4a2787
titlestringTitle of the contract as published by the buyer. Empty only on the single status row emitted when nothing matched.UOW995b - Virtual Placement Software - AWARD
descriptionstringContract description: the award description and, when the notice also publishes a different tender description, that text after it. Whitespace collapsed, cut at 800 characters.The University of Worcester is seeking to procure a Virtual Placement Simulation
sourceUrlstringLink to the award notice on Contracts Finder.https://www.contractsfinder.service.gov.uk/Notice/1fcc1776-84a0-4171-9c5b-f24532
buyerNamestringPublic body that awarded the contract, i.e. who will buy again.University Of Worcester
buyerIdstringContracts Finder identifier of the buyer organisation.GB-CFS-44290
buyerLocalitystringTown or city of the buyer.Worcester
buyerPostcodestringPostcode of the buyer.WR2 6AJ
buyerWebsitestringWebsite of the buyer when published.https://www.worcester.ac.uk/
buyerContactEmailstringContact email the buyer published on the notice. Filled only when you turn on 'Include buyer contact email'.procurement@example.org.uk
supplierNamestringSupplier that won the contract (first supplier when there are several).Insight Direct (UK) Ltd
supplierCountintegerNumber of suppliers awarded on this award, more than 1 for multi-supplier frameworks.1
allSuppliersstringNames of all suppliers separated by semicolons; null when there is a single supplier.Alpha Ltd; Beta Ltd
supplierCompanyNumberstringCompanies House number of the first supplier when the notice gives one.02343760
supplierScalestringSize class of the first supplier as declared on the notice: sme or large.sme
supplierVcsebooleanWhether the first supplier is a voluntary, community or social enterprise; null when not stated.false
awardValuenumberAwarded contract value in the currency field, as published; null when the buyer did not disclose a value (the source publishes 0 in that case).318979.29
currencystringCurrency of the award value.GBP
awardDatestringDate the contract was awarded, ISO 8601.2026-08-20T23:00:00.000Z
publishedAtstringWhen the award notice was published on Contracts Finder, ISO 8601.2026-09-23T12:09:20.000Z
contractStartstringStart of the contract period, ISO 8601.2026-09-30T23:00:00.000Z
contractEndstringEnd of the contract period as published, ISO 8601. This is the date the recompete is driven by.2029-09-30T22:59:59.000Z
daysToExpiryintegerWhole days from the run date to the contract end; negative when the contract has ended.1096
monthsToExpirynumberMonths from the run date to the contract end, one decimal.36
contractLengthMonthsintegerLength of the contract period in months.36
cpvCodestringMain Common Procurement Vocabulary code of the contract.48100000
cpvDescriptionstringPlain-language description of the main CPV code.Industry specific software package
additionalCpvCodesstringFurther CPV codes on the notice, comma separated.48000000, 72000000
categorystringMain procurement category: goods, services or works.services
procurementMethodstringOCDS procurement method: open, selective, limited or direct.selective
procurementMethodDetailsstringProcedure as described by the buyer, for example Restricted procedure or Call-off from a framework agreement.Restricted procedure
frameworkCallOffbooleanTrue when the procurement procedure field of the notice says call-off from a framework agreement or from a dynamic purchasing system. Based only on that field, not on the wording of the title or description.false
callOffMentionedbooleanTrue when the title or description contains the words call-off. It is a mention in the text, not a statement of the procedure: use frameworkCallOff for the procedure.false
extensionMentionedbooleanTrue when the title or description mentions an extension, renewal or optional period, so the real end date may be later.false
smeSuitablebooleanWhether the buyer flagged the contract as suitable for small and medium enterprises; null when not stated.false
recordDisputedbooleanTrue when the award was published more than once and the copies disagree on a value (both non-empty and different). The row carries one of them; the other is in disputedOtherValues. A missing value (0, null) in one copy is filled from the other and is not a dispute.false
disputedFieldsstringComma separated names of the fields the duplicate copies disagree on; null when recordDisputed is false.awardValue
disputedOtherValuesstringThe values of the disputed fields that were not used in this row, as field=value pairs separated by semicolons; null when recordDisputed is false.awardValue=504113
foundbooleanFalse only for the single status row emitted when nothing matched your filters (not billed).true
scrapedAtstringISO 8601 timestamp of the run.2026-09-30T00:00:00.000Z

Sample record

{
  "query": "software",
  "id": "ocds-b5fd17-82384ae3-342e-49e5-aba3-f07a7b4a2787-1",
  "ocid": "ocds-b5fd17-82384ae3-342e-49e5-aba3-f07a7b4a2787",
  "title": "UOW995b - Virtual Placement Software - AWARD",
  "description": "The University of Worcester is seeking to procure a Virtual Placement Simulation Software Platform.",
  "sourceUrl": "https://www.contractsfinder.service.gov.uk/Notice/1fcc1776-84a0-4171-9c5b-f245323ded52",
  "buyerName": "University Of Worcester",
  "buyerId": "GB-CFS-44290",
  "buyerLocality": "Worcester",
  "buyerPostcode": "WR2 6AJ",
  "buyerWebsite": "https://www.worcester.ac.uk/",
  "buyerContactEmail": "procurement@example.org.uk",
  "supplierName": "Insight Direct (UK) Ltd",
  "supplierCount": 1,
  "allSuppliers": "Alpha Ltd; Beta Ltd",
  "supplierCompanyNumber": "02343760",
  "supplierScale": "sme",
  "supplierVcse": false,
  "awardValue": 318979.29,
  "currency": "GBP",
  "awardDate": "2026-08-20T23:00:00.000Z",
  "publishedAt": "2026-09-23T12:09:20.000Z",
  "contractStart": "2026-09-30T23:00:00.000Z",
  "contractEnd": "2029-09-30T22:59:59.000Z",
  "daysToExpiry": 1096,
  "monthsToExpiry": 36,
  "contractLengthMonths": 36,
  "cpvCode": "48100000",
  "cpvDescription": "Industry specific software package",
  "additionalCpvCodes": "48000000, 72000000",
  "category": "services",
  "procurementMethod": "selective",
  "procurementMethodDetails": "Restricted procedure",
  "frameworkCallOff": false,
  "callOffMentioned": false,
  "extensionMentioned": false,
  "smeSuitable": false,
  "recordDisputed": false,
  "disputedFields": "awardValue",
  "disputedOtherValues": "awardValue=504113",
  "found": true,
  "scrapedAt": "2026-09-30T00:00:00.000Z"
}

Input

FieldNameTypeWhat it does
queriesKeywordsarrayOptional. A contract is returned when its title, award and tender descriptions, buyer or supplier name contains any of these keywords (case-insensitive; the description is searched in full even though the result shows it shortened to 800 characters). Leave empty to list every contract in the expiry window. Keywords narrow results but make the scan read more notices.
expiresAfterMonthsEnds no sooner than (months from today)integerEarliest contract end date, counted in months from today. 0 includes contracts that end from today on; use 6 to skip contracts ending in the next six months.
expiresBeforeMonthsEnds no later than (months from today)integerLatest contract end date, counted in months from today. 12 lists contracts that come up for recompete within a year.
publishedLookbackDaysAward notices published in the last N daysintegerHow far back to read Contracts Finder award notices, newest first; the scan stops as soon as Maximum results is reached. Roughly one to two hundred award notices are published per working day (1 to 2 API requests; very few at weekends) and the source allows 12 requests per 120 seconds (the Actor paces itself), so the default 7 days (11 to 12 requests, about 15 seconds) normally never waits, and each further working day of lookback adds roughly 20 seconds of pause. Long contracts (3-5 years) need a long window to appear as expiring - raise this together with Maximum run time.
maxRunSecondsMaximum run time (seconds)integerHard time budget for reading the source. When it is reached the Actor stops, keeps every contract found so far and says how many lookback days it covered in the run status. 0 removes the limit (needed for long lookback windows with strict filters).
cpvPrefixesCPV code prefixesarrayOptional. Keep only contracts whose CPV code (main or additional) starts with one of these prefixes, for example 72 for IT services or 45 for construction.
minValueMinimum contract value (GBP)integerSkip contracts with an awarded value below this amount. Awards published without a value (the source reports 0) are skipped as soon as a minimum above 0 is set. 0 keeps everything, including awards without a value.
includeFrameworkCallOffsInclude framework call-offsbooleanCall-offs from frameworks such as G-Cloud and from dynamic purchasing systems are usually short and re-bought through the same route. Turn off to drop every notice whose procurement procedure says call-off from a framework agreement or a dynamic purchasing system. Notices that only mention call-off in their text are kept.
includeContactEmailInclude buyer contact emailbooleanAdds the contact email the buyer published on the notice. Off by default; when you turn it on, you are responsible for using it in line with data protection rules.
maxItemsMaximum resultsintegerStop after this many contracts.
proxyConfigurationProxy configurationobjectOptional proxy. Contracts Finder is a public government API and normally needs none; enable Apify Proxy only if the run fails with a message that the source is throttling your IP address.

Call it from your code

Run the Actor and get the results in one request. Replace YOUR_APIFY_TOKEN with the token from your Apify account settings.

curl -X POST "https://api.apify.com/v2/acts/datagrit~uk-contract-expiry-radar/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"expiresAfterMonths":0,"expiresBeforeMonths":12,"publishedLookbackDays":7,"maxItems":20}'
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('datagrit/uk-contract-expiry-radar').call({
  "expiresAfterMonths": 0,
  "expiresBeforeMonths": 12,
  "publishedLookbackDays": 7,
  "maxItems": 20
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, items[0]);

Install with npm i apify-client.

from apify_client import ApifyClient
import os

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("datagrit/uk-contract-expiry-radar").call(run_input={
  "expiresAfterMonths": 0,
  "expiresBeforeMonths": 12,
  "publishedLookbackDays": 7,
  "maxItems": 20
})
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(len(items), items[0] if items else None)

Install with pip install apify-client.

Runnable Node.js and Python examples: github.com/getdatagrit/uk-contract-expiry-radar.

Frequently asked questions

What if the run returns nothing?

The run status tells you why. "Published no award notices in the last N days" means the source had nothing in the window (it is nearly empty at weekends, so a 1-day lookback on a Saturday or Sunday is often empty): raise the lookback days. If the source answers with empty days on working days while it is clearly throttling (it does this when one IP sends many requests), the Actor waits up to 3.5 minutes per day (within "Maximum run time"), re-checking against a control request, and then either continues without that day (named in the run status) or, if nothing could be read at all, fails with a clear message instead of returning an empty result: run it again later or turn on a proxy. "No contract matched your filters" means notices were read but none passed your expiry window, keywords, CPV or value filters: widen those.

Does it list contracts that are not yet awarded?

No. It covers awarded contracts and shows when each one ends. Use the expiry window to find recompete opportunities early.

Why do some contracts have no end date?

Some notices are published without a contract period. Those are skipped, because the expiry window cannot be applied.

How current is the data?

Every run reads the live Contracts Finder API for the lookback window you choose.

How long does a run take?

Contracts Finder allows 12 requests per 120 seconds. A working day costs 1 or 2 requests (a day with more than 100 notices needs a second page), a weekend day 1. The default 7-day window comes to 11 to 12 requests (measured on the live feed: 11 or 12 requests and several hundred award records per week; the exact number changes from week to week), which fits inside the limit: reading the whole week takes about 15 seconds with no pause. The count is not fixed: a single day above 200 notices (the busiest measured day had 193) would add a request, and the 13th request waits about two minutes for the window to reopen - the run then takes around 2.5 minutes, still inside the default 240-second budget. Windows longer than a week need more than 12 requests, and then the Actor pauses about two minutes after every further 12 requests (roughly 20 seconds per extra working day). The run also stops at the run-time budget (240 seconds by default): the results found so far are kept and the run status says how many lookback days were read. For a longer lookback window raise both the lookback days and the maximum run time (0 removes the limit).

Which sources does it cover?

Contracts Finder only. Contracts published only on Find a Tender (the above-threshold notices) are not included, and some other UK tender Actors on the Store read both portals. The nearest Actor on the same source, dataio/uk-public-contract-awards, also reads only Contracts Finder and filters by the days left to contract end, so the expiry view is not what sets this Actor apart. Choose this Actor when you care about how the award rows are built: a call-off flag taken from the procurement procedure field (`frameworkCallOff`) next to a separate flag for call-off wording in the text (`callOffMentioned`), an extension-clause flag (`extensionMentioned`), one row per award with corrected and republished notices merged field by field and conflicting values marked (`recordDisputed`), and an expiry window counted in months ahead.

What if a notice was corrected and published twice?

You get one row per award, merged field by field. On the live feed the two copies of a duplicated award carry the same award date (12 of 12 pairs in a 7-day scan), so the award date cannot tell which copy is right. The rules are: - A missing value never beats a real one. When one copy has no award value (the source publishes 0, shown as null), no end date or no supplier and the other has the data, the row carries the data (in the scan above one award was published once with a value of 0 and once with 150,263.40 GBP; the row shows 150,263.40). Days, months to expiry and contract length are recomputed from the merged dates. - When both copies have a value and the values differ (for example an award value of 504,113 against 50,411,351, or an end date a year apart), the Actor cannot know which is right. It keeps the copy with the later award date, or the later one in the feed when the dates are equal, and marks the row: `recordDisputed` is true, `disputedFields` lists the fields and `disputedOtherValues` shows the values it did not use (for example `awardValue=504113`). Check those rows against the notice link before you rely on the value; in the 7-day scan 5 of 586 rows were disputed. - `frameworkCallOff` is recomputed from the procedure of the kept copy; `callOffMentioned` and `extensionMentioned` are true when either copy says so. A newer publication day always wins over an older one.

Can I schedule it?

Yes, use Apify schedules or call the Actor from your own workflow.

Something looks wrong.

Open an issue with the input you used; layout or API changes at the source are fixed quickly.

Try UK Contract Expiry Radar - Recompete Leads on Apify

Related Actors

Company data

French Company Finder - Sirene Financials

French company lead lists from Sirene screened by net result and revenue, with net margin, size, matching establishment and optional directors.

from $5.60 / 1,000 results
Company data

Poland KRS New Company Registrations Feed

Newly registered Polish companies, foundations and associations from the official KRS court register: NIP, address, PKD, capital, email, with filters and change detection.

from $10.50 / 1,000 results