# UK & EU Public Tenders: Find a Tender, Contracts Finder & TED (`optimistprime/uk-eu-public-tenders`) Actor

Every UK and EU public tender as clean JSON from the official Find a Tender, Contracts Finder and TED APIs. Filter by keyword, CPV, country, value and deadline. Daily bid alerts. No API key.

- **URL**: https://apify.com/optimistprime/uk-eu-public-tenders.md
- **Developed by:** [Derek VanLaningham](https://apify.com/optimistprime) (community)
- **Categories:** Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 tender notice returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## UK & EU Public Tenders: Find a Tender, Contracts Finder & TED

**Every UK and EU public tender in one clean JSON feed, from the official Find a Tender, Contracts Finder and TED APIs. Filter by keyword, CPV, country, value and deadline. Daily bid alerts. No API key.**

> **Throughput at a glance**
>
> - **UK sources (Find a Tender + Contracts Finder): up to 500 notices per minute** = 100 notices per page × 5 requests per minute. The UK services answer `429 Rate limit of 12 exceeded. Please retry after 120 seconds` well before 12 requests a minute in practice, so we pace at 5 and slow down further after any 429.
>   **Measured on Apify (Oct 3, 2026), 31-day UK pull (110 requests):** at 10 requests/min the first 429 came after ~15 requests and the run needed 7 forced 120 s waits (26.4 min); at 6/min the first 429 came after ~26 requests; at **5/min there was no wait at all (21.9 min)**. That is **~500 Find a Tender records per minute**. About 65% are award/contract notices that v1 skips, so expect **~150–175 open or planned UK notices per minute** before your keyword/CPV filters.
> - **EU TED: up to 250 notices per request** at ≤ 2 requests per second, so a whole publishing day (~1,850 calls for tender) takes well under a minute.
> - TED runs **in parallel** with the UK sources, so a mixed run takes about as long as its UK part.
> - The prefilled example (2 days, all sources, 5 notices) returns in seconds (measured Oct 5, 2026: 2.5 s, $0.017). The default `maxItems` is 50; the default 60 s time budget stops UK paging and returns what was found.
>
> **Tips for large pulls**
>
> - Pull long UK windows in one run. They are slow but don't fail. Set `timeBudgetSeconds: 0` and expect 7 days ≈ 5 min (~25 pages) and 31 days ≈ 22 min (110 pages, 3,341 notices, no waits). The max window is 92 days; split longer backfills into several runs.
> - EU only? Set `sources: ["eu-ted"]` (or countries without `GB`). The UK limiter then doesn't apply at all.
> - Use CPV codes rather than keywords for EU-wide searches. EU notices are written in the buyer's language, and CPV matching works in every language.
> - Set `maxItems: 0` for no cap, and set a **max cost per run** in the run options to control spend. The run stops cleanly when it is reached.
> - For a daily feed, use **monitor mode** with `publishedFrom: "3 days"`. You pay only for new or changed notices.

### Ready-to-run examples

Each example below is a **published task**: open its page and click **Try** to copy it into your account and run it, or paste the JSON into the Input tab (JSON view). Times, rows and costs are from real runs on Oct 3, 2026 (FREE-tier prices); results change as the source data changes.

#### UK cyber security tenders from the last 2 days

[Open this example](https://apify.com/optimistprime/uk-eu-public-tenders/examples/uk-cyber-security-tenders-last-2-days): 46 s, 1 row, $0.005 (Oct 5). Uses a 45 s time budget, newest notices first; add `"timeBudgetSeconds": 0` to search the full window.

```json
{"keywords":["cyber security","cybersecurity"],"countries":["GB"],"publishedFrom":"7 days","maxItems":6,"timeBudgetSeconds":45}
```

Output excerpt (one row, selected fields):

```json
{
 "source": "uk-fts",
 "noticeId": "093371-2026",
 "title": "715862501-Provision of a Maritime Interdiction Operations (MIOps) Training Facility",
 "buyerName": "Ministry of Defence",
 "estimatedValue": 1233300,
 "currency": "GBP",
 "publishedAt": "2026-10-02T12:52:24Z",
 "deadlineDate": "2026-10-27",
 "matchedKeywords": [
  "cyber security"
 ],
 "noticeUrl": "https://www.find-tender.service.gov.uk/Notice/093371-2026"
}
```

#### German construction tenders on TED (CPV 45)

[Open this example](https://apify.com/optimistprime/uk-eu-public-tenders/examples/germany-construction-tenders-ted-cpv-45): 4 s, 5 rows, $0.017 (Oct 5).

```json
{"sources":["eu-ted"],"cpvCodes":["45"],"countries":["DE"],"publishedFrom":"2 days","maxItems":5}
```

Output excerpt (one row, selected fields):

```json
{
 "source": "eu-ted",
 "noticeId": "674261-2026",
 "titleEn": "Germany – Installation of doors and windows and related components – Sanierung Grundschule Evesen - Innentüren",
 "buyerName": "Landkreis Schaumburg - Vergabestelle",
 "mainCpv": "45421100",
 "category": "works",
 "publishedAt": "2026-10-01T00:00:00Z",
 "deadlineDate": "2026-11-02",
 "noticeUrl": "https://ted.europa.eu/en/notice/-/detail/674261-2026"
}
```

#### IT tender alerts for the UK, Germany and France (monitor mode)

[Open this example](https://apify.com/optimistprime/uk-eu-public-tenders/examples/it-tender-alerts-monitor-mode): 47 s, 5 rows, $0.017 (Oct 5). The first run returns recent notices; later runs with the same `monitorId` return only new or changed ones (rename `monitorId` to keep your own state).

**Daily GB/DE/FR monitoring:** set `maxItems: 500` (with `timeBudgetSeconds: 0`). Estimate: roughly **$1.00–$1.50/day if ~500 new notices** ($0.002–$0.003 per notice by plan + $0.002 per run; platform compute, ~$0.001–$0.002 per run in our 3-day test, is included). Measured Sep 29–Oct 2, 2026: ~460–530 new open TED notices per weekday for DE+FR plus ~66 Find a Tender notices (1 Oct), so busy weekdays can still reach the cap; the status message then says e.g. "Returned 500 of 500+ new notices. Raise maxItems to get them all."

```json
{"monitorMode":true,"monitorId":"it-tender-alerts","cpvCodes":["72","48"],"countries":["GB","DE","FR"],"publishedFrom":"2 days","maxItems":5,"timeBudgetSeconds":45}
```

Output excerpt (one row, selected fields):

```json
{
 "source": "uk-fts",
 "noticeId": "093570-2026",
 "title": "Support for CEC Digital Products",
 "buyerName": "The Careers and Enterprise Company",
 "mainCpv": "72222300",
 "deadlineDate": "2026-10-27",
 "changeType": "new",
 "noticeUrl": "https://www.find-tender.service.gov.uk/Notice/093570-2026"
}
```

### Time budget (fast answers vs. full pulls)

- `timeBudgetSeconds` (default **60**) stops fetching after that many seconds and returns the notices found so far. This keeps AI-agent / MCP calls and quick checks to about a minute.
- **Newest first.** Both UK APIs return the most recent notices first (checked on real captures), so a quick UK call searches from now backwards and stops at the budget, at `maxItems` or at your start date. With a budget, Find a Tender is read first and every page's matches are pushed to the dataset as soon as the page arrives; Contracts Finder follows if time allows (its duplicates of Find a Tender notices are dropped and not charged). In 60 s that is about 5 UK pages (~500 of the newest records).
- TED answers in seconds and runs **in parallel** with the UK sources, so EU notices usually arrive well within the budget, even when the UK sources are still paging.
- Every run summary shows `pagesFetched` and the date range actually searched (`coveredFrom` / `coveredTo`), per source and overall. A quick call with no matches says so plainly, e.g. *"0 matches in the 5 pages fetched so far (covering 2026-10-02 14:05 UTC to 2026-10-03); rerun with timeBudgetSeconds 0 for a full pull."*
- When the budget ends early, `RUN_SUMMARY` has `"truncated": true`, `"stopReason": "timeBudget"` and a `note`, and the status message says so. You are charged only for the notices returned.
- **For a full UK pull, rerun as a normal Actor run with `timeBudgetSeconds: 0` (unlimited).** The UK APIs allow only a few requests per minute (see below), so 7 days of Find a Tender takes several minutes.
- Monitor mode is safe with a budget: a truncated run only remembers notices it actually fetched, so anything it didn't reach is reported as new on the next run. For scheduled monitors, use `timeBudgetSeconds: 0`.

### What you get

One row per notice, with the same typed schema for all three sources. Every key is always present (`null` when unknown), so the output works directly for spreadsheets, CRMs and AI agents.

| field | type | description |
|---|---|---|
| `recordType` | `notice` | `error` | `error` rows (free) appear only when a source returned incomplete results |
| `source` | `uk-fts` | `uk-cf` | `eu-ted` | which official API |
| `noticeId` | string | FTS release id (`092822-2026`), CF release id, or TED publication number (`674226-2026`) |
| `procedureId` | string|null | OCDS `ocid` / TED procedure id; groups notices of one procurement |
| `noticeKind` | `new` | `update` | `cancellation` | amendments and cancellations are flagged |
| `stage` | `tender` | `planning` | open call for bids, or a planned procurement (pipeline) |
| `noticeType` | string | raw type (`tender`, `tenderUpdate`, `cn-standard`, `pin-only`, …) |
| `title`, `titleEn` | string | title (TED: in your preferred language if available), English title |
| `description` | string|null | truncated to `descriptionMaxChars` |
| `language` | string|null | ISO 639-1 |
| `buyerName`, `buyerCountry` | string | buying organisation, ISO alpha-2 country |
| `buyerRegion`, `buyerId`, `buyerWebsite` | string|null | NUTS/ITL code, `scheme:id` (e.g. `GB-COH:07386086`), website |
| `buyerContactEmail` | string|null | **role mailboxes only** (`tenders@…`, `procurement@…`, `marches@…`) |
| `cpvCodes`, `mainCpv`, `mainCpvDescription` | | all 8-digit CPV codes (main first) |
| `category` | `goods` | `services` | `works` | null | |
| `procedureType`, `procedureTypeRaw` | | normalized (`open`, `restricted`, `negotiated`, `competitive-dialogue`, `innovation-partnership`, `direct`, `other`) + as published |
| `estimatedValue`, `currency` | number|null, string|null | in the notice's own currency (no FX conversion) |
| `publishedAt` | string | ISO 8601 UTC |
| `deadlineAt`, `deadlineDate`, `daysUntilDeadline` | | ISO UTC timestamp, `YYYY-MM-DD`, days left (negative = passed) |
| `contractStart`, `contractEnd`, `contractDurationDays` | | when published |
| `lotsCount`, `isFramework`, `suitableForSme` | | |
| `placeOfPerformance` | string\[] | NUTS/ITL codes |
| `noticeUrl`, `apiUrl`, `pdfUrl`, `documentUrls` | | official notice page (cite this), machine record, TED PDF, public tender documents |
| `alsoPublishedOn` | string\[] | the same notice was also on this source (merged, charged once) |
| `matchedKeywords` | string\[] | which of your keywords matched |
| `changeType`, `changedFields` | | monitor mode: `new`/`changed` and which fields changed |
| `licence` | string | attribution for the record |
| `errorCode`, `errorMessage` | string|null | error rows only |
| `fetchedAt` | string | ISO 8601 UTC |

Example row (Find a Tender `092822-2026`):

```json
{
  "recordType": "notice", "source": "uk-fts", "noticeId": "092822-2026", "procedureId": "ocds-h6vhtk-077e95",
  "noticeKind": "new", "stage": "tender", "noticeType": "tender",
  "title": "Cleaning Services Framework", "titleEn": "Cleaning Services Framework",
  "description": "Contract cleaning services framework", "language": "en",
  "buyerName": "DELTA ACADEMIES TRUST", "buyerCountry": "GB", "buyerRegion": "UKE45", "buyerId": "GB-COH:07386086",
  "buyerWebsite": "http://www.deltatrust.org.uk", "buyerContactEmail": "tenders@deltatrust.org.uk",
  "cpvCodes": ["90910000"], "mainCpv": "90910000", "mainCpvDescription": "Cleaning services", "category": "services",
  "procedureType": "restricted", "procedureTypeRaw": "Restricted procedure", "estimatedValue": 2000000, "currency": "GBP",
  "publishedAt": "2026-10-01T11:49:51Z", "deadlineAt": "2026-10-27T12:00:00Z", "deadlineDate": "2026-10-27", "daysUntilDeadline": 24,
  "contractStart": null, "contractEnd": null, "contractDurationDays": 1800, "lotsCount": 1, "isFramework": true, "suitableForSme": null,
  "placeOfPerformance": ["UKE"], "noticeUrl": "https://www.find-tender.service.gov.uk/Notice/092822-2026",
  "apiUrl": "https://www.find-tender.service.gov.uk/api/1.0/ocdsReleasePackages/092822-2026", "pdfUrl": null, "documentUrls": [],
  "alsoPublishedOn": [], "matchedKeywords": [], "changeType": null, "changedFields": [],
  "licence": "OGL-3.0 (contains public sector information licensed under the Open Government Licence v3.0)",
  "errorCode": null, "errorMessage": null, "fetchedAt": "2026-10-03T19:00:00Z"
}
```

### Why this Actor

- **3 official sources, 1 schema.** UK Find a Tender (above-threshold), UK Contracts Finder (below-threshold, England) and EU TED (all EU/EEA above-threshold notices). Same fields, same types.
- **Built for the UK API limiter, so runs don't fail.** Both UK services allow about 12 requests per minute per IP and then block for 120 seconds. Scrapers that ignore this fail on a large share of their runs. This Actor paces the UK hosts at **5 requests per rolling minute (12 s apart), strictly sequential, with one limiter shared by both UK hosts**; that was the fastest pace with no forced wait in our timing runs. If a `429` still arrives, it waits the server's `Retry-After` (120 s) plus a few seconds of jitter, retries the same page (up to 3 times) and **slows down by a third for the rest of the run** (never below 3 per minute). The status message tells you it's waiting. TED keeps delivering in parallel the whole time.
- **Complete Find a Tender coverage.** Find a Tender's `stages=tender` filter only returns legacy (PCR 2015) notices. On 1 Oct 2026 it returned 7 of 66 tender notices and missed every Procurement Act 2023 notice. We read every record in your window and select tender/planning notices ourselves, so nothing is missed.
- **Monitor mode charges only for new or changed notices.** Unchanged notices cost $0.
- **$3 per 1,000 notices** (Free tier), with no extra platform-usage fee.
- **Privacy-safe contacts.** No contact names or phone numbers, ever. Emails only when they're a role mailbox. Phone numbers, personal emails and "Contact: <name>" mentions are also removed from free-text descriptions.
- **MCP-ready.** Numeric values, ISO deadlines and `daysUntilDeadline` mean an AI agent needs no date maths.

### Use cases

- **Bid alerts for SMEs:** a daily feed of new tenders for your CPV codes and keywords, sent to Slack, email or a CRM.
- **B2G sales prospecting:** which councils, trusts and ministries buy what, how often and at what value.
- **Consultancies and tender-alert resellers:** one bulk normalized feed instead of three APIs.
- **Market sizing:** pipeline volume by sector, country and value, including planned procurements.
- **AI procurement assistants:** "find open IT tenders in the UK and Germany over £100k closing in the next 30 days".

### How to get UK and EU public tenders (Find a Tender, Contracts Finder and TED API)

1. **Choose sources.** **Sources** defaults to all three: UK **Find a Tender** (`uk-fts`), UK **Contracts Finder** (`uk-cf`) and the EU's **TED** (`eu-ted`).
2. **Filter.** Add **Keywords** (e.g. `cyber security`), **CPV codes** (e.g. `72` for IT services, `45` for construction), **Countries** (`GB`, `DE`, `FR`…), **Published from** (e.g. `2 days`), **Minimum value** or **Deadline within days**. **Only open** is on by default.
3. **Run.** You get one clean row per notice: buyer, title (in English where TED provides it), estimated value and currency, deadline and days left, CPV codes, procedure type, SME suitability and the official notice link.
4. **Quick or full.** By default a run stops after 60 s (**Time budget**) and returns the newest notices first. That's ideal for AI agents and quick checks. Set **Time budget** to `0` for a full pull. UK sources are paced at 5 requests/minute to respect their rate limits.
5. **Daily alerts.** Turn on **Monitor mode** with a **Monitor ID** and schedule it daily. Later runs return only new or changed notices, and you pay only for those.
6. **AI agents.** Callable as an MCP tool via `mcp.apify.com`.

Pricing: $3 per 1,000 notices on the Free plan (down to $2), plus $0.002 per run. Official open government data. Every row carries its licence/attribution (UK: Open Government Licence v3.0).

**Example inputs**

**1. IT tenders in the UK and Ireland**

```json
{ "keywords": ["software", "cloud", "cyber security"], "cpvCodes": ["72000000"], "countries": ["GB", "IE"], "publishedFrom": "7 days" }
```

**2. German construction tenders via CPV 45 (works)**

```json
{ "sources": ["eu-ted"], "cpvCodes": ["45000000"], "countries": ["DE"], "minValue": 1000000, "maxItems": 300 }
```

**3. Daily bid-alert monitor with a Slack/email webhook**

```json
{ "monitorMode": true, "monitorId": "my-it-alerts", "cpvCodes": ["72", "48"], "countries": ["GB", "IE", "NL"], "publishedFrom": "3 days", "maxItems": 0, "timeBudgetSeconds": 0 }
```

Schedule it daily at 07:00 Europe/London (Console → Schedules). Add an integration (Slack, email, Zapier, Make or a webhook) on *Run succeeded*. Each run returns only notices that are new, or whose title, deadline, value, currency or status changed (`changeType`, `changedFields`). Use `publishedFrom: "3 days"` so late-updated notices are not missed.

**4. API call**

```bash
curl -X POST "https://api.apify.com/v2/acts/optimistprime~uk-eu-public-tenders/run-sync-get-dataset-items?token=<YOUR_API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"keywords":["cybersecurity"],"countries":["GB"],"maxItems":20}'
```

### Input reference

| field | default | notes |
|---|---|---|
| `keywords` | – | ANY of these words/phrases, case-insensitive, matched at word start in title + description. EU notices use the buyer's language, so add local terms (`Software`, `logiciel`) or use CPV codes. |
| `cpvCodes` | – | 2–8 digits, optional check digit (`72000000-5`). Hierarchical: `72` or `72000000` = all IT services (72xxxxxx). Main or any additional CPV matches. |
| `countries` | all | ISO alpha-2 buyer countries (EU-27, IS, LI, NO, CH, GB and EU candidates). `GB`/`UK` enables the UK sources; only `GB` skips TED. |
| `sources` | all three | `uk-fts`, `uk-cf`, `eu-ted` |
| `stages` | `tender` | `tender` (open calls) and/or `planning` (prior information / pipeline) |
| `publishedFrom` / `publishedTo` | 2 days ago / today | `YYYY-MM-DD` or `"7 days"`; max window 92 days |
| `onlyOpen` | true | drop tender notices whose deadline has passed (notices without a deadline are kept) |
| `deadlineWithinDays` | 0 | only deadlines within N days |
| `minValue` / `excludeWithoutValue` | – / false | in the notice's currency; notices without a value are kept unless excluded |
| `includeUpdates` | true | amendments and cancellations (UK) |
| `descriptionMaxChars` | 1500 | 0 = no descriptions |
| `preferredLanguage` | `en` | TED title/description/buyer language when available |
| `monitorMode` / `monitorId` | false / `default` | only new or changed notices since the last run with this ID |
| `maxItems` | 50 (prefill 5) | 0 = no limit. While UK and TED both run, each lane gets a fair share. |
| `timeBudgetSeconds` | 60 | stop fetching after N seconds and return what was found (`truncated: true` in `RUN_SUMMARY`); 0 = unlimited. Use 0 for full UK pulls and scheduled monitors. |

**CPV primer.** CPV (Common Procurement Vocabulary) is the EU/UK classification of what is bought. The first 2 digits are the division: `03` agriculture, `09` energy, `30` office/computing equipment, `33` medical, `34` transport equipment, `45` construction works, `48` software packages, `50` repair and maintenance, `71` architecture and engineering, `72` IT services, `79` business services, `80` education, `85` health and social, `90` waste/cleaning/environmental. Full list: [CPV 2008 on SIMAP](https://simap.ted.europa.eu/cpv) and the [EU CPV regulation](https://eur-lex.europa.eu/eli/reg/2008/213/oj).

**Dates and time zones.** The UK APIs take local UK times without an offset; we send `00:00:00`–`23:59:59` of your window, so expect a ±1 h edge effect. Find a Tender filters on the **last-updated** time, so an older notice updated in your window also appears (`noticeKind` tells you if it's an update). TED filters on the publication date.

### Output reference

The default dataset holds the rows above. The default key-value store holds `RUN_SUMMARY`:

```json
{
  "sources": {
    "uk-fts": { "status": "ok", "pages": 10, "pagesFetched": 10, "complete": true, "coveredFrom": "2026-10-01T00:00:00Z", "coveredTo": "2026-10-03T23:59:59Z", "releasesFetched": 955, "rowsPushed": 11, "filteredOut": 348, "rateLimitWaits": 0, "requests": 10, "error": null },
    "uk-cf":  { "status": "ok", "pages": 1, "releasesFetched": 11, "rowsPushed": 0, "filteredOut": 11, "rateLimitWaits": 0, "requests": 1, "error": null },
    "eu-ted": { "status": "ok", "pages": 1, "releasesFetched": 250, "rowsPushed": 89, "filteredOut": 0, "rateLimitWaits": 0, "requests": 1, "error": null }
  },
  "filteredOut": 359, "filteredBy": { "keyword": 359 }, "duplicatesMerged": 0, "pushed": 100, "stopReason": "maxItems",
  "truncated": false, "timeBudgetSeconds": 60, "note": null,
  "pagesFetched": 12, "coveredFrom": "2026-10-01T00:00:00Z", "coveredTo": "2026-10-03T23:59:59Z",
  "window": { "from": "2026-10-01", "to": "2026-10-03" }, "monitor": null, "charged": { "tender": 100 }
}
```

`status` is `ok`, `partial` (the source failed after some pages; you keep what was delivered), `failed` or `skipped`. A partial run still succeeds when at least one source worked: its status message starts with `⚠ Partial:` and it adds one free `error` row per affected source. The run fails only if every selected source failed.

### Pricing

Pay per event, with no platform-usage fee on top:

| event | what | Free | Bronze | Silver | Gold | Platinum | Diamond |
|---|---|---|---|---|---|---|---|
| `tender` | Tender notice returned | $0.0030 | $0.0027 | $0.0023 | $0.0020 | $0.0020 | $0.0020 |
| `apify-actor-start` | Actor start | $0.002 | $0.002 | $0.002 | $0.002 | $0.002 | $0.002 |

- You pay only for notice rows returned after your filters. Duplicates across sources are merged and charged once. Error rows are free.
- **In monitor mode you pay only for new or changed notices**; unchanged notices are free.
- Worked examples (Free tier): 500 notices = $0.002 + 500 × $0.003 = **$1.50**. A daily monitor returning ~30 new notices = $0.092/day ≈ **$2.76/month**. A 10,000-notice backfill = **$30.00**.
- The minimum max-cost setting per run is $0.05. When your max cost per run is reached, the run stops at once with the status "Stopped: max cost per run reached after N notices".

### Use as an MCP tool / AI agents

Add the Actor to any MCP client through the Apify MCP server:
`https://mcp.apify.com/?tools=optimistprime/uk-eu-public-tenders` (streamable HTTP, `Authorization: Bearer <APIFY_TOKEN>`).

Example prompts:

- "Find open UK and German IT tenders over £100k closing in the next 30 days." → `{"cpvCodes":["72"],"countries":["GB","DE"],"minValue":100000,"deadlineWithinDays":30}`
- "Which English councils are tendering for cleaning services this week?" → `{"cpvCodes":["909"],"countries":["GB"],"publishedFrom":"7 days"}`
- "List upcoming (planned) cybersecurity procurements in the Netherlands and Belgium." → `{"keywords":["cybersecurity","cyberbeveiliging"],"countries":["NL","BE"],"stages":["planning"]}`

Which fields answer which questions: *how much* → `estimatedValue` + `currency`; *how long to bid* → `daysUntilDeadline` / `deadlineAt`; *what* → `cpvCodes`, `category`, `title`; *who* → `buyerName`, `buyerCountry`, `buyerRegion`; *cite it* → `noticeUrl`. Defaults are safe (2 days, open only, 50 max, 60 s time budget), so a call without parameters returns a small, useful result in about a minute. If `RUN_SUMMARY.truncated` is true, tell the user the list is partial; a full UK pull needs a normal run with `timeBudgetSeconds: 0`.

### Data sources, licences and attribution

- **UK Find a Tender** ([OCDS API](https://www.find-tender.service.gov.uk/apidocumentation/1.0/GET-ocdsReleasePackages)) and **UK Contracts Finder** ([OCDS search API](https://www.contractsfinder.service.gov.uk/apidocumentation/Notices/1/GET-Published-Notice-OCDS-Search)): *Contains public sector information licensed under the [Open Government Licence v3.0](https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/).* Every UK row carries this attribution in `licence`.
- **EU TED, Tenders Electronic Daily** ([Search API v3](https://docs.ted.europa.eu/api/latest/search.html)): **Source: TED – Tenders Electronic Daily, © European Union, https://ted.europa.eu.** Reuse is authorised under Commission Decision 2011/833/EU. This Actor is not affiliated with or endorsed by the European Union, the Publications Office or the UK Government, and it does not use the TED logo.
- **Fair use.**
  - Only these three official, documented, keyless APIs are contacted. No HTML pages, no proxies, no IP rotation.
  - UK hosts: 5 requests/min (12 s apart), slower after any 429, `Retry-After` honoured, largest documented page size (100) and server-side date filters to keep the request count low. Contracts Finder's documented `403` throttle gets a 5-minute pause.
  - TED: ≤ 2 requests/s (the fair-usage policy allows 700/min), and no notice XML/PDF downloads.
  - Every request carries an identifying User-Agent.

### FAQ

**How does this compare with other tender Actors?** Single-source tender Actors on the Store cost $0.002–$0.008 per notice. This one covers Find a Tender, Contracts Finder and TED in one run for $0.003, paces UK requests to avoid rate-limit failures, and has a monitor mode that charges only for new notices.

**Why is the UK part slower?** The UK services enforce about 12 requests per minute per IP and block for 120 s after that. In practice the limit bites earlier, so we pace at 5 requests per minute (slower after any 429) and a run never fails because of it. That is about 500 UK records a minute; a 31-day UK pull takes ~22 minutes. TED runs in parallel. For quick answers the default 60 s time budget returns what was found; use `timeBudgetSeconds: 0` for a complete UK pull.

**Why no contact names?** UK and EU notices often name a person, with a direct phone number and email. We never output names or phone numbers, and keep an email only when it is a role mailbox (`tenders@`, `procurement@`, `vergabe@`, `marches@`, …). Use `noticeUrl` to reach the buyer through the official notice.

**Why are some values missing?** Many buyers don't publish an estimated value, especially for frameworks and below-threshold UK notices. We never guess. Use `excludeWithoutValue` to drop them. Values are in the notice's own currency (no FX conversion).

**Does it include awards?** Not in v1. Contract awards and winners are planned for v1.1, after a privacy review (some suppliers are sole traders).

**Are keywords translated?** No. TED notices are in the buyer's language (titleEn is an English "Country – CPV – title" summary). Add local terms or, better, use CPV codes, which work across languages.

**Why do I see an older notice?** Find a Tender filters on last-updated time. An amended older notice reappears with `noticeKind: "update"`. Set `includeUpdates: false` to skip them.

### Changelog and support

See the Changelog tab. Found a problem or need a field? Open an issue on the Issues tab with the run ID.

# Changelog

This Actor's version history is a separate document: https://apify.com/optimistprime/uk-eu-public-tenders/changelog.md

# Actor input Schema

## `keywords` (type: `array`):

Return notices matching ANY of these words or phrases (case-insensitive) in the title or description. Leave empty for all. Tip: EU notices are often in the buyer's language, so add local terms (e.g. 'Software', 'logiciel') or use CPV codes for language-independent matching.

## `cpvCodes` (type: `array`):

Common Procurement Vocabulary codes (2-8 digits, optional check digit like '72000000-5'). Matching is hierarchical: '72' or '72000000' matches all IT services (72xxxxxx). A notice matches if its main or any additional CPV matches. Leave empty for all.

## `countries` (type: `array`):

ISO 3166-1 alpha-2 codes of the buying organisation's country, e.g. \['GB','DE','FR','IE']. 'GB' (or 'UK') enables the UK sources. Leave empty for all UK + EU/EEA notices.

## `sources` (type: `array`):

Which official sources to query. Default: all three.

## `stages` (type: `array`):

'tender' = live calls for bids. 'planning' = early-warning notices of upcoming procurements.

## `publishedFrom` (type: `string`):

Start of the publication window (YYYY-MM-DD, or relative like '7 days'). Default: 2 days ago (keeps the default run under 2 minutes: the UK APIs allow only a few requests per minute). Max window: 92 days. UK Find a Tender filters on last-updated time, so older notices updated in the window also appear.

## `publishedTo` (type: `string`):

End of the publication window (YYYY-MM-DD, inclusive). Default: today.

## `onlyOpen` (type: `boolean`):

Drop tender-stage notices whose submission deadline is before today (UTC). Notices without a deadline are kept. Ignored for planning notices.

## `deadlineWithinDays` (type: `integer`):

Only notices whose deadline is within the next N days (0 = no limit).

## `minValue` (type: `integer`):

Only notices with an estimated value at or above this amount, in the notice's own currency (no FX conversion). Notices without a published value are kept unless 'Exclude notices without value' is on.

## `excludeWithoutValue` (type: `boolean`):

Drop notices that publish no estimated value.

## `includeUpdates` (type: `boolean`):

Also return amended or cancelled notices (flagged with noticeKind 'update' or 'cancellation').

## `descriptionMaxChars` (type: `integer`):

Truncate descriptions to this many characters (0 = omit descriptions).

## `preferredLanguage` (type: `string`):

For TED notices, use this language for title/description/buyer name when the notice has it, else the original language. 'titleEn' is always filled when TED provides an English title.

## `monitorMode` (type: `boolean`):

Return only notices not returned in a previous run with the same Monitor ID (or whose deadline, value, status or title changed). Schedule daily for bid alerts. You only pay for returned notices.

## `monitorId` (type: `string`):

Name of the saved monitor state (letters, digits, '-'). Use one ID per saved search.

## `maxItems` (type: `integer`):

Stop after this many notices are returned (0 = no limit). Default 50 (prefilled 5 so a first try costs under $0.02). While UK and EU sources both run, each lane gets a fair share, so one fast source can't crowd out the other. You are charged per returned notice.

## `timeBudgetSeconds` (type: `integer`):

Stop fetching after this many seconds and return the notices found so far (0 = unlimited). Default 60 keeps AI-agent/MCP and quick calls to about a minute. The UK APIs allow only a few requests per minute, so for a full UK pull (e.g. 7 or 31 days) run the Actor normally with 0. UK sources are searched newest first and matches are pushed page by page, so a quick call returns the most recent notices. When the budget ends the run summary has truncated: true plus pagesFetched and the date range covered (coveredFrom/coveredTo); you are charged only for the notices returned, and monitor mode only remembers notices it actually fetched.

## `debugLog` (type: `boolean`):

Verbose logging.

## Actor input object example

```json
{
  "keywords": [
    "software",
    "cloud"
  ],
  "sources": [
    "uk-fts",
    "uk-cf",
    "eu-ted"
  ],
  "stages": [
    "tender"
  ],
  "publishedFrom": "2 days",
  "onlyOpen": true,
  "deadlineWithinDays": 0,
  "excludeWithoutValue": false,
  "includeUpdates": true,
  "descriptionMaxChars": 1500,
  "preferredLanguage": "en",
  "monitorMode": false,
  "monitorId": "default",
  "maxItems": 5,
  "timeBudgetSeconds": 60,
  "debugLog": false
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `alerts` (type: `string`):

No description

## `allRows` (type: `string`):

No description

## `runSummary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "keywords": [
        "software",
        "cloud"
    ],
    "publishedFrom": "2 days",
    "maxItems": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("optimistprime/uk-eu-public-tenders").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "keywords": [
        "software",
        "cloud",
    ],
    "publishedFrom": "2 days",
    "maxItems": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("optimistprime/uk-eu-public-tenders").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "keywords": [
    "software",
    "cloud"
  ],
  "publishedFrom": "2 days",
  "maxItems": 5
}' |
apify call optimistprime/uk-eu-public-tenders --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,optimistprime/uk-eu-public-tenders"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/gcjAvHi7ngbWVYea1/builds/SFecpygYg3JGUkcPm/openapi.json
