UK Tenders Scraper: Contracts Finder & Find a Tender Notices
Pricing
from $3.00 / 1,000 notices
UK Tenders Scraper: Contracts Finder & Find a Tender Notices
Search UK tenders and public sector contract notices from Find a Tender and Contracts Finder in one schema: buyer, value, CPV codes, dates, region. New-notice alerts for daily schedules and a renewals mode for contracts ending soon. Not affiliated with the Government.
Pricing
from $3.00 / 1,000 notices
Rating
0.0
(0)
Developer
Russell Yapp
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
1
Monthly active users
9 hours ago
Last modified
Categories
Share
UK Public Contracts Lookup: Find a Tender and Contracts Finder notices in one schema
You get UK public procurement notices published on Find a Tender and Contracts Finder (and Public Contracts Scotland if you choose it), one fixed row per notice.
A row holds the notice title, the buyer (an organisation), value, currency, CPV codes, regions, stage, dates, a link to the notice and notice ids. suppliers is reserved for organisation supplier names, pending a review; it is null in this version. A row has no contact, address or description field. A title is free text copied as published. Email addresses, UK phone numbers and a name written straight after cue words such as "contact", "FAO" or "write to" are replaced with [redacted] by pattern, so a name written another way, including a supplier named in a title, can remain. The notice link opens the source notice, which we do not filter.
A first run from the input form (maxItems prefilled at 3) costs up to $0.020: $0.005 for the run and $0.005 for each of the 3 newest notices, and usually takes a minute or two. Runs refused before anything is read (invalid input, or a window over 92 days) are not charged. A run that reads data and finds nothing still pays the $0.005 run fee.
Through the API, if you leave out maxItems, the cap is 1,000 notices, which can cost up to $5.005 ($20.005 in the renewals modes). The input form prefills 3. Set maxItems to what you need.
| Mode | You give | A question it answers |
|---|---|---|
search | Filters: keywords, buyers, cpvCodes, valueMin, regions, stages, deadlineFrom, a publication window | Which NHS tenders still open for bids are worth at least 100,000 pounds? (within the publication window you set, 31 days by default) |
pipeline | The search filters, and optionally expectedFrom and expectedTo | Which IT procurements do buyers expect to bring to tender in the next three months? (planning notices published in the window you set) |
lookup | noticeIds: up to 20 | What are the title, buyer, value and deadline of notice 094494-2026? |
alert | alertId and filters | Which highways tenders in the North West are new since my last run? |
profile | buyers: 1 to 10 | What did this buyer put out to tender and award on Contracts Finder and Find a Tender over the last 365 days? |
renewals | endWithinMonths (or endFrom and endTo) and filters | Which cleaning contracts in the South East have a stated end date in the next 12 months? |
renewalsAlert | alertId and the renewals filters | Which contracts have newly entered that list, or had their dates changed, since my last run? |
| publishedAt | source | title | buyer | value | currency | stage | deadline | regions |
|---|---|---|---|---|---|---|---|---|
| 2026-09-09T00:00:00Z | fts | Children Affected by Parental Offending - Expression of Interest | Bristol City Council | 80000 | GBP | tender | 2026-09-30T11:00:00Z | South West |
A default run (search, the last 31 days, both default services, with maxItems prefilled at 3) returns up to the 3 newest notices it can read and costs up to $0.020 ($0.005 for the run plus 3 x $0.005). It usually takes a minute or two. Find a Tender and Contracts Finder are read for the whole window from their published daily files, with today from their APIs (Find a Tender's file for yesterday appears by about 03:05 UTC, Contracts Finder's by about 02:00 UTC). API requests are paced at one every 15 seconds, so a run that reaches its cap of 12 API requests spends about 3 minutes on them, more if a service asks it to wait, and more with pcs (see Limits). RUN_SUMMARY names any day not covered. Raise maxItems in the input, up to 100,000, to get more.
Dates. A row carries the notice's publication time (publishedAt), not the time the Actor read it; there is no scrape date on the row. Each run's RUN_SUMMARY records startedAt and finishedAt, and notices are read during the run, from the Find a Tender and Contracts Finder daily bulk files and from their APIs (see Freshness and coverage), so a notice published after the run reads its day is not included.
When to use it, and when not to
Use it to:
- list the notices that match your filters (keywords, buyer, CPV code, value, region, stage, deadline);
- look up named notices by id;
- be told once about each new matching notice, on a schedule;
- list contracts whose notice states an end date in a window, and be told when one is new or its dates change (renewals).
Do not use it:
- to find the suppliers of a contract or to search by supplier:
suppliersis reserved for organisation supplier names, pending a review, and is null in this version; there is no supplier filter, by design; - for contact fields or the full notice text: there are no contact name, email, phone or description fields (a title can still contain a name);
- as proof that a notice does not exist, or as a real-time feed: it returns what the services published and what it could read within its request limits (see Freshness and coverage);
- for notices from services other than Find a Tender, Contracts Finder and (if you choose it) Public Contracts Scotland.
What you get
One row per notice, the same fields for every service. A missing value is null (or [] for lists), never an absent key.
| Field | Type | Meaning |
|---|---|---|
source | "fts", "cf" or "pcs" | The service the notice came from: Find a Tender, Contracts Finder or Public Contracts Scotland |
noticeId | string | Find a Tender id (094494-2026), Contracts Finder notice GUID, or Public Contracts Scotland notice id (SEP565172; SEP564648-1 for a notice split into lots) |
ocid | string or null | The contracting-process id. For a Find a Tender legacy form (the pre-Procurement Act forms, about 15% of its 2026 notices): null when the notice is read from its daily file, which carries no OCID (search, alert, renewals and profiles read past Find a Tender days from those files); set when search, alert or lookup read it from the API (today's notices, lookup); and in renewals and the renewals alert always null, because those modes read legacy notices from the daily files only. Find a Tender Procurement Act notices carry it on both routes, and pcs rows always carry theirs |
relatedOcids | array of strings | OCIDs of the same procurement on another service, taken only from explicit references in the notice. Nearly always [] |
reconcileKey | string or null | Probably the same procurement: join it yourself. A hash of the normalised buyer and title; two rows with the same key, from different services, are likely one procurement. It misses pairs worded differently, and generic titles from one buyer can collide. null if buyer or title is empty |
releaseId | string | Id of the notice version returned (Find a Tender: the notice id; Contracts Finder: {guid}-{n}, the latest; Public Contracts Scotland: the same as noticeId) |
title | string or null | Notice title, with email addresses, UK phone numbers and some contact names redacted by pattern. null for follow-up notices that have no title of their own |
buyer | string or null | Buyer organisation name as published |
value | number or null | Estimated value; for award and contract notices, the sum of the awarded or contracted amounts. null where currencies are mixed. As published, including placeholders |
currency | string or null | ISO 4217 code of value |
cpvCodes | array of strings | The CPV codes on the notice, 8 digits, de-duplicated and sorted |
regions | array of strings | UK ITL1 regions of delivery, or Outside UK. A UK-wide location lists all twelve regions |
publishedAt | string | Publication time (ISO 8601, UTC) of the version returned. For a Find a Tender legacy form read from its daily file (past days) it is the date at T00:00:00Z, because the file gives no time; in renewals, which reads legacy notices from the daily files only, it is always the UK publication date at T00:00:00Z. So for a legacy notice published between 00:00 and 01:00 UK summer time the shown publishedAt can be up to an hour later than the real time. For pcs rows it is almost always a date at midnight UTC, because Public Contracts Scotland publishes dates, not times (quick quote awards are the exception) |
deadline | string or null | Tender deadline (ISO 8601, UTC); null if none or unreadable |
contractEndDate | string or null | When the contract ends (ISO 8601, UTC). For a planning or tender notice, the planned end date (the latest across its lots when the notice has lots); for an award, contract or termination notice, the latest end date of the awards and contracts, falling back to the planned one. null where the notice gives none. Patchy: many notices do not state it |
awardDate | string or null | The latest award date on the notice (ISO 8601, UTC). null if there is no award or no date |
procurementMethod | string or null | open, selective, limited or direct (the OCDS procurement-method codes), as the notice states it. null where not stated. Find a Tender legacy forms read from its daily files (search and alert, past days) carry none. The free-text procedure description is not returned |
smeSuitable | boolean or null | true where the notice says it is suitable for small and medium-sized enterprises. null otherwise: Contracts Finder's bulk files write "not suitable" and "not stated" the same way, so null does not mean "not suitable" |
stage | string | planning, tender, award, contract or termination |
noticeUrl | string | The public notice page (for pcs, on the Public Contracts Scotland website) |
alertFingerprint | string or null | Set only in alert mode; null otherwise |
daysToDeadline | integer or null | Whole days from the day the run started (UTC) to the deadline's UTC date: 0 on the day, negative once the deadline has passed, null when the notice has no deadline. Worked out when the run delivers the row, so the same notice read tomorrow shows one day less. A deadline of 00:30 on 31 October in British Summer Time is 2026-10-30T23:30:00Z, so its UTC day is 30 October |
lotsCount | integer or null | How many lots the notice lists. Find a Tender and Public Contracts Scotland notices state them; Contracts Finder notices do not, so cf rows are null. null when the notice lists none, which does not mean it has no lots. Only the count is read, never a lot's text |
suppliers | null | Reserved for organisation supplier names, pending a review; null in this version, on every row. No supplier name is read. There is no supplier filter |
noticeType | string or null | The Procurement Act form of a Find a Tender notice: UK1 pipeline, UK2 preliminary market engagement, UK3 planned procurement, UK4 tender, and so on. null for Find a Tender notices on the older forms, and for cf and pcs rows |
futureNoticeDate | string or null | When the buyer expects to publish its next notice for this procurement, usually the tender (ISO 8601, UTC), as the notice states it. Planning notices carry it: Find a Tender UK1 to UK3 and older prior information notices, and planning notices on Contracts Finder and Public Contracts Scotland. null where the notice gives none. Pipeline mode sorts and filters on it |
All dates and times are UTC (deadline, contractEndDate, awardDate, publishedAt, futureNoticeDate). UTC is an hour behind British Summer Time, so 23:59:59 BST on 31 March is 2025-03-31T22:59:59Z, and midnight BST at the start of a day appears as 23:00 the previous evening.
Three ids, three jobs: noticeId identifies one notice on one service (use it to look the notice up); ocid identifies the whole contracting process across its notices on that service (a tender, its award and its contract share one ocid); reconcileKey is a hash of the normalised buyer and title that two services' rows for the same procurement are likely to share (use it to join rows yourself; it can miss a pair and can, rarely, join two different procurements).
Illustrative row (the values are made up):
{"source": "fts","noticeId": "012345-2026","ocid": "ocds-h6vhtk-0a1b2c","relatedOcids": [],"reconcileKey": "3f9a1c7e5b2d4a60","releaseId": "012345-2026","title": "Highways Maintenance Framework","buyer": "Example County Council","value": 4500000,"currency": "GBP","cpvCodes": ["45233141", "50230000"],"regions": ["North West"],"publishedAt": "2026-10-06T09:30:00Z","deadline": "2026-11-13T12:00:00Z","contractEndDate": "2029-03-31T00:00:00Z","awardDate": null,"procurementMethod": "open","smeSuitable": true,"stage": "tender","noticeUrl": "https://www.find-tender.service.gov.uk/Notice/012345-2026","alertFingerprint": null,"daysToDeadline": 16,"lotsCount": 3,"suppliers": null,"noticeType": "UK4","futureNoticeDate": null}
Rows come out newest publishedAt first, then by source and notice id, so the same input on the same data gives the same order. Pipeline mode sorts by futureNoticeDate instead (see Pipeline).
Modes
sources (default ["fts","cf"]) chooses which service to read. Public Contracts Scotland is opt-in and is read only when chosen: "sources": ["pcs"], or ["fts","cf","pcs"] for all three. Public Contracts Scotland's API is currently not working for anyone (PCS support confirmed on 28 Oct 2026; no fix date), so pcs returns nothing and is not charged. Above-threshold notices from Scottish bodies also appear on Find a Tender, which this Actor reads; Scotland-only notices below that threshold are unavailable while PCS is down. A run that includes pcs stops asking it after usually about a minute (a few minutes at most if the server accepts the connection but never replies), finishes from the other services you chose (with "sources": ["pcs"] alone it finishes with no rows), names every list it could not read in RUN_SUMMARY.sources.pcs.missingLists, and is not charged for Public Contracts Scotland (a run that reads nothing is not charged); a lookup lists the OCIDs it could not reach under unavailable. Notices are charged only when returned; the run fee follows the Pricing section. A procurement published on two services is two notices and two rows.
There are seven modes: search, pipeline (planning notices by expected tender date; see Pipeline below), lookup, alert, profile (one summary row per buyer, from Contracts Finder and Find a Tender; see Buyer profiles below), renewals and renewalsAlert (contracts by stated end date, one row per contract; see Renewals and Renewals alert below). The renewals modes have their own row, set out under Renewals, and their own price.
Search
{"mode": "search","keywords": ["bridge"],"cpvCodes": ["45"],"regions": ["Scotland"],"deadlineFrom": "2026-10-07","maxItems": 200}
- Window:
publishedFromandpublishedTo(UTC days, inclusive). Default is 31 days: today and the 30 days before. Maximum 92 days. A longer window is refused before anything is read, and nothing is charged. ApublishedToin the future counts as today. - Relative window:
lastDays(a whole number from 1 to 92) instead of the two dates: the last N UTC days, today included, worked out when the run starts (7is today and the 6 days before it;92is the largest window). A saved Task withlastDays: 7therefore always reads the last week, where fixed dates would go stale.lastDaystogether withpublishedFromorpublishedTois refused, and so is0or a number over 92, before anything is read; nothing is charged. Alert mode (a fixed 14-day window) and the renewals modes refuselastDays. - Filters are combined with AND; within a list, any match counts.
buyers: case-insensitive part of the buyer name.keywords: whole words in the title and description. The description is matched but never returned.cpvCodes: prefixes.72matches all of division 72;72212000matches itself and the codes beneath it.valueMin,valueMax: GBP. Notices in another currency or without a value are left out when either is set.regions: the 12 UK ITL1 regions, plusOutside UK.stages:planning,tender,award,contract,termination.deadlineFrom: deadline on or after this day. Notices without a deadline are left out when it is set.ocids(alert mode only): 1 to 200 OCIDs, matched exactly against the row'socid; a repeated OCID counts once (the input form takes at most 200 entries, repeats included). Refused in all other modes, before anything is read. See Track specific procurements, under Alert.maxItems: cap on rows and so on notice charges. Prefilled at 3 in the input form. When it is left out, as in an API call with nomaxItems, it is 1,000. If the platform restarts an alert run, that run can return up tomaxItemsmore notices, each charged once; set a maximum cost per run as well, which the run is designed to keep within across a restart.
Pipeline
{"mode": "pipeline","cpvCodes": ["72"],"lastDays": 92,"expectedFrom": "2026-11-01","expectedTo": "2027-01-31","maxItems": 50}
- What it returns: planning notices, sorted by when the buyer expects to go to tender. On Find a Tender these are the Procurement Act pipeline (
UK1), preliminary market engagement (UK2) and planned procurement (UK3) notices, plus prior information notices on the older forms; on Contracts Finder (and Public Contracts Scotland, if you choose it) its planning notices. - A search in all other respects: the same publication window (31 days by default,
lastDaysorpublishedFromandpublishedTo, 92 days at most), the same filters (keywords,buyers,cpvCodes,valueMin,valueMax,regions,deadlineFrom,sources,maxItems), the same row and the same price:runonce plus onenoticeper row.stagesis set toplanningfor you; any other stage is refused before anything is read, and nothing is charged. - Order:
futureNoticeDatesoonest first, then notices that state no expected date; ties go newestpublishedAtfirst.maxItemsis applied after sorting, so it keeps the soonest. - Expected window:
expectedFromandexpectedTo(YYYY-MM-DD, inclusive; either or both) keep notices whosefutureNoticeDatefalls on those days in UK time (a date written as midnight British Summer Time, which the row shows as 23:00 UTC the evening before, counts on its own UK day). A full timestamp such as2026-11-01T00:00:00Zis also accepted; its date part is the day. With either set, notices that state no expected date are left out and counted inRUN_SUMMARY.noFutureNoticeDate, and notices with a date outside the days inoutsideExpectedWindow. Search, alert, renewals and renewals alert refuse these two inputs. - Read this before you rely on it: only notices published inside the publication window are read, so a pipeline notice published four months ago is not found; use
lastDays: 92for the widest view. The expected date is the buyer's estimate as published and can move or pass without a tender. A date of midnight British Summer Time shows in the row as 23:00 UTC the evening before; the expected window still counts it on its UK day.
Lookup
{"mode": "lookup","noticeIds": ["094494-2026"]}
- Up to 20 ids per run: a Find a Tender notice id, a Find a Tender OCID (its latest notice is returned), a Contracts Finder notice GUID, or a Public Contracts Scotland OCID. Contracts Finder OCIDs are not supported.
- A Public Contracts Scotland OCID is
ocds-r6ebe6-plus 10 digits (for exampleocds-r6ebe6-0000791963), optionally with a lot suffix such as-1. A Public Contracts Scotland notice id such asSEP565172cannot be looked up, because its API has no call for it: it is refused with a message asking for the OCID. Everypcsrow carries its OCID, so take it from there. - An id that is not found gives no row and no notice charge, and is listed in
RUN_SUMMARY.notFound. The source still answered, so the run is still charged the run event once, as for any run that read data. - If a source rate-limits the run, lookup stops at once. Ids already found are returned; every id not yet looked up is listed in
RUN_SUMMARY.retryAfterasseconds,untilandids, with no row and no charge for them. The run itself succeeds, so a script or agent can read the summary and retry afteruntil.
Alert
{"mode": "alert","alertId": "highways-north-west","keywords": ["highway"],"regions": ["North West"],"firstRunLookbackDays": 1,"maxItems": 20}
alertIdis yours to choose: lower-case letters, digits and hyphens, 1 to 40 characters.- State is kept in a key-value store named
contracts-alert-<alertId>in your own Apify account. - Each run looks at the last 14 days of notices. Anything that matches your filters and is not already in the store is new, so a notice that reached the source late is still caught.
- First run: notices from the last
firstRunLookbackDaysdays (default 1, maximum 14) are returned and charged. The rest of the 14-day window is recorded as already seen, without charge. - Alerts created before 0.1.13, which reads Find a Tender from its published daily files, do not receive a backlog of older Find a Tender notices: on their first run under 0.1.13 they only send Find a Tender notices published after their last run, and the older ones are left out without charge (counted in
RUN_SUMMARY.ftsBeforeAlertFloor); that first run may also leave out Find a Tender notices that the earlier run held back formaxItems, could not read, or that were published around its last run, so such notices can be missed but no backlog is ever sent or charged. - Each notice is sent once per
alertId. A Contracts Finder amendment is not sent again. A Find a Tender award or change is its own notice with its own id, so it is new. Changing the filters keeps the store, so nothing already sent comes back. - A second run for the same
alertIdthat starts while one is active stops before reading anything and charges nothing; its summary sayslocked. - Safe to schedule daily.
Getting alerts where you work
The Actor itself sends no email or message. It writes the new notices to the run's dataset, and Apify's own features do the delivering. In outline (check each against Apify's documentation, which you can reach from your Apify Console):
- Save your alert input as a Task, one Task per alert, each with its own
alertId. - To run it every day, open Schedules in the Console, create a schedule, add the Task and check the schedule's Enabled switch.
- To be told when a run finishes, open the Task's Integrations tab and choose one of the available notification integrations. The message tells you the run finished; the notices are in the run's dataset.
- A webhook on the Task can call your own system when a run finishes (the API events are
ACTOR.RUN.SUCCEEDEDandACTOR.RUN.FAILED). Its payload carries the run's details, not the notices; the dataset id isresource.defaultDatasetId, so your system then reads the run's dataset. Refused inputs end SUCCEEDED withRUN_SUMMARY.statusrefused, so read that field rather than relying onACTOR.RUN.FAILED; a run that charged and then could not continue ends FAILED. - A notification may arrive on a day with no new notices, because a run that read data and found nothing still succeeds.
- A run that finds nothing new still pays the $0.005 run fee, so a daily alert costs at least about $0.15 a month. Set Apify's maximum-charge setting on the run.
- Do not put secrets in a webhook URL or payload template.
A weekly feed. For a bid team, the same alert on a weekly schedule gives one batch of new notices a week. For example, NHS software tenders:
{"mode": "alert", "alertId": "bid-it-nhs-weekly", "buyers": ["NHS"], "cpvCodes": ["72"], "stages": ["tender"], "firstRunLookbackDays": 7, "maxItems": 100}
The first run returns the last 7 days (today included); each later run returns only what is new since the run before. Keep the schedule at 14 days or less, the alert's window, or notices published in between are missed. It costs $0.005 a run plus $0.005 a notice on the Free plan (notices cost less on higher plans; see Pricing), so a week of about 20 new notices is about $0.105, and a quiet week still pays the $0.005 run fee. maxItems 100 keeps a week at $0.505 or less on the Free plan.
Track specific procurements
Give the OCIDs of the procurements you follow in ocids, and the alert sends their new notices only (an award, a change, a contract notice), with the same window, store, charges and digest as any alert:
{"mode": "alert", "alertId": "my-tracked-bids", "ocids": ["ocds-h6vhtk-06ef19", "ocds-b5fd17-22c262e6-9f52-43d3-9054-6fec036fe46d"], "firstRunLookbackDays": 14, "maxItems": 50}
- An OCID is the row's
ocidfrom a search or lookup. Up to 200 a run (the input form takes at most 200 entries, repeats included; through the API a repeated OCID counts once); they are matched exactly, and combined with any other filter you set (keywords,buyersand the rest). - Adding OCIDs to an existing
alertIdtakes effect on its next run: matching notices from the last 14 days that this alert has not sent before are sent then, and charged once each, as any new notice. - One procurement can carry a different OCID on Find a Tender and on Contracts Finder. Track each one you see.
- Find a Tender's older-regulations notices read from its daily files carry no OCID, so they cannot be tracked this way.
- The alert reads the last 14 days on each run: run it at least once in 14 days and no notice of these procurements is missed.
- As in any alert, each notice is sent once; a Contracts Finder amendment of a notice already sent is not sent again.
RUN_SUMMARY.ocidsgivesrequested(how many OCIDs) andmatchedProcesses(how many had a matching notice in the 14 days, sent now or earlier). The OCIDs are not listed back.
Digest
Each alert run also writes a short summary, ALERT_DIGEST, to the run's key-value store next to RUN_SUMMARY, as Markdown text (text/markdown), so a person, a webhook or a connector can read one small record instead of the dataset. It lists only the notices that run returned:
- a header with the
alertId, the run's UTC date, the number of new notices and the count per stage; - the notices whose
deadlinefalls within 7 days of the run date; - the notices grouped by CPV division (the first two digits of the first CPV code), then by region, each as one line with title, buyer, value and currency, deadline and
noticeUrl; - the Open Government Licence attribution.
It uses only those row fields: no description, contact, address or supplier. It stops at 200 lines or 50 KB and then says how many more notices are in the dataset. A run with nothing new writes one line ending "no new notices". A run that found new notices but returned none or only some, because your spending limit was reached, says how many it found and did not return, for example "3 new notices found, none returned (spending limit reached)"; those are sent on a later run while they are still inside the 14-day window. A run that was refused, found its alert locked or lost its lock writes none, and so does a renewals alert. The digest is part of the run and is not charged separately; if it cannot be written, the run, its summary and its charges are unchanged.
Buyer profiles
"mode": "profile" returns one summary row per buyer you name: what that buyer put out to tender and awarded on Contracts Finder and Find a Tender over the last 365 days, by stage, CPV code and month. It is an aggregate of fields the notice rows already carry, not a list of notices.
{"mode": "profile", "buyers": ["Bristol City Council", "GB-CFS-322528"]}
- Buyers: 1 to 10 in
buyers. Each is an exact buyer name or a buyer id: a Contracts Finder one such asGB-CFS-53257, or the id a Find a Tender Procurement Act notice gives its buyer (such asGB-PPON-PNXX-8585-GQTH). Find a Tender's older-regulations forms are matched by name only. - Matching: a name matches a buyer published under exactly that name, ignoring case, punctuation and a leading "the", with "&" read as "and". It never matches part of a name, so
NHSalone matches only a buyer published exactly as "NHS". An id is matched exactly, ignoring case. The row lists what was joined inmatchedNamesandmatchedIds. - Window: fixed at the 365 days ending the day before yesterday (yesterday's file appears only about 02:00 UTC).
publishedFromandpublishedToare not used, and neither aresourcesor the search filters. - Coverage: Contracts Finder and Find a Tender, each from its daily files over the same 365 days. Since 24 February 2025, procurements under the Procurement Act 2023, including below-threshold contracts, are published on Find a Tender; Contracts Finder carries procurements begun under the earlier regulations (on one day, 9 September 2026, Contracts Finder had 128 notices and Find a Tender 444). Find a Tender notices are read as search reads them, its older-regulations forms included. Public Contracts Scotland is not included. Each profile says what was read in
coverage, which comes before any total: read it first. No API is called. A day file that cannot be read is listed incoverage.cf.missingDaysorcoverage.fts.missingDays, with the first instaleSince; a Find a Tender file over the size limits or not a valid archive is also named incoverage.fts.rejectedDayswith the reason. Day files not read within 25 minutes count as missing. - Values: notice values, not payments.
awardedcovers award and contract notices;tenderedcovers tender notices (an estimate). They are kept apart and never added together. GBP only: values in another currency are counted inotherCurrency. Framework call-offs are counted. A notice with CPV codes in several divisions counts in each. - Not in a profile: supplier names or counts, contacts, addresses, descriptions, or any ranking of buyers.
- Too few days: a profile needs at least 85% of the year's day files of each service: Contracts Finder's and Find a Tender's. Contracts Finder publishes files for working days, so its share is taken over its expected days: weekdays that are not bank holidays in England and Wales, plus any weekend or bank-holiday day whose file was read (
coverage.cf.daysExpected); a weekend or bank-holiday day with no file does not count (quietDaysWithoutFile). Find a Tender publishes a file each day, so its share is over all the days. If either falls short, no profile is made and nothing is charged; the status message andRUN_SUMMARY.coverageTooLowFor(cf,ftsor both) name the service and the shortfall in days, for example "Contracts Finder: 210 of 253 expected day files read (83.0%, 85% needed: 6 more days were needed)". Run again later.coverage.cf.percentReadandcoverage.fts.percentReadgive each share. - No profile, no charge: the
runevent is charged only when the run returns at least one profile. A run that returns none (no notices for your buyers, too few days) costs nothing. - Refused, with nothing read or charged: no buyers, more than 10, a name under 3 or over 200 characters, or a name that reads like a person's (no organisation or public-body word in it). A matched buyer that publishes such a name also gets no profile and no
profilecharge.
| Field | Meaning |
|---|---|
profileOf | The names or ids you asked for that found these notices (a name and its id give one profile, charged once) |
buyer | The buyer name as most often published |
matchedNames, matchedIds | Every published name and buyer id that was matched |
window | from and to (UTC days) |
coverage | cf (daysRead, daysInWindow (calendar days), daysExpected (the share's denominator, above), quietDaysWithoutFile, missingDays (working days, and weekend or bank-holiday days whose file could not be read), staleSince, complete, percentRead (daysRead / daysExpected), minimumPercent, incomplete), fts (daysRead, daysInWindow, missingDays, staleSince, complete, plus rejectedDays when a file was refused, percentRead, minimumPercent and incomplete), then pcs as text saying it is not included |
valuesNote | A reminder that values are notice values, not payments |
notices | Count in the window: total, then one count per stage |
awarded | count, valueGbp, medianGbp, withValue, noValue, otherCurrency, frameworkCallOffs |
tendered | count, estimatedValueGbp, withValue, noValue, otherCurrency |
byCpv | Per CPV division (2 digits) and group (3 digits, the top 30): cpv, level, notices by stage, awardedValueGbp, estimatedValueGbp. Sorted by awarded value |
byMonth | Per month: month and notices by stage |
topNotices | largestAwards and latestTenders, up to 10 each: noticeId, title, stage, value, currency, publishedAt, noticeUrl, frameworkCallOff |
- Price:
profileis $0.10 flat for each buyer profile delivered, plus therunevent ($0.005) when at least one profile is returned. From 0.1.21 a buyer that publishes only on Find a Tender gets a profile, charged as one, where earlier releases returned "no notices" free of charge; and a run below 85% of either service's day files makes no profile and charges nothing. If you set a maximum cost per run, profiles are delivered first and the run fee is charged only if what is left of the maximum covers it, so a maximum that covers only the profiles delivers them without the run fee (RUN_SUMMARY.runFeeRefusedByLimitis then true). If the platform restarts a profile run, a buyer's notices may be delivered again inside a new profile; that new profile is charged as one profile. A buyer with no notices in the window gets no row and noprofilecharge; a run that returns no profile at all is not charged. Ten buyers in one run cost at most $1.005. The run stops before reading ifrunorprofilehas no flat price. Thenoticeevent is not used in this mode. - Run summary:
buyersgives the outcome for each buyer by its position in your input:profile,sameAsEarlier(the same notices as an earlier input: one profile, charged once),noNotices,personLike,noNameorcoverageTooLow(withcoverageTooLowFornaming the service below 85%).coverageis repeated there, andftsDayFilescounts what the Find a Tender day files held that was skipped or refused.
Renewals
"mode": "renewals" lists contracts whose notice states an end date inside a window you choose, one row per contracting process.
{"mode": "renewals", "endWithinMonths": 12, "cpvCodes": ["90"], "regions": ["South East"], "maxItems": 200}
What a row means. endDate is the end date as the notice states it. It is not a confirmed re-tender: an option can be used without a new notice, and a contract can end without one. A contract on both services gives two rows, one for Find a Tender and one for Contracts Finder. With several contracts, awards or lots in the notice, endDate is the latest of them. The mode has no supplier data and no supplier filter: a supplier input is refused. An end date is never worked out from a start date and a duration.
| Field | Meaning |
|---|---|
source, noticeId, ocid, noticeUrl, publishedAt, title, buyer, regions, value, currency, cpvCodes | As in the notice row, for the notice the row was built from |
noticeType | UK7, UK10, UK6 or UK5 (UK1 to UK4 with includePlanned) for Find a Tender Procurement Act notices; null for Contracts Finder and legacy Find a Tender forms |
endDate | The latest end date across the notice's contract, award or lot records (ISO 8601, UTC) |
maxExtentDate | The latest maximum extension date across the same records. null means no extension date is stated. Only Find a Tender Procurement Act notices publish it; Contracts Finder has none |
hasRenewal | true if any record says the contract can be renewed; false if records state it and none says true; null if none states it. Only Find a Tender Procurement Act notices publish it |
records | How many contract, award or lot records the dates came from |
confidence | The kind of evidence behind the row, not a probability: changed (a contract change notice with a period), signed (a contract details notice: the contract has been entered into), awarded (an award notice, before signature: dates can still move), intended (a direct award notice, stated in advance), cf (a Contracts Finder award, which has a period but no extension fields), legacy (an older-regulations Find a Tender notice with a stated end date; from its daily file only the explicit end date is read, never a start date plus a duration, and such a row has no ocid and a midnight publishedAt), planned (a planned period, only with includePlanned) |
alertFingerprint | Set only in renewals alert mode; null otherwise |
Rows come out by endDate ascending, then source and notice id. One row per contracting process: where a process has several notices in the run, the strongest wins (changed, then signed, then awarded, then intended; the newest on a tie). A process with a termination notice in the run is left out and counted in terminatedInRun.
Inputs.
- End window:
endWithinMonths(1 to 24; 12 if you give nothing), orendFromandendTo(UTC days;endFromdefaults to today,endToto 12 months after it; at most 24 months).endWithinMonthscannot be combined withendFromorendTo. matchOn:endDate(default),maxExtentDateoreither.eitheralso catches a contract past its first end date but inside an extension. A contract with nomaxExtentDatenever matches on it.- Filters, as in search:
cpvCodes,regions,valueMin,valueMax,buyers(case-insensitive part of the buyer name).buyerIds: the buyer's own published reference, matched exactly (case aside), for exampleGB-CFS-53257. It is a filter only and is not returned. sources:fts,cf(both by default).pcsis refused: Public Contracts Scotland rows carry no end dates.lookbackMonths: how many months of Contracts Finder and Find a Tender day files are read, by publication date (default 12). When Find a Tender is among the sources (it is by default), at most 12 months are read: 1 to 24 are accepted, so saved Tasks keep working, but a value above 12 is cut to 12, and the run says so (lookbackClampedFrominRUN_SUMMARY, and a sentence in the status message). A run with"sources": ["cf"]reads up to 24 months, as before. This cuts Contracts Finder too: an existing renewals alert or saved Task set to 24 months on the default sources now reads 12 months of Contracts Finder as well, so a contract first published 13 to 24 months ago whose end date later moves into the window is not sent. To keep 24 months of Contracts Finder, use"sources": ["cf"](Contracts Finder only). A Contracts Finder-only run of 24 months may not finish within the 25-minute limit: unread days then count as missing, and a renewals alert below 85% coverage sends nothing (this release has not measured that case). In a measured run, 12 months of both services' day files took about 17 minutes; 24 months did not finish within the 25-minute limit and fell below 85% coverage.includePlanned: also list the planned periods of Find a Tender planning and tender notices (UK1toUK4). These are new procurements, not current contracts. Off by default.maxItems: at most 20,000 (1,000 if left out).- Refused, with nothing read or charged:
keywords,stages,deadlineFrom,publishedFrom,publishedTo,lastDaysandnoticeIds(they would be ignored and widen the paid query), a supplier input,pcs, an end window over 24 months, andendWithinMonthstogether withendFromorendTo.
Coverage. Read it before relying on a list.
- Find a Tender is read from its daily files over the look-back, as Contracts Finder is, plus yesterday's file once it is out (about 03:05 UTC). Today, and yesterday before its file appears, come from its API (Procurement Act notices only: older-regulations notices wait for the next day's file, below). A day in the look-back whose file is missing, over the size limits or not a valid archive is listed as missing (with the reason in
coverage.fts.rejectedDays), not filled from the API.coverage.fts.recentDayssays how yesterday and today were read. Contracts ending in the next 12 months were mostly awarded one to four years ago, so a longer look-back finds more of them. - Older-regulations Find a Tender notices read from a daily file give an end date only where the notice states one (its
DATE_END); one that states only a duration gives no row. Renewals reads these notices from the daily files only, never from the API: today's and yesterday's older-regulations notices are skipped there (counted insources.fts.ftsLegacyFromApiSkipped) and arrive with the next day's file, so they reach renewals a day later than Procurement Act notices. (Their API record can differ from the file, for example a modification carrying the original contract's end date, which would make a renewals alert send and charge it twice.) Their daily files carry no OCID, so such a row hasocidnulland, aspublishedAt, the UK publication date at midnight UTC (not the time), and a renewals alert sends and charges it once. So an older-regulations award and its later modification notice are always two separate rows, which 0.1.20 and earlier joined into one process by their OCID when both came from the API. Two versions of one older-regulations notice from the same day have the samepublishedAtand the same release number (a Find a Tender release number is its notice id's year), so the first read is kept; Find a Tender gives a change its own notice id, so this concerns a re-publication of the same notice only. A change notice read from a daily file takes its title, and its buyer if it names none, from another notice of the same process read in the run (followUpsCompleted); with none, its title isnull(followUpsWithoutTitle). - Contracts Finder is read from its daily files over the look-back. Its share is falling: notices under the Procurement Act 2023 go to Find a Tender only, and Contracts Finder carries procurements begun under the earlier regulations. Contracts Finder awards carry no extension dates.
- Changes and terminations are seen only if published inside the look-back or on the days read after it. A contract that was changed or terminated outside it can still appear with its older dates.
- Public Contracts Scotland is not read. The list is what the run could read, not a record of every contract.
- One-off mode below 85% coverage: if fewer than 85% of the look-back's Contracts Finder day files, or fewer than 85% of its Find a Tender day files, could be read, the run still returns rows and charges as usual, and the status message and
coverage.cforcoverage.ftssay it is incomplete, with the day files read, the expected days, the percentage and how many more days were needed. Contracts Finder's share is over its expected days (working days, plus weekend or bank-holiday days whose file was read; see Buyer profiles); today and yesterday are not part of the Find a Tender share. - Day files not read within 25 minutes count as missing, and
coverage.cfandcoverage.ftslist them.
Renewals alert
"mode": "renewalsAlert" takes the renewals inputs plus alertId and sends a contract when it first matches, then again only if it changes.
{"mode": "renewalsAlert", "alertId": "cleaning-south-east", "endWithinMonths": 12, "cpvCodes": ["90"], "regions": ["South East"], "maxItems": 500}
- Run it weekly. Every run re-reads the whole Contracts Finder look-back, so a daily schedule costs about seven times as much for little change. Schedule it as described under Getting alerts where you work.
alertIdis yours to choose, as in alert mode (lower-case letters, digits and hyphens, 1 to 40 characters; not a single hyphen). State is kept in a key-value store namedcontracts-renewals-<alertId>in your own Apify account, apart from a notice alert's store.- The first run sends the whole current backlog, up to
maxItems. If more match, the rest are sent on later runs (truncatedByMaxItemscounts them). - A contract is sent again, and charged again, only when its end date, maximum extension date or renewal flag changes. A stronger notice with the same three values is not sent again. The exception is Find a Tender's older-regulations forms: they carry no contracting-process id here, so each such notice is its own row, and a modification notice (F20) of an older-regulations contract is sent and charged as a new row even when its dates are the same as the award's (0.1.20 and earlier joined the two by their OCID).
- Find a Tender is read from its daily files over the whole look-back. A renewals alert created before this release does not send Find a Tender contracts whose notice was published before its first run of this release, more exactly at or before the end of its last run under the earlier release (counted in
ftsBeforeAlertFloor, not sent or charged; the time isftsFilesSince): its earlier runs could read only Find a Tender's last few days, and sending the rest at once would charge for a backlog it never asked for. Start a newalertIdto get them; a new alert receives the whole backlog. Contracts Finder rows are not affected. Because that time is the end of the last earlier run, Find a Tender notices published while that run was going (up to about 25 minutes) are left out too, and so are older-regulations notices published later on that same day (their daily file gives the date, not the time): the alert prefers never charging a contract twice to never missing one. - Below 85% of either service's day files the alert sends nothing and charges nothing, not even the run fee: it needs 85% of the look-back's expected Contracts Finder day files (working days, plus weekend or bank-holiday days whose file was read) and 85% of its Find a Tender day files. It leaves its record unchanged, apart from settling an interrupted earlier run.
statusisincompleteSourceCoverageand the status message says "Incomplete source coverage", with the shortfall in days (for example "Contracts Finder: 210 of 253 expected day files read (83.0%, 85% needed: 6 more days were needed)"). Run it again later. - Capacity: the alert's record holds about 27,600 entries. A contract whose dates change uses one entry per version. When it is full, a run sends only what still fits and reports
ledgerFullwithleftOutLedgerFull(how many were left out); nothing beyond that is sent or charged. If no row can be sent, the run charges nothing. Room returns when an entry has not been seen again for 800 days. - A second run for the same
alertIdthat starts while one is active stops before reading and charges nothing (locked). skippedUntokenisablein the run summary: rows skipped because an id could not be turned into a fingerprint; they are counted, not sent, not charged.pendingUnverified: rows an earlier interrupted run had queued whose delivery could not be checked; they are treated as delivered and are not sent or charged again.
Pricing
Pay per event. Platform usage is included, so this is what you pay.
| Event | Charged | Price on the Free plan | Bronze | Silver | Gold and above |
|---|---|---|---|---|---|
run | Once per run that read data (profile mode: only when it returns a profile; renewals modes: when Contracts Finder day files were read or the Find a Tender API answered, so a Find a Tender-only run whose API gave no answer pays for its rows but not this fee; renewals alert: not charged when it sends nothing because of incomplete coverage or a full record) | $0.005 | same | same | same |
notice | Each row returned (search, pipeline, lookup and alert) | $0.005 ($5.00 per 1,000) | $0.0045 | $0.004 | $0.003 |
profile | Each buyer profile returned (profile mode) | $0.10 | same | same | same |
renewal | Each row returned (renewals and renewals alert) | $0.02 ($20.00 per 1,000) | $0.018 | $0.016 | $0.012 |
notice and renewal cost less on higher Apify plans, as in the table; run and profile are the same on every plan. The worked costs below use Free-plan prices.
Runs refused before anything is read cost nothing: a window over 92 days, invalid input, a lookup rate-limited before any answer, or an alert that finds its store locked. "Not found" counts as read, because the source answered.
Notices from Public Contracts Scotland are charged as notice events like the others, and its answers count as data read for the run event.
Renewals and renewals alert: $0.005 for the run plus $0.02 for each row, and the notice event is not used. 20 rows cost $0.405, 1,000 rows $20.005 and 20,000 rows (the most maxItems allows) $400.005. From 0.1.21 Find a Tender is read over the whole look-back (about 12 months by default) instead of about its last 8 days, so a run returns, and charges, more rows than before, up to maxItems. The run fee is charged when the run read Contracts Finder day files or got an answer from the Find a Tender API: a run with "sources": ["fts"] whose API gave no answer charges its rows but not the run fee. A renewals alert charges again for a contract only when its end date, maximum extension date or renewal flag changes, except that a modification notice of a Find a Tender older-regulations contract is its own row and is charged as a new one even with unchanged dates (see Renewals alert). Set maxItems and Apify's maximum-charge setting; the first run of an alert can be large.
Typical costs: looking up one notice costs $0.01; a daily alert returning about 20 new notices costs about $0.105 a run. Cap spend with maxItems and with Apify's maximum-charge setting on the run; if your maximum left rows out, the summary reports chargeLimitReached.
Freshness and coverage
- Contracts Finder: its daily file first. Contracts Finder publishes one file per day, the day after, at about 02:00 UTC. Days with a file are read from it, with no API call. Today, and any day whose file is missing, comes from its API.
- Find a Tender: the whole window from its published daily files (yesterday's file appears by about 03:05 UTC; today from its API). In search and alert, each past day is read from Find a Tender's daily file, with no API call; today, and any day whose file is missing, comes from its API. An alert reads all 14 days of its window this way. A day file over the size limits (see Limits) or not a valid archive is not read and not filled from the API:
RUN_SUMMARY.sources.fts.missingDaysnames the day andrejectedDaysgives the reason. Notices in the older, pre-Procurement Act forms read from a daily file have noocid, no publication time (the date at midnight UTC) and noprocurementMethod,smeSuitable,awardDate,contractEndDateorlotsCount. Follow-up notices (contract changes, terminations and performance) take their title and buyer from an earlier notice of the same procurement read in the same run, and otherwise have none (followUpsWithoutTitle). Renewals and buyer profiles read Find a Tender's daily files too (below). - When the source limits the run, the run still finishes from what it has read.
RUN_SUMMARY.sources.<source>.staleSinceis the first day not covered, andmissingDayslists them. An alert picks those notices up on its next run. - Later versions. A notice first published inside your window can carry a later
publishedAtif a newer version has since been published; the row is returned and counted inlaterVersions. - Public Contracts Scotland has no bulk files. It is read as month lists: one list per notice type per month (21 types), so a search reads every notice type for each month the window touches. A window spanning 2 months is 42 lists and one spanning 4 months is 84. Large lists are slow; when measured in October 2026 from the developer's machine, one month's 21 lists took about 3.5 minutes of response time plus about 1 minute of pacing, so a search that includes
pcstakes minutes longer. - A past month's list can fail on the first request. The Actor tries each list three times, 30 seconds apart. A list that still fails is named in
RUN_SUMMARY.sources.pcs.missingLists(for example2026-09:102, month and notice type), never silently dropped, and the run finishes from everything else. The same happens if the service cannot be reached. - Renewals reads the Contracts Finder and Find a Tender daily files over its look-back (12 months by default), the way profile mode does, and Find a Tender's API only for today and for yesterday until its file is out. A look-back day whose file cannot be read is listed as missing, not filled from the API. The Contracts Finder share of upcoming expiries falls as new notices go to Find a Tender only. Changes and terminations are seen only if published inside what the run read.
- Buyer profiles read the Contracts Finder and Find a Tender daily files over their 365 days, with no API call.
Run summary
Every run writes a RUN_SUMMARY record to its key-value store; an alert run also writes ALERT_DIGEST there (see Digest, under Alert). Main fields:
| Field | Meaning |
|---|---|
status | ok, refused (input rejected; reason says why; the run ends as succeeded and nothing is charged; a pricing set up wrongly on the Actor also shows refused, but the run fails), locked (alert already running) or failed (the run ends FAILED: a check that could not be made, or a run the platform restarted after it had charged whose input was then refused; the status message says what was charged) |
pushed | Rows returned |
readData | Whether the run read any data |
runCharged | Whether the run event was charged; on a restarted run, whether an earlier start is treated as having charged it (so it is never charged twice) |
chargeLimitReached | true only when your maximum cost per run left rows out |
runFeeRefusedByLimit | Present and true when the run fee did not fit your maximum and was not charged |
restarted | Whether the run found signs that the platform had restarted it. A restarted run is designed to skip the run charge if this run already made it. In search and lookup it leaves out the notices already in this run's dataset (alreadyStored counts them), and a restarted search stops at maxItems in total (cutAfterRestart counts the rest). If you set a maximum cost per run, the run is designed to keep within it across a restart |
sources.fts, sources.cf | Per service: bulkDays, apiDays, missingDays, staleSince, noticesRead, and rejectedDays (day files refused by a size or archive limit, with the reason) when there are any. In alert mode these sit under search.sources |
sources.pcs | Public Contracts Scotland: listsRead and missingLists (month lists that could not be read, as YYYY-MM:type). In alert mode this sits under search.sources |
api | API use: requests, answers, budget, refusals, waitedSeconds, stopped, retryAfter (seconds) |
pcsApi | The same kind of API use figures, for Public Contracts Scotland, which has its own client and pacing |
bulkBytes, bulkErrors, bulkRetries | Daily files: bytes read; bulkErrors, the requests that ended with neither a file nor a "no file for that day" answer (403 or 404): a network error, a server error (5xx) or a "too many requests" answer (429), counted after the retry where there is one, and any other answer such as 400; and bulkRetries, present when a request was tried a second time, about 2 seconds after a network error, a 5xx or a 429. Only buyer profiles and the renewals modes retry, because they read their daily files within a 25-minute limit; search and alert do not. A 403, a 404, any other 4xx and a file over the size limit are never retried |
retryAfter | Lookup only: seconds, until, ids still to look up |
notFound | Lookup only: ids that returned nothing |
laterVersions | Rows whose publishedAt is after your window |
followUpsCompleted, followUpsWithoutTitle | Follow-up notices that borrowed a title and buyer from another row with the same OCID in the run, and those that found none |
matched, alreadySent | Alert: notices in the 14-day window that match your filters, and those not sent because an earlier run of this alertId already sent them or recorded them as seen |
ocids | Alert with ocids only: requested (how many different OCIDs were given) and matchedProcesses (how many of them had at least one matching notice in the 14-day window, sent now or earlier). The OCIDs themselves are not listed |
seenWithoutCharge | Alert, first run only: matching notices older than firstRunLookbackDays, recorded as seen without charge and never sent |
truncatedByMaxItems | Alert: new notices beyond maxItems, sent on later runs |
expected, noFutureNoticeDate, outsideExpectedWindow | Pipeline only: the expectedFrom and expectedTo you gave (from, to), and, when either was given, the matching notices left out because they state no expected date or one outside those days. mode is pipeline |
Renewals and renewals alert add these (in the alert, the read's own summary sits under renewals):
| Field | Meaning |
|---|---|
status | Also incompleteSourceCoverage (renewals alert below 85% of the look-back's Contracts Finder day files or below 85% of its Find a Tender day files: nothing sent or charged; reason names the service, the day files read of the expected days, the percentage and how many more days were needed), and lockLost (the alert's lock expired and another run took it: this run sent nothing) |
endWindow | from, to (UTC days) and matchOn |
includePlanned | Whether planned periods were included |
lookbackClampedFrom | The lookbackMonths you asked for, when it was over 12 and cut to 12 (at most 12 months are read when Find a Tender is among the sources); absent when nothing was cut. In the renewals alert it is also at the top level |
coverage | Read it first. cf and fts: from, to, lookbackMonths, the day-file counts as in profile mode (for cf also daysExpected and quietDaysWithoutFile; its percentRead is over daysExpected), percentRead, minimumPercent (85), incomplete. fts also has route (files+api), rejectedDays (a file refused, with the reason), recentDays (yesterday and today: file, api or missing), text (the day files read and how yesterday and today were read) and counts of what its files held that was skipped. pcs: text saying it is not included. A service not asked for says "not requested" |
processes, matched, returned | Contracting processes found, those matching your filters and window, and rows returned |
terminatedInRun | Processes left out because a termination notice for them was read in the run |
supersededNotices, olderVersions | Notices set aside because a stronger notice for the same process, or a newer version of the same notice, was read |
ftsDaysNotReached | Find a Tender days not tried because the API stopped or the request budget ended |
ftsApiDaysPastDeadline | Find a Tender days with no file yet (today, yesterday) not asked of the API because less than 130 seconds of the 25 minutes were left; they are missing days |
sources.fts.ftsLegalBasisUnknown | Find a Tender notices from the API whose legal basis is neither the Procurement Act nor an EU directive: no row from the API, so it cannot be identified differently from its daily file; the next run reads it from that file |
sources.fts.ftsLegacyFromApiSkipped | Find a Tender older-regulations notices from the API (today's and yesterday's), not read there: renewals reads them from their daily file only, so they arrive with the next day's file |
ftsBeforeAlertFloor, ftsFilesSince | Renewals alert: Find a Tender rows left out (not sent or charged) because their notice was published at or before the alert's ftsFilesSince, the end of its last run before this release (for a new alert, a time before anything it can read) |
followUpsCompleted, followUpsWithoutTitle | Find a Tender change notices read from a daily file that took their title from another notice of the same process in the run, or found none |
truncatedByMaxItems | Matches beyond maxItems: not returned (renewals), or sent on later runs (renewals alert) |
alreadyStored, cutAfterRestart | After a platform restart: rows already in this run's dataset and left out, and rows left out because maxItems was already reached in total |
ledgerFull, leftOutLedgerFull | Renewals alert: the alert's record is full; leftOutLedgerFull rows were left out, not sent or charged |
emitted, alreadySent | Renewals alert: rows sent this run, and matched rows not sent because they were already sent with the same dates and flag |
skippedUntokenisable | Renewals alert: rows skipped because an id could not be made into a fingerprint |
pendingUnverified | Renewals alert: rows queued by an interrupted earlier run whose delivery could not be checked; treated as delivered |
pendingRecovered, ledgerMigrated | Renewals alert: what was settled from an interrupted earlier run (committed, resend), and the one-time move of an older record to the current version |
Data and licence
- Contracts Finder's notice data is published under the Open Government Licence v3.0. Find a Tender's notice data, including its daily files, is published under the Open Government Licence (confirmed by the Find a Tender operator on 28 Oct 2026, in answer to a question about its daily files: all content on Find a Tender falls under the OGL); the packages its API returns name Open Government Licence v3.0 in their
licensefield. Public Contracts Scotland data is published by the Scottish Government (Scottish Procurement and Commercial Directorate) under the same licence: the packages its API returns carry the licence as Open Government Licence v3.0 in theirlicensefield. The licence does not extend to personal data. Please keep this attribution: Contains public sector information licensed under the Open Government Licence v3.0. - Rows have no contact, address, identifier or description fields.
suppliersis reserved for organisation supplier names, pending a review; it is null in this version and no supplier name is read. Titles and buyer names are copied as published, and a title can still contain a name. In titles, email addresses, UK phone numbers and a person's name written straight after "contact", "speak to", "email", "FAO", "attention of" or "write to" are replaced with[redacted]. That redaction works by pattern, so a name written some other way is not caught. - Sources: Find a Tender, Contracts Finder and Public Contracts Scotland only. No proxies are used.
- This Actor is not affiliated with or endorsed by the operators of Find a Tender, Contracts Finder or Public Contracts Scotland.
- Removal and objection: email info@yappman.com with the notice id or link from Find a Tender or Contracts Finder; if you don't have it, tell us the product and the name as it appears. We will reply within one month.
Limits
- Search window: 31 days by default, 92 days at most.
- Lookup: 20 ids per run.
- Renewals: end window at most 24 months;
lookbackMonths1 to 24 accepted, at most 12 read when Find a Tender is among the sources (a larger value is cut to 12 and the run says so; Contracts Finder alone reads up to 24);maxItemsat most 20,000; Contracts Finder and Find a Tender day files not read within 25 minutes count as missing; at most 733 Find a Tender day files a run (a safety cap; a 12-month run reads about 366). - Renewals alert: run weekly; the alert's record holds about 27,600 entries, kept until not seen again for 800 days.
- Alert: 14-day window;
firstRunLookbackDays1 to 14. maxItems: prefilled at 3, 1,000 when left out, maximum 100,000 (20,000 in the renewals modes). A search reads only the days it needs: once it hasmaxItemsnotices newer than a day still to be fetched from an API, that day and older ones are skipped, because they could not appear in the newest-first result.- Scottish duplicates: an above-threshold Scottish notice can appear as both a
pcsrow and anftsrow. That is two rows and two charges; join them withreconcileKey. pcsdates:publishedAtis almost always a date only (midnight UTC), so Public Contracts Scotland rows are day-precise, not time-precise.- API pacing: Find a Tender and Contracts Finder share one rate limiter, so the Actor makes at most one API request every 15 seconds, across both. Public Contracts Scotland is a different host with its own pacing: one request every 3 seconds, and a refusal from one client does not stop the other.
- Public Contracts Scotland request budget: months touched × 21, plus 10 (a 92-day window touches up to 5 months). An alert's 14-day window touches 1 or 2 months, and all of its month lists are read on every run. Reading Public Contracts Scotland stops after 40 minutes in any run; lists not read by then are named in
missingLists. - Public Contracts Scotland's API is currently not working for anyone (PCS support confirmed on 28 Oct 2026; no fix date), so
pcsreturns nothing and is not charged. Above-threshold notices from Scottish bodies also appear on Find a Tender, which this Actor reads; Scotland-only notices below that threshold are unavailable while PCS is down. A run that includespcsstops asking it after usually about a minute (a few minutes at most if the server accepts the connection but never replies) (the first list's 3 tries, 30 seconds apart), reports the lists it could not read inmissingLists, and is not charged for Public Contracts Scotland; a run that reads nothing is not charged. - Request budget per run: lookup makes one request per id (20 ids takes about 5 minutes). Searches and alerts have a fixed cap of 12 API requests a run (today's pages for both services, or a missing day; about 3 minutes at the paced rate). Renewals has the same 12 (Find a Tender's pages for today, and for yesterday until its file is out). When the cap is reached the run ends normally and reports the days it did not cover.
- Find a Tender daily files (search, alert, renewals and profiles): at most 25 MB compressed, 50 MB uncompressed and 5,000 notices per file; a file over a limit is reported as a missing day, with the reason in
rejectedDays. A search or alert reads at most 184 day files (two services over the longest window, 92 days). - The input takes no URLs, headers or query strings: the Actor builds every request itself.
What changed
- 0.1.21 (4 Nov 2026): Find a Tender's daily files in renewals and buyer profiles, and pipeline mode.
- Renewals, renewals alerts and buyer profiles read Find a Tender from its daily files over the whole look-back (12 months; 365 days for profiles), not only its last few days. When Find a Tender is among the sources, renewals now reads at most 12 months: a
lookbackMonthsof 13 to 24 is still accepted, so saved Tasks keep working, but is cut to 12, and the run says so (lookbackClampedFrom). Contracts Finder alone still reads up to 24. This cuts Contracts Finder too: an existing renewals alert or saved Task set to 24 months on the default sources now reads 12 months of Contracts Finder as well, so a contract first published 13 to 24 months ago whose end date later moves into the window is not sent. To keep 24 months of Contracts Finder, use"sources": ["cf"](Contracts Finder only). A Contracts Finder-only run of 24 months may not finish within the 25-minute limit: unread days then count as missing, and a renewals alert below 85% coverage sends nothing (this release has not measured that case). Renewals reads Find a Tender's API only for today, and for yesterday until its file is out, and only for Procurement Act notices: older-regulations notices come from the next day's file. One-off renewals runs now return about 12 months of Find a Tender rows instead of about 8 days, charged at $0.02 each up tomaxItems. A buyer that publishes only on Find a Tender now gets a charged profile ($0.10) instead of a free "no notices". - Profiles and renewals need 85% of each service's day files: below it no profile is made (nothing charged), a renewals alert sends and charges nothing, and a one-off renewals run says it is incomplete.
- A renewals alert created before 0.1.21 does not send Find a Tender contracts published before its first 0.1.21 run (
ftsBeforeAlertFloor); start a newalertIdto get them. Older-regulations rows carry noocid, so such an award and its modification are separate rows, each charged. - New mode
pipeline: planning notices sorted byfutureNoticeDate(soonest first), withexpectedFromandexpectedToto keep the expected tender dates you want; see Pipeline. Two new fields on each notice row:noticeType(the Find a Tender Procurement Act form) andfutureNoticeDate. Search, alert, renewals and renewals alert refuseexpectedFromandexpectedTo.
- Renewals, renewals alerts and buyer profiles read Find a Tender from its daily files over the whole look-back (12 months; 365 days for profiles), not only its last few days. When Find a Tender is among the sources, renewals now reads at most 12 months: a
- 0.1.20 (4 Nov 2026): Find a Tender, two days' notices were missing from searches. Find a Tender re-published two of its daily files with their notices inside a folder: the file for 28 October 2026, re-published around 2 to 3 November, and the file for one day between 4 and 14 October. Our reader expected the notices at the top of the file, so from each re-publication until 4 November it skipped them. Searches that covered either day returned no Find a Tender notices from that day's file; the run summary counted them in
sources.fts.skippedEntries. Contracts Finder and Public Contracts Scotland were not affected, and nothing was charged for notices that were not returned.- Alerts had already received both days' notices before the files were re-published, so they missed nothing and do not receive them again. An alert started after a re-publication (for 28 October: after about 06:45 UTC on 3 November) receives that day's matching notices on its next run, as a late delivery.
- From 0.1.20 the notices are read wherever they sit in the file. If you searched either day during that period, run the search again to get the full results.
About
From Yappman Data, a trading name of Abley-Yapp Consulting Limited. Contact: info@yappman.com.