Public tenders API: EU tenders (TED) and UK tenders (Find a Tender, Contracts Finder). Open tenders and contract awards in one schema, no source API key. Filter by CPV, country, NUTS, value, deadline, keywords. Daily tender alerts. Not affiliated with the EU, TED, Find a Tender or Contracts Finder.
Status message and free status rows: singular and plural for notices and UK duplicates (for example "Done: 1 notice published since ..." and "1 notice was read" instead of "1 notices"). No change to output rows or charging.
README: removed the "sample dataset will be linked here at launch" line (no external sample links).
0.1.4 (26 Oct 2026), private build
Contact emails removed from URL and short text fields (found in a Find a Tender notice, 098299-2026, where documentsUrl carried free text with a person's email). With excludeContactDetails on (the default), every URL field (documentsUrl, buyerWebsite, sourceUrl) keeps only its first token, drops mailto: links, drops a query string or fragment that holds an email (plain or %40-encoded) and drops the value if an email is left anywhere else. Other strings (titles, lot titles, buyer and supplier names) get emails and mailto: links replaced with [email removed]. Rows are never dropped for this.
0.1.0 (22 Oct 2026), private build
First build. Sources: TED Search API v3, Find a Tender OCDS releases, Contracts Finder OCDS search.
Acceptance tests (live APIs, local runs with simulated pay-per-event, 22 Oct 2026):
Default input (CPV 72, tender, 3 days, 30/source): rows from all three sources; notice charges = notice rows. Local duration 18 s to 202 s depending on Find a Tender latency.
TED completeness, Ireland, 7 days, all four stages: 263 rows = TED totalNoticeCount 263 for the same query (ITERATION mode). TED's raw query with every form type (incl. corrigenda) counted 265.
UK OCDS, yesterday, all stages: Find a Tender 453 rows (482 releases read, 29 older releases merged), Contracts Finder 197 rows (206 read, 8 merged); every row has ocid, buyer and sourceUrl; 437/453 and 197/197 have CPV; 1 cross-source duplicate returned once with a free duplicate row; charges = 650 rows.
Filters (CPV 72, DE, ≥ €100k): with "cloud migration" 0 rows and a free no_matches row (filteredOut noValue 153, keywords 37, value 14, deadlinePassed 1); with "cloud" 4 rows, all matching every filter, charges = 4. "cloud" vs "Cloudsley" covered by unit test.
Rolling dates: unit test (window and default deadline move with the run date).
Monitor: run 1 baseline (346 notices, free row, 0 charges); run 2 no changes, 0 rows, 0 charges; run 3 after simulated changes, 3 new + 2 amended, alert charged once + 5 notice events.
Upstream failure (simulated 403 on Find a Tender): run finishes, free source_error row, status message "Partial results… Warnings"; all sources failing fails the run.
Award join: 989 award suppliers with a Companies House number across 992 Find a Tender award rows (3 days).
Contacts: no emails or phone numbers in any default-settings dataset above (regex check; found and fixed eForms "t:" phone identifiers used as buyer IDs).
Platform runs (build 0.1.2, 256 MB, 22 Oct 2026):
Default input: succeeded in 173 s; 56 notice rows (TED 30, Find a Tender 25, Contracts Finder 1); charged notice 56 + start 1. No emails or phone numbers.
GB+IE, CPV 72, ≥ €100k, tender+award, 2 days, 10 per source: succeeded; 28 rows (TED 8, Find a Tender 10, Contracts Finder 10), all matching every filter; charged notice 28 + start 1; 13 award suppliers with Companies House numbers.
Fixed in 0.1.2: when Find a Tender answers HTTP 429 partway through, the run now backs off within its time budget, and the status message and a free source_error row say the source was read partially.