# Government Tenders Monitor: Canada, UK & EU Bid Alerts (`jtpalms/gov-tenders-monitor`) Actor

New public tenders from official open data in one clean feed: CanadaBuys (federal Canada), UK Find a Tender, EU TED, and US SAM.gov with your own key. Filter by keyword, code, region and closing date. Schedule it with "only new" for daily bid alerts. USD 1 per 1,000 notices.

- **URL**: https://apify.com/jtpalms/gov-tenders-monitor.md
- **Developed by:** [JT Palms](https://apify.com/jtpalms) (community)
- **Categories:** Lead generation, Business, Automation
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 1,000 tender notices

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

## Government Tenders Monitor: Canada, UK and EU bid alerts

Daily bid alerts from official public procurement portals in one clean feed: **CanadaBuys** (federal Canada), **UK Find a Tender**, **EU TED** (Tenders Electronic Daily), and **US SAM.gov** with your own free API key. Filter by keyword, classification code, country or province and closing date, and schedule it with **Only new notices** to get only tenders you have not seen before.

**USD 1 per 1,000 notices.** Runs where nothing is new cost only the start.

### What people use it for

- **IT and software firms:** a daily alert for "software", "cloud" or "cyber\*" tenders in Canada, the UK and the EU, or CPV code 72 (IT services) and UNSPSC 43.
- **Construction and trades:** works notices (`category` contains `works`) in Ontario, Scotland or one EU country, closing in the next weeks.
- **Bid consultants:** one saved task per client, each with its own keywords and regions. Each set of filters keeps its own memory of what it has already sent.
- **Alerts to Slack or e-mail:** schedule the task daily and connect an Apify integration (Slack, e-mail, Google Sheets, Zapier, Make or a webhook) to the run's dataset.
- **Canadian suppliers** in particular: federal CanadaBuys notices with UNSPSC and GSIN codes, trade agreements and regions of delivery, in English or French.

### Sample output

Real notices from the default input (keywords software, cloud, cyber; last 7 days), fetched 2026-10-05:

| Source | Title | Buyer | Closes | Value | Matched |
|---|---|---|---|---|---|
| canadabuys | Software Developers | Department of National Defence (DND) | 2026-10-14 14:00 | | software |
| canadabuys | AFCCIS Capability Enhancement | Department of National Defence (DND) | 2026-10-21 14:00 | | software |
| find-a-tender | Uniform, Inventory and Asset Management Solutio | The Police and Crime Commissioner for Nottinghamshire | 2026-11-04 12:00 UTC | GBP 200,000 | software |
| find-a-tender | Support for CEC Digital Products | The Careers and Enterprise Company | | | software |
| ted | Lithuania – Programming services of systems and user software – RITS ir SMPS elektroninio ... | Viešoji įstaiga CPO LT | 2026-11-03 | EUR 89,000 | software |
| ted | Poland – Software package and information systems – Zakup subskrypcji na system wirtualizacji Vmware ... | Gmina Miejska Kraków - Urząd Miasta Krakowa | 2026-11-03 | PLN 3,129,714.76 | software, cloud |

One full row (CanadaBuys, description shortened):

```json
{
  "id": "canadabuys:WS5915382399-Doc5915395393",
  "source": "canadabuys",
  "country": "CA",
  "title": "RFP – Programmable Logic Controller System Upgrades - Canso Canal",
  "description": "Project Description: Public Services and Procurement Canada (PSPC) on behalf of the Canadian Coast Guard (CCG) has a requirement for a consultant to prepare detailed design and construction documents to replace existing PLC-5 based system control and monitoring with modern hardware and software at the Canso Canal in Nova Scotia. ...",
  "buyer": "Department of Public Works and Government Services (PSPC)",
  "buyerRegion": "Quebec",
  "noticeType": "Request for Proposal",
  "procurementMethod": "Competitive - Selective tendering",
  "category": ["services"],
  "codes": { "cpv": [], "unspsc": ["81101701"], "gsin": [], "naics": [] },
  "codeDescriptions": ["Electrical engineering services"],
  "publishedAt": "2026-10-02",
  "closingAt": "2026-10-20T14:00:00",
  "daysLeft": 15,
  "estimatedValue": null,
  "currency": null,
  "tradeAgreements": ["Canada-Chile Free Trade Agreement (CCFTA)", "Canadian Free Trade Agreement (CFTA)", "..."],
  "regionsOfDelivery": ["Nova Scotia"],
  "noticeUrl": "https://canadabuys.canada.ca/en/tender-opportunities/tender-notice/WS5915382399-Doc5915395393",
  "documentsUrl": null,
  "matchedKeywords": ["software"],
  "licence": "Contains information licensed under the Open Government Licence – Canada",
  "fetchedAt": "2026-10-05T18:05:45.004Z"
}
```

### Input

| Field | What it does |
|---|---|
| **Sources** | `canadabuys`, `find-a-tender`, `ted` (default: all three) and `sam` (needs **SAM.gov API key**). |
| **Keywords** | A notice matches when its title or description contains any of them: whole words, any case. `cyber*` matches word endings (cybersecurity); `/regex/` is a regular expression. Empty = all notices. |
| **Exclude keywords** | Drop notices that contain any of these. |
| **Classification codes** | Code prefixes, matched against every code list of a notice: CPV for the UK and EU (`72` IT services, `48` software packages; [CPV list](https://simap.ted.europa.eu/cpv)), UNSPSC or GSIN for Canada (`43` IT equipment and software; [UNSPSC](https://www.unspsc.org)), NAICS for the US (`5415`). |
| **Countries and regions** | TED: ISO country codes such as `FRA`, `DEU` (or `FR`, `DE`). UK: `GB`, or `England`, `Scotland`, `Wales`, `Northern Ireland`. Canada: `CA`, or a province or region name such as `Ontario`, matched against the buyer's province and the regions of delivery. Find a Tender and TED are not asked at all when your list cannot match them. |
| **Look back (days)** | Notices published or updated in the last N days (default 7, up to 90). |
| **Closing after** | Hide notices that close before this date. Empty or `today` hides notices that already closed; `any` keeps them all. Notices without a closing date are kept. |
| **Minimum estimated value** | Hide notices whose estimated value is lower, in the notice's own currency. Notices without a value are kept (CanadaBuys publishes no values). |
| **Only new notices** / **First run with Only new** | See the scheduling recipe below. |
| **Maximum notices** | Cap per run across all sources (default 1,000; the form starts at 50). Sources take turns, so each gets a share. |
| **Language** | English or French titles and descriptions where the portal has them (CanadaBuys; TED titles). |
| **SAM.gov API key** | Your own free key, only for the `sam` source. Stored as a secret input, sent only to api.sam.gov, never logged or saved. |

### Output fields

| Field | What it is |
|---|---|
| `id` | Source and the portal's own ID: CanadaBuys reference number, Find a Tender OCID, TED publication number, SAM.gov notice ID. |
| `source`, `country` | Portal, and the buyer's country as an ISO 2-letter code. |
| `title`, `description` | Title, and the first 1,000 characters of the description as plain text. TED titles are TED's English (or French) title; TED descriptions are in the notice's own language. |
| `buyer`, `buyerRegion` | The contracting organisation and its province (CanadaBuys), NUTS region (Find a Tender), city (TED) or state (SAM.gov). |
| `noticeType`, `procurementMethod` | As published (for example "Request for Proposal", "Open procedure", TED `cn-standard`). |
| `category` | `goods`, `services` and/or `works`. CanadaBuys cells with several values become several entries (`*SRV` and `*GD` become `["services", "goods"]`; "services related to goods" counts as services). |
| `codes`, `codeDescriptions` | `cpv`, `unspsc`, `gsin` and `naics` code lists, and their descriptions where the portal gives them. |
| `publishedAt`, `closingAt`, `daysLeft` | Publication date, closing date and time as published, and whole days left (negative when closed). |
| `estimatedValue`, `currency` | Where published (Find a Tender, TED). |
| `tradeAgreements`, `regionsOfDelivery` | Trade agreements (CanadaBuys, Find a Tender) and delivery regions or places of performance. |
| `noticeUrl`, `documentsUrl` | The notice page on the official portal, and the tender documents or e-tendering link when the notice has one. |
| `matchedKeywords`, `licence`, `fetchedAt` | Which of your keywords matched, the source's licence credit, and when. |

The `SUMMARY` record shows, per source: status, the look-back start, requests made, notices read, matches and rows output (and the exact TED query sent).

### Scheduling recipe: daily bid alerts

1. Set your keywords, codes and regions, turn on **Only new notices**, and choose **First run**: `All current matches` to get what is open now, or `None` to start from today.
2. Save it as a task and schedule it daily.
3. Each run reads what was published since the previous run (with one day of overlap) and outputs only notices it has not output before. Runs with nothing new output nothing and cost only the start.
4. Connect Slack, e-mail or a webhook to the task to receive the new rows.

The memory is kept in your `gov-tenders-monitor-state` key-value store: per source and per set of filters, the latest 50,000 notice IDs. Changing the keywords or regions starts a new memory. Matches cut off by **Maximum notices** or by your cost limit are not remembered on later runs, so they come out next time.

### Pricing

| What | Price |
|---|---|
| Tender notice that matched your filters | USD 0.001 (USD 1 per 1,000) |
| Run start (Apify's standard start event) | USD 0.00005 per run |
| Notices filtered out, runs with nothing new, failed sources, malformed input | Free |

Example: a daily schedule that finds 20 new matching notices a day costs about 20 x 30 x USD 0.001 = USD 0.60 a month, plus USD 0.0015 in starts. The default input (up to 50 notices) costs at most USD 0.05. Set a maximum cost per run in the run options; the actor stops cleanly when it is reached and keeps everything saved so far.

### Limits and what this does not do

- **Contact persons are removed on purpose.** Names, e-mail addresses and phone numbers of contracting officers are not output (the open licences exclude personal information), and e-mail addresses and phone numbers written inside descriptions are replaced with `[email removed]` and `[phone removed]`. A person's name written in a description's free text can remain. Open `noticeUrl` for the official contacts.
- **Not every portal.** Only federal Canada (no provincial or municipal portals such as MERX-only or Bids\&Tenders notices), UK Find a Tender (not Contracts Finder), EU TED and US SAM.gov. Australia AusTender, UK Contracts Finder and Canadian provinces are on the roadmap.
- **No documents are downloaded**, and no bids are submitted. Tender documents stay on the portals (`documentsUrl`, `noticeUrl`).
- **CanadaBuys** publishes no estimated values, and its open-notices file is updated once a day. Closing times are as published, without a time zone.
- **Find a Tender**: the actor reads the whole OCDS release feed and keeps tender notices and tender updates (the API's own `stages=tender` filter returned far fewer notices on 2026-10-05). The service rate-limits bursts; when it answers HTTP 429 the actor waits the time it asks for (up to 5 minutes, otherwise that source stops for the run with a clear message), so an occasional run can take longer. A 7-day look-back read 24 pages of 100 releases on 2026-10-05.
- **TED**: keywords, CPV codes and countries are sent to TED's search, then checked again here on the English title and the description; up to 2,000 notices are read per run. Descriptions are in the notice's original language.
- **SAM.gov**: built from the official API documentation and tested against recorded sample responses only. It has not yet been run against the live SAM.gov API, because that needs a key; if it misbehaves with yours, please open an issue. It needs your own key; request limits per day depend on your SAM.gov role, and an HTTP 429 answer ends that source for the run with a message in `SUMMARY`. Descriptions (a separate keyed request per notice) are not fetched, and award and justification notices are skipped.
- Keyword matching is literal: "software" does not match "logiciel". Add the words in each language you need.

### Data sources, licences and attribution

Every row carries its source's credit in `licence`.

- **CanadaBuys** (Public Services and Procurement Canada), [open data](https://open.canada.ca/data/en/dataset/6abd20d4-7a1c-4b38-baa2-9525d0bb2fd2): "Contains information licensed under the Open Government Licence – Canada" ([licence](https://open.canada.ca/en/open-government-licence-canada)).
- **UK Find a Tender** (Cabinet Office), [OCDS API](https://www.find-tender.service.gov.uk/Developer/Documentation): "Contains public sector information licensed under the Open Government Licence v3.0" ([licence](https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/)).
- **EU TED** (Publications Office of the European Union), [TED API](https://docs.ted.europa.eu/api/latest/index.html): © European Union. TED's legal notice says the procurement notices "can be freely reused, for commercial or non-commercial purposes".
- **US SAM.gov** (General Services Administration), [Get Opportunities API](https://open.gsa.gov/api/get-opportunities-public-api/): U.S. Government data, used with your own key.

All requests identify the actor with the user agent `Mozilla/5.0 (compatible; gov-tenders-monitor/1.0; +https://apify.com/JTPalms)`, at most one request per second per portal (one per 1.5 s for Find a Tender). No proxies, no logins, no scraping of web pages: only the portals' open-data files and APIs.

### Related actors

- [RSS Feed Reader & Scraper: Atom, JSON Feed & New-Item Monitor](https://apify.com/JTPalms/rss-feed-monitor): RSS feed reader and scraper.
- [Hiring Signals: Companies Hiring by Role, Team & Location](https://apify.com/JTPalms/hiring-signals): Find companies hiring right now, one row per company: open roles by team, seniority and location, roles posted in the last 7 and 30...

### FAQ

**Why did a notice match "cyber" when its title does not say so?** Keywords are matched in the title and the description; the description may mention it (for example a UK Ministry of Defence notice stating its "Cyber Risk Profile").

**Can I get notices that already closed?** Set **Closing after** to `any`.

**Why are some CanadaBuys notices missing from a 7-day run?** The look-back uses the publication or amendment date. Raise **Look back (days)** (up to 90) to include older open notices.

**Something missing or wrong?** Open an issue with your input.

# Actor input Schema

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

Which official portals to read. canadabuys = CanadaBuys (federal Canada), find-a-tender = UK Find a Tender, ted = EU Tenders Electronic Daily, sam = US SAM.gov (needs your own free API key below).

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

A notice matches when its title or description contains any of these words (whole words, any case). End a word with \* to match endings (cyber\* matches cybersecurity), or write /regex/. Empty = all notices.

## `excludeKeywords` (type: `array`):

Drop notices whose title or description contains any of these words.

## `codes` (type: `array`):

Keep notices with a code that starts with any of these: CPV for the UK and EU (72 = IT services, 48 = software packages), UNSPSC or GSIN for Canada (43 = IT equipment and software), NAICS for the US (5415 = computer systems design). Lists: CPV https://simap.ted.europa.eu/cpv and UNSPSC https://www.unspsc.org.

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

Keep notices from these places. TED: ISO country codes such as FRA, DEU (or FR, DE). UK: GB, or England, Scotland, Wales, Northern Ireland. Canada: CA, or a province or region name such as Ontario or British Columbia (matched against the buyer's province and the regions of delivery).

## `sinceDays` (type: `integer`):

Notices published or updated in the last N days. With Only new, later runs look back to the previous run instead (with one day of overlap).

## `closingAfter` (type: `string`):

Hide notices that close before this date (YYYY-MM-DD). Empty or today = hide notices that already closed. Write any to keep them all. Notices without a closing date are kept.

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

Hide notices whose estimated value is below this amount, in the notice's own currency. Notices without a value are kept (CanadaBuys publishes none).

## `onlyNew` (type: `boolean`):

Remember which notices earlier runs saw and output only notices that are new. Schedule the actor daily to get bid alerts; runs with nothing new output nothing and cost only the start. The memory is kept per source and per set of filters in your gov-tenders-monitor-state key-value store (the latest 50,000 notice IDs).

## `firstRunOutput` (type: `string`):

All: the first run outputs the current matches. None: the first run outputs nothing and only records what is there, so you get only notices published after today.

## `maxResults` (type: `integer`):

Cap per run, across all sources. Sources take turns, so each gets a share of the cap.

## `language` (type: `string`):

Language of titles and descriptions where the source has it: CanadaBuys (English or French) and TED titles. Find a Tender and SAM.gov are in English.

## `samApiKey` (type: `string`):

Your own free SAM.gov public API key, needed only for the sam source. Get it on the Account Details page of SAM.gov. Daily request limits depend on your SAM.gov role. The key is sent only to api.sam.gov and never logged or saved.

## Actor input object example

```json
{
  "sources": [
    "canadabuys",
    "find-a-tender",
    "ted"
  ],
  "keywords": [
    "software",
    "cloud",
    "cyber"
  ],
  "sinceDays": 2,
  "onlyNew": false,
  "firstRunOutput": "all",
  "maxResults": 50,
  "language": "en"
}
```

# Actor output Schema

## `results` (type: `string`):

One row per tender notice that matched your filters (JSON, CSV, Excel via the format parameter). Each row carries its source licence.

## `summary` (type: `string`):

Per source: status, look-back start, requests, notices read, matches and rows output; billed count; cost-limit stop.

# 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 = {
    "sources": [
        "canadabuys",
        "find-a-tender",
        "ted"
    ],
    "keywords": [
        "software",
        "cloud",
        "cyber"
    ],
    "sinceDays": 2,
    "maxResults": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("jtpalms/gov-tenders-monitor").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 = {
    "sources": [
        "canadabuys",
        "find-a-tender",
        "ted",
    ],
    "keywords": [
        "software",
        "cloud",
        "cyber",
    ],
    "sinceDays": 2,
    "maxResults": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("jtpalms/gov-tenders-monitor").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 '{
  "sources": [
    "canadabuys",
    "find-a-tender",
    "ted"
  ],
  "keywords": [
    "software",
    "cloud",
    "cyber"
  ],
  "sinceDays": 2,
  "maxResults": 50
}' |
apify call jtpalms/gov-tenders-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jtpalms/gov-tenders-monitor"
        }
    }
}
```

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/3RnaVM04IMzPN1FVa/builds/eagvyz9mmtWfp81WC/openapi.json
