UK Tenders API — Find a Tender + Contracts Finder in One Feed
Pricing
from $3.00 / 1,000 notice records
UK Tenders API — Find a Tender + Contracts Finder in One Feed
UK public-sector tenders and awards from Find a Tender + Contracts Finder in one feed; notices on both sources merged when title, buyer, region and value match. Filter by CPV, value, region, keyword; daily onlyNew alerts, including amendment re-sends.
Pricing
from $3.00 / 1,000 notice records
Rating
0.0
(0)
Developer
Celest Jux
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
One clean, typed JSON feed of UK public-sector procurement notices from both official UK
sources: Contracts Finder (England, mostly below-threshold) and Find a Tender (UK-wide,
above-threshold, including every Procurement Act 2023 notice). A notice carried by both sources is
merged into a single item when its title, buyer, region and value match. A source that fails to
load stops the run before anything is written, so you never get a half-read feed. Filter by CPV code, value, region and keyword, and run it
daily with onlyNew for tender alerts/monitoring: only what's new since yesterday, plus amendment
re-sends (v1.1) when a delivered notice's deadline, value or status later changes.
Built on the two official, keyless OCDS APIs. No HTML scraping, no proxy, nothing to bypass.
Contains public sector information licensed under the Open Government Licence v3.0. https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/
What you get
| Field | Example | Notes |
|---|---|---|
id | find_a_tender:ocds-h6vhtk-077b05:tender | source:ocid:stage — dedupe key, used by onlyNew |
ocid | ocds-h6vhtk-077b05 | OCDS procurement id (per source) |
source | find_a_tender | or contracts_finder |
alsoIn | contracts_finder or null | the same notice is also published on the other service |
stage | tender | tender, award or planning |
noticeType | UK4 / tenderAmendment | FTS form code when given, else the OCDS release tag |
title, description | ||
buyer | {"name": "East Riding of Yorkshire Council", "id": "GB-FTS-33279", "region": "Yorkshire and the Humber", "contactEmail": "…"} | |
cpv | [{"code": "85000000", "description": "Health and social work services"}] | every CPV code on the notice |
valueAmount, valueCurrency | 8400000, GBP | net value; awards without a tender value = sum of award values |
valueMin, valueMax | 8000000, 8400000 | Contracts Finder value ranges |
publishedDate | 2026-09-25T23:16:45+01:00 | ISO 8601; latest publication/edit of the notice |
tenderDeadline | 2026-10-26T12:00:00+00:00 | |
contractStart, contractEnd | ||
region | London | delivery region; FTS NUTS codes are turned into region names |
procedureType | Open procedure | |
isSmeSuitable, isVcseSuitable | true / false / null | buyer's suitability flags |
awards | [{"supplierName": "Tarmac Trading Limited", "supplierId": "GB-PPON-…", "value": 84000000, "date": "2026-08-26T00:00:00+01:00"}] | award stage only (empty otherwise) |
noticeUrl | https://www.find-tender.service.gov.uk/Notice/091204-2026 | human page |
licence | OGL-UK-3.0 | on every item |
scrapedAt | UTC | |
isUpdate | true / false | onlyNew: true when this item replaces an earlier delivered release of the same notice |
previousReleaseId | "091204-2026" / null | onlyNew update: the previously delivered release id, when known; null otherwise |
extraFields | {"legalBasis": "2023/54", "regionCodes": ["UKE12"], "alsoInUrl": "…"} | release tags, release id, legal basis (Procurement Act 2023 vs PCR 2015), category, gross value, first-published date, raw region codes, tender status, submission URL, buyer phone, the other source's id + URL |
Missing values are null, never empty strings.
Input examples
IT tenders over £100k this week:
{ "cpvPrefixes": ["72"], "minValue": 100000, "includeNoValue": false, "publishedFrom": "7 days" }
Construction in London:
{ "cpvPrefixes": ["45"], "regions": ["London"] }
Daily alert (schedule once a day):
{ "stages": ["tender"], "publishedFrom": "2 days", "onlyNew": true }
Last month's awards with supplier names:
{ "stages": ["award"], "publishedFrom": "1 month", "maxItems": 5000 }
How filters work
publishedFrom/publishedTo—YYYY-MM-DDor relative (7 days,2 weeks,1 month= that long before today, UK time). Default7 days. Contracts Finder filters on "published or last edited"; Find a Tender only offers "last updated", which is what it uses.stages— Contracts Finder filters stages server-side. Find a Tender's own stage filter drops every Procurement Act 2023 notice (checked 2026-09-27:stages=tenderreturned only old-regime PCR 2015 notices), so this actor reads all Find a Tender notices in the window and filters stages itself. Amendments, updates and cancellations belong to their stage; contract-performance (implementation) notices are out of scope.keyword,cpvPrefixes,minValue/maxValue,regions— neither API supports these, so they are applied by the actor (client-side) after download.cpvPrefixes: matches if any CPV code on the notice starts with any prefix.- Value filters compare
valueAmountin GBP. Notices with no value (or a non-GBP value) are kept only ifincludeNoValueis on (default on). regions: region names (London,North West,Scotland, …) or NUTS/ITL code prefixes (UKI,UKD3,TLI). Matches delivery regions and the buyer's region.
- One item per procurement and stage per source: when a notice has been amended in the window, you
get the latest version (
noticeTypetells you it was an amendment). - Results from both sources are merged newest-first and cut at
maxItems.
Cross-source dedupe
The two services give the same notice different ocids and do not cross-reference each other. A
notice counts as the same in both when stage, title, buyer name and tender deadline match
(case/punctuation-insensitive) and that combination is unique within each source — buyers often
reuse one title for many separate notices (e.g. one council published 9 different taxi-route lots
under the same title in one week), so ambiguous matches are never merged. Records with conflicting
GBP values are also kept separate. Cross-source matching runs only when both source windows were
fully read; a maxItems or charge cap may leave two copies in the limited output. The Find a Tender
copy is kept, alsoIn is set to contracts_finder, and the Contracts Finder id and URL go to
extraFields.alsoInId / alsoInUrl. Overlap is small: since the Procurement Act 2023 most
above-threshold notices appear on Find a Tender only.
Contracts Finder sometimes prefixes its title with an internal reference code (e.g.
CA18485 - Hopwood Hall College - Provision of HR & Payroll SystemHopwood Hall College - Provision of HR & Payroll System). The matching step
strips a leading reference-code prefix — one of the letter families actually seen across every
Contracts Finder title captured during this actor's development (C, CA, ENQ, PR, PS,
UOW, each followed by 2+ digits; T0nnn on its own, the exact shape observed — not T plus any
digits, which would also catch unrelated titles like "T20 - ...") — then a -/–/:
separator — before comparing titles, but only for the match; the notice's own title field always
keeps the prefix as published. This is deliberately conservative: the letter families are
constrained to what was actually observed (plus an explicit denylist for standard/product names
like ISO or NHS reference numbers that could otherwise look like a code), a prefix followed only by
a space (no punctuation), a location prefix (GB-London: ...), or a title that's nothing but a
reference code are all left alone rather than guessed at. It reduces common duplicates from this
pattern; it doesn't guarantee every duplicate is caught, and in principle two distinct notices
could still share a match key if they coincidentally matched on stage, buyer and deadline too.
onlyNew
Pushed notices are fingerprinted in the named key-value store celestjux-uk-tenders-seen (it
persists between runs): {releaseId, publishedDate} per notice id. A later run skips a notice only
when the store holds the same release (same release id, or a same-or-older publishedDate) — a
genuinely later release of an already-delivered notice (an amendment that moves the deadline,
changes the value, or cancels the tender) is pushed again, with isUpdate: true and
previousReleaseId set to the release it replaces. Set resendUpdates: false to go back to the old
behaviour: any previously delivered notice id is always skipped, updates included.
A copy from either source of an already-delivered notice is still skipped when both have the same GBP value and the first source window was fully read (unchanged — see Cross-source dedupe above).
A tenderCancellation of an already-delivered notice arrives as an update too (isUpdate: true,
previousReleaseId set) — check noticeType to tell it apart from an amendment.
The newest-release guarantee only covers pages actually read: once a source has enough candidates
to fill maxItems (or the charge cap), the run stops reading that source's remaining pages, so an
even-newer release of an already-kept notice past that cutoff is not seen. In practice the live
feed is newest-first, so this rarely bites.
Charging: a re-sent update is a normal record event like any other pushed item — amendments of a
delivered notice are delivered again, charged as a record; set resendUpdates=false to skip them.
To start over, delete the celestjux-uk-tenders-seen store.
Daily schedule → Slack / Sheets / webhook
- Save an input like the "daily alert" example as a task.
- Console → Schedules → daily (e.g. 07:00 Europe/London) → add the task.
- Integrations on the task: Slack (post new items), Google Sheets (append rows), or a webhook on "Run succeeded" pointing at your system; it receives the run with its dataset id.
Pricing
Pay per event: $0.003 per notice pushed (= $3 per 1,000). No start fee. Set maxTotalChargeUsd
to cap a run; it stops cleanly before going over.
Reliability
- Sequential requests, ≥ 1 s apart, identifying user agent.
- Timeouts and 5xx: 3 attempts with backoff.
- Find a Tender 429/503: honours
Retry-Afterup to 4 times per request. A malformedRetry-After(missing, non-numeric, negative,NaN/Infinity, or bigger than 300s) is never guessed at — it fails the run loud immediately, naming the URL and the raw header value. - All rate-limit waits — Find a Tender's
Retry-Afterwaits and a Contracts Finder 403 cooldown — count against one shared cap on total wait per run (default 600s/10min, setmaxRateLimitWaitSecto change it). If honouring a wait would push the total over that cap, the run fails loud before sleeping rather than after. A 429/403 with a usable, in-range wait usually just recovers on its own — the run only fails once the per-request cap (4) or the run-wide cap is actually reached. - After the first 429/403 of a run, the delay between pages increases for the rest of the run to ease off the source.
- Contracts Finder 403 (its rate limit): one 5-minute cooldown, then the run fails if it happens again — unchanged behaviour, now counted in the shared wait budget/stats above and triggering the same slower page pace as a Find a Tender 429.
- A source fetch or parse failure fails the run with the URL in the error, before any item is written — items are pushed only after every source has been read completely.
- Run stats include
rateLimitWaitsandrateLimitWaitSec(both sources combined) so you can see how much of a run's time went to honouring a rate limit.
Limits
- v1 has no Companies House enrichment of suppliers (planned v1.1: bring your own Companies House API key).
- Contracts Finder's planning stage is practically empty (0 notices in the week checked); pipeline notices come from Find a Tender.
- A long window on Find a Tender means many pages (roughly 500 notices of all types per weekday), since its stage filter cannot be used.