UK Find a Tender: Contract End Dates & Amendments
Pricing
from $2.00 / 1,000 contract evidence rows
UK Find a Tender: Contract End Dates & Amendments
Track published contract end dates, renewal terms and amendments in UK Find a Tender records. Monitor an OCID watchlist or update window, compare saved snapshots, and export buyer, supplier and value evidence with notice links. End dates indicate possible renewal windows.
Pricing
from $2.00 / 1,000 contract evidence rows
Rating
0.0
(0)
Developer
Felix GoldWin
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 hours ago
Last modified
Categories
Share
UK Public Contract Renewal Window Monitor
Find published contract end dates and changes in official Find a Tender OCDS procurement records. Use an explicit OCID watchlist for repeat monitoring, or discover processes updated in a bounded date window. Outputs include buyer and supplier organization references, contract or award value, CPV category evidence, published end dates, renewal/option descriptions, amendment evidence and official notice links.
A published end date is a potential renewal window, never a guaranteed retender. Missing dates, currencies and values stay missing. A missing record is not a cancellation. This Actor does not estimate dates from durations, merge partial releases itself, claim all UK spending is covered, or harvest contact fields.
Scope and evidence
Only the public www.find-tender.service.gov.uk/api/1.0/ocdsReleasePackages and /ocdsRecordPackages/{ocid} endpoints are fetched. No credentials, browser/proxy or arbitrary URL input. Read-only requests only, with redirects rejected. Documentation: data outputs, releases, records.
Discovery follows validated links.next cursors while retaining the exact update range and page size. It then loads the current compiledRelease for each discovered OCID. These records are current at fetch time, not historical snapshots as of updatedTo. Inputs are normalized to UTC and sent in the API's documented timezone-free YYYY-MM-DDTHH:mm:ss format; the source applies the update filter, which is distinct from a release publication timestamp. The API documentation does not state a parameter timezone, so do not use this window as proof of an exact publication-time interval. A complete scan means complete for your configured OCIDs or discovered update window, not a complete UK contract register. Use the same exact range when comparing discovery snapshots. For continuing monitoring, copy the discovered OCIDs into an explicit stable watchlist; moving windows intentionally have different scope keys.
Each published contract becomes a row. If no contracts exist, awards become separately labeled rows and only award.contractPeriod.endDate can supply their end date. If neither exists, one process row makes the absence of contract evidence visible. Tender submission deadlines are never used as contract end dates. Contract suppliers are joined only through their matching award ID; renewal terms are attached only to explicitly related lots. No inference links an unrelated award or lot. Unmapped extensions remain out of scope; read the linked procurement for additional terms.
Run locally
Requires Node.js 22 or newer. From this unpacked folder:
npm ci --ignore-scriptsnpm testnpm run validate:schemanpm run smoke
The smoke command performs one bounded real public request with examples/input.json and writes artifacts/live-smoke.json. Fixtures and tests require no network. npm start uses the normal Apify SDK input and storage interface; put your input at storage/key_value_stores/default/INPUT.json for a local run. Use a fresh CRAWLEE_STORAGE_DIR per independent run.
Input examples: examples/input.json, examples/discovery-input.json. The sample procurement is a real source example, not an endorsement. No account publication or paid run was performed during development.
Prefilled public demonstration
The Console and Store-test input prefills watch the verified public example ocds-h6vhtk-0691e5, with limits of one process, five rows, three HTTP attempts, 30 seconds, 5 MB per response and a $0.02 event budget. This procurement demonstrates date and notice evidence; it is not presented as an imminent renewal opportunity. asOfDate is not prefilled, so the Actor uses the current UTC run date. See examples/prefill-input.json.
Apify's Store testing documentation says its automated check uses schema prefill values and expects a successful run with a nonempty default dataset within five minutes. Prefill semantics apply to the input interface; API calls do not inherit them. The existing general defaults and runtime behavior are unchanged. API callers still provide their own explicit OCIDs or date range. No snapshot or fixed historical evaluation date is prefilled.
The schema-derived input was validated and live-tested locally against the public source, including OUTPUT.diagnostics.completeScan: true; no hosted Store test or account run was performed at that stage. Hosted verification is recorded below. Public-source changes can still require updating this example.
Reusing snapshots
Copy the complete OUTPUT.previousSnapshot object into the next input's previousSnapshot. The Actor accepts only the same canonical source scope, version 1 and complete: true. It checks keys, types, date evidence, array sizes and a 1.5 MB input ceiling. Snapshot evidence is strictly projected, prototype-like keys are rejected, and snapshots are capped at 1 MB. There is no global cross-user state, shared named dataset or external history service. You control where you save the output.
When every request succeeds and no bound is hit, OUTPUT.comparison contains exact before/after selected fields with newly_observed, changed or not_observed_in_scope labels. Source hash and fetch time are kept separately in diagnostics, so a new fetch alone does not manufacture a change. Changes in published release ID/date remain source changes. A disappearance is only an observation within this scope. Baseline runs and unchanged runs emit evidence rows with their corresponding status.
Incomplete/failed scans return completeScan: false, an explicit stop reason, no change events, and previousSnapshot: null. Partial observed evidence rows may still be useful. No missing row is interpreted as a deletion or cancellation. Oversized text or snapshots are stopped rather than silently truncated and described as exact. A delivery limit also invalidates the reusable snapshot; treat OUTPUT as authoritative before applying dataset changes.
Output
The default dataset holds key, evidence, renewal, change, completeScan, scopeKey, observedAt, sourceUrl, and an interpretation note. evidence.endDate includes the source string, validated value or null, and valid/missing/invalid status. renewal is a UTC evaluation of days to the published end date; it is separate from source change comparison.
The OUTPUT key-value record contains diagnostics, scope, every successful source response's URL/time/byte count/SHA256, comparison events (including old-only records), and a reusable snapshot only after a complete scan. examples/output.json is fixture-derived; artifacts/live-smoke.json is clearly labeled live. Personal contact points, addresses, emails and phone fields are not selected. Buyer/supplier labels are those the source publishes as organizations; the Actor does not certify legal entity status.
Limits
| Control | Default | Hard maximum |
|---|---|---|
| Release page size | 20 | 100 |
| Discovery pages | 5 | 20 |
| Processes | 20 | 50 |
| Result rows | 100 | 250 |
| HTTP attempts including retries | 30 | 100 |
| Retries per request | 2 | 3 |
| Scan wall time | 90 s | 300 s |
| Bytes per response | 5 MB | 10 MB |
| Total response bytes | 20 MB | 20 MB |
| Snapshot evidence | 1 MB | 1 MB |
| Date discovery window | required | 31 days |
Sequential requests bound concurrency to one. Response bytes are hashed before strict UTF-8 decoding; malformed UTF-8 stops the scan instead of replacing source characters. A request has a 20-second timeout further reduced to remaining scan time. The source Retry-After for 429/503 is respected; if it cannot fit the remaining run time, the Actor stops. Access denials are never retried or bypassed. Large framework records can exceed response limits and will produce an incomplete diagnostic; narrow the watchlist or raise the explicit byte limit up to the hard maximum. Some official records exceed even the hard ceiling and are intentionally unsupported.
256 MB is the declared default. Local small-sample feasibility is recorded in QA.md, and bounded hosted checks are recorded below. These small-sample checks do not establish maximum-size workload behavior.
Approved listing rates
User-approved renewal-record: $0.002 per emitted evidence row ($2/1,000), plus the approved automatic start price of $0.005 per started 1 GB memory unit. This approved listing rate covers baseline request setup and evidence normalization on very small runs. It is not the platform default: the documented automatic-start default at verification was $0.00005; no account price was changed during that local verification. The approved record rate reflects compiled-record retrieval, date/evidence projection and snapshot comparison; it is not a validated demand or profitability claim. Unchanged/baseline/unknown rows cost the same if this event is configured. No event is charged for OUTPUT alone. An empty scan can still incur the platform's automatic start fee.
These listing rates are configured for this Actor. The code never manually charges apify-actor-start. It reads actual configured PPE event prices, refuses a positive synthetic dataset-item price (which would double-bill), reserves any automatic start fee still missing from SDK counters against the platform cap, recomputes budget limits before scanning and each push, and checks charge results. Already charged events on resurrection reduce the application budget. Free configured result events are supported; zero input budget with the normal positive reserve makes no source request.
maxBudgetUsd is a strict conservative event-work cap using a start reserve plus result cost; locally it uses the approved listing rates without sending charges. Set the platform maximum total charge too. Compute, storage and other platform fees are outside this input cap. Invalid input can still incur the platform's automatic start charge because that charge precedes code execution. See PUBLISHING.md for verification gates.
Hosted verification — 2026-10-12
The final runtime was built successfully on Apify as build 0.0.2 (Linux, Node 22.23.3, Apify 3.7.2), then checked in five fresh owner-development runs at 256 MB with a 60-second platform timeout. The Console input form and output/storage views were checked.
- Schema-prefill baseline: one dated contract row, one official request, 145,649 response bytes, complete scan and a reusable snapshot. Run
8sFRaymCMtsDBurSb. - Repeat with that exact snapshot: one row,
unchanged, zero comparison events and another complete snapshot. Runetdr7UnEWzdfypjgx. The public source response hash matched the baseline; evaluation used the current UTC date. The evidence end date was2027-06-30T23:59:59+01:00, with three official notice links. This example was outside the default renewal window. - Exact $0.005 platform cap: zero source requests and rows,
budget_limit, suppressed comparison and no reusable snapshot. RunHHa6fRZg3f5SGmJsq. - Zero application event budget with a $0.02 platform cap: zero source requests and rows, suppressed comparison and no reusable snapshot. Run
FbYHNZ06ONAf12VGb. - Exact $0.007 platform cap: capacity one, one delivered row, a complete scan and an unchanged comparison. Run
OFgd1Kxzl64InXyjt.
The active listing configuration was verified as $0.005 automatic start plus $0.002 per renewal-record, with no positive synthetic dataset-item charge and platform usage included for customers. Successful result runs used the platform pay-per-event code path and passed its custom-event count/delivery checks. These owner-development runs incur platform usage; they do not prove a real customer's invoice, payout or profitability. Free-result pricing, controlled source changes, null-date cases and concurrency/race handling remain local fixture/SDK tests rather than additional hosted claims. Small hosted samples do not establish worst-case memory or response-size behavior.
The source was downloaded after import and compared against the tested 41-file package: all filenames and contents matched after outer-whitespace normalization by the Web IDE. Only this documentation was subsequently updated; runtime, dependency and schema contents were not changed. Earlier QA.md and PUBLISHING.md entries describe the historical local-only verification stage.