# Public Tenders Feed: EU, UK, US, Canada, New Only (`lone_rachis/public-tenders-feed`) Actor

New public tenders from EU TED, UK Find a Tender, the SAM.gov public extract and CanadaBuys in one record shape. Filter by CPV, NAICS, UNSPSC, keywords, country and value. Each run delivers only notices it did not deliver before, with a free summary row per source.

- **URL**: https://apify.com/lone_rachis/public-tenders-feed.md
- **Developed by:** [Kesav Kumar Jayakumar](https://apify.com/lone_rachis) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 1,000 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

### What does Public Tenders Feed do?

**Public Tenders Feed** returns new public tenders from four official sources in one record shape: the EU's TED (Tenders Electronic Daily), the UK's Find a Tender, the US SAM.gov Contract Opportunities extract and CanadaBuys. You set the sources, the notice types, your codes (CPV, NAICS, UNSPSC), keywords and buyer countries, run it on a schedule, and each run returns only the notices it has not delivered to you before.

- **TED (`ted`):** tenders of buyers in the EU and the EEA, filtered by CPV code, keyword and buyer country.
- **Find a Tender (`uk_fts`):** UK tenders above the thresholds, filtered by CPV code and keyword.
- **SAM.gov (`sam`):** US federal opportunities from the public extract, filtered by NAICS code and keyword.
- **CanadaBuys (`canadabuys`):** Canadian federal tenders, filtered by UNSPSC code and keyword.
- **Contracts Finder (`uk_cf`):** the UK's older service for lower-value notices, off unless you add it.

Each row is one notice: its ID and page, the type (open tender, planning, award or update), the title, the buyer and its country, the codes, the value when one is stated, the deadline, the publication date, the place of performance, the procedure, the set-aside for US notices, links to the documents, and which of your codes and keywords it matched. Each source also gets one free summary row per run: what it read, what matched, what was delivered and why the rest was not. A source that fails says why in its summary row, and the other sources still deliver.

No contact names, emails or phone numbers are taken from any source, and descriptions are searched but never copied into the rows. **This Actor is not affiliated with or endorsed by the Publications Office of the EU, the UK Cabinet Office, the US General Services Administration or Public Services and Procurement Canada.**

### Why use Public Tenders Feed?

Finding tenders to bid on means checking four portals with four search forms, four code lists and four date rules, and working out which notices you saw last week. This Actor asks all four for you, puts the notices in one table, remembers what it delivered, and on a schedule sends only what is new.

What you get:

- **Four official sources, one record shape.** TED, Find a Tender, SAM.gov and CanadaBuys map to the same fields, so one sheet or one alert handles them all.
- **Every filter works on every source it applies to.** Codes are matched per scheme (CPV, NAICS, UNSPSC), keywords on every source, and each filter has a test per source.
- **Only what is new.** The memory keeps the notice IDs it delivered, so a notice reaches you once, and a later run reads only from where the last complete run stopped.
- **A free summary row per source.** It shows how many notices the source had, how many matched and why the others were left out, so a run that finds nothing is visible and costs nothing.
- **A failing source does not stop the run.** Its summary row says what went wrong, and the next run reads its days again.
- **No personal data.** No contact details from any source, and no description text.
- **No key needed.** All four sources are public.

Each claim above has a test behind it:

| What you get | How it works | The test that backs it |
| --- | --- | --- |
| **Four sources, one shape** | Each source has its own reader that maps its answer into the same fields. The tests run every reader on real answers saved from the sources on 15 Oct 2026. | "TED: every saved notice maps, with the English title only, the TED page and the PDF link", "Find a Tender and Contracts Finder: release tags give the type, and every saved release maps without contact details", "SAM.gov: every saved row maps; the key holds the type; values only on awards; no contact column is read", "CanadaBuys: every saved row maps; an amended tender also gives an update notice; RFI is planning, ACAN is an award", and the live test "live: the prefill run returns rows and summary rows, and a second run on the same memory repeats nothing". |
| **Filters per source** | CPV, NAICS and UNSPSC codes match by prefix, keywords as whole words, types, countries and values the same way everywhere. | "CPV codes (TED, Find a Tender, Contracts Finder) match by prefix, and trailing zeros do not matter", "NAICS codes match SAM.gov notices by prefix", "UNSPSC codes match CanadaBuys notices by prefix, and trailing 00 pairs do not matter", "keywords in titles: every source delivers only notices whose title holds a keyword as a whole word", "excludeKeywords drop notices at every source", "match all needs a code and a keyword; match any takes either", "minValue drops notices with a lower stated value at every source that states values, and keeps the ones without". |
| **Only what is new** | The memory holds the IDs delivered in the last 60 days. The window starts where the last complete read of the source started. | "later runs on the same memory never repeat a notice, and the SAM.gov extract is not downloaded again once read in full", "a complete read moves the window: the next run asks Find a Tender only from the last run on", "the window: firstRunDays on a first run, the last complete run later (a day earlier for date-only sources), or since". |
| **The SAM.gov extract read with care** | The 200 MB file is streamed row by row. It lists the newest notices first, so reading stops once 1,000 rows in a row are older than the window. An unchanged file is not downloaded again. | "a newest-first extract stops downloading after 1,000 rows older than the window, with every notice of the window read", "a row out of order switches the early stop off, so the whole file is read", "an unchanged extract after a complete read with the same filters is not downloaded", "a SAM.gov download that breaks off is a failed read, not a short file taken as complete". |
| **A failing source does not stop the run** | Each source is read on its own. Its summary row carries the reason and its window is kept. | "a source that keeps failing does not fail the run: its summary row says why, the others deliver", "a failed source keeps its window: the next run reads the same days again", "a source that never answers times out, and the others are not held up", "an answer that is not the expected shape fails that source only". |
| **Polite to the sources** | TED is paced under 500 notices per 6 minutes and stops at a limit answer. Find a Tender waits as long as its Retry-After header asks. Every request names the Actor. | "TED: reading keeps under 500 notices per 6 minutes (the fair-use limit is 600)", "TED: a limit answer (HTTP 429) stops TED at once, keeps what was read, and says so", "Find a Tender: Retry-After on a 429 is honoured, then reading goes on", "every request names the Actor in its User-Agent". |
| **No personal data** | Contact columns and contact points are never read, and descriptions are used for matching only. | "no contact detail from any source reaches a row, the summary or the memory". |
| **Charged only for what you get** | One charge per row with status `ok`. Summary rows are free, and the run stops before it would charge past your maximum cost per run. A run that restarts reads its own rows first. | "the prefill run delivers notices plus a free summary row per source, and charges one event per notice", "your maximum cost per run stops delivery; the next run delivers the rest without repeats", "a restart after a row was written but before the memory was saved charges nothing twice". |

Plus what the Apify platform adds around it:

- **API access.** Start runs and read results with the Apify API or the JavaScript and Python clients.
- **Scheduling.** Run the feed every weekday morning and get only what was published since the last run.
- **Integrations.** Send rows to Make, Zapier, Google Sheets, Slack or any webhook.
- **Monitoring.** Run history, logs and alerts for failed runs.

Typical uses are a daily list of tenders for a bid team, an alert on the codes a company sells in, a feed of awards to see who wins what in a market, and a tender feed for an AI agent that should read only what is new.

### What data can Public Tenders Feed extract?

Each notice becomes one row in the dataset. Each source also gets one free summary row per run. The fields:

| Field | Type | What it holds |
| --- | --- | --- |
| `status` | string | `ok` (a notice, charged) or `summary` (the free row of one source). |
| `source` | string | `ted`, `uk_fts`, `sam`, `canadabuys` or `uk_cf`. |
| `noticeId`, `url` | string | The source's own ID for the notice and its public page. TED gives a publication number such as `718613-2026`, Find a Tender a notice number such as `097467-2026`, SAM.gov a 32-character notice ID, CanadaBuys a reference number (with `#` and the amendment number on an update notice). |
| `noticeType`, `sourceType` | string | `open`, `planning`, `award` or `update`, and the type in the source's own words, such as `competition / cn-standard` for TED or `Combined Synopsis/Solicitation` for SAM.gov. |
| `title` | string | The title. For TED it is TED's English heading: the buyer's country, the main CPV label and the buyer's own title, which is often in the buyer's language. |
| `buyerName`, `buyerCountry` | string | The buying organisation, and its country as a two-letter code. |
| `classification` | object | `scheme` (`CPV`, `NAICS` or `UNSPSC`) and `codes`, the codes of the notice and its lots. |
| `psc` | string | SAM.gov only: the product or service code. |
| `value` | object | `amount` and `currency` when the notice states a value above 0, in the notice's own currency. SAM.gov gives values on awards only, CanadaBuys gives none. |
| `deadline`, `publishedAt` | string | The deadline for bids or responses and the publication date, as the source writes them. TED gives the earliest lot deadline with its time. |
| `placeOfPerformance`, `procedure`, `setAside` | string | Where the work is done (NUTS codes for TED, regions for the UK and Canada, city and state for the US), the procedure type, and the US small-business set-aside. |
| `documentLinks` | array | The notice page, the PDF or the buyer's tender documents, and attachments, at most 20. |
| `matchedKeywords`, `matchedCodes` | array | Which of your keywords and codes the notice matched. |
| `firstSeenAt`, `licence` | string | When this memory first delivered the notice, and the licence line of the source, on every row. |
| `sourceStatus` | string | Summary rows: `ok` (read and delivered in full), `partial` (read or delivered in part; the rest comes next run), `unchanged` (the SAM.gov extract has not changed since the last complete read, so it was not downloaded), `skipped` or `failed`. |
| `windowFrom`, `windowTo` | string | Summary rows: the time span the source was asked for. |
| `noticesRead`, `noticesMatched`, `noticesNew`, `delivered`, `alreadySeen`, `notDelivered` | integer | Summary rows: what the source sent, what matched your filters, what was new to the memory, what this run delivered, what was delivered before, and what is left for the next run. |
| `byType`, `filteredOut` | object | Summary rows: the matches per notice type, and the notices left out by reason: `window`, `noticeType`, `country`, `value`, `topic` (outside your codes and keywords), `excluded` (an exclude keyword) and `noDate`. |
| `filtersApplied`, `sourceTotal`, `fileLastModified`, `durationMs` | array, integer, string, integer | Summary rows: your filters as this source used them, TED's own count for the query (or the rows in the SAM.gov and CanadaBuys files read), the extract's Last-Modified for SAM.gov, and the read time. |
| `stateStore`, `checkedAt` | string | The memory's store name, and when the row was written. |
| `message`, `errorCode` | string | Summary rows: what happened in plain words, and a short code when there is something to report. |

The **Notices** view shows the source, type, title, buyer, country, deadline, publication date, value, what matched and the link. The **Source summaries** view shows each source's counts, status and message.

A summary row that has something to report carries one of these codes in `errorCode`, so you can count by reason without reading the sentence. The sentence in `message` can be reworded in a later version. The code stays.

| `errorCode` | Meaning |
| --- | --- |
| `excluded-by-countries` | The source covers one country only, and your `countries` leave it out, so it was skipped without a request. |
| `no-filter-for-source` | You gave codes for other schemes only and no keyword, so this source had nothing to match and was skipped. |
| `max-notices` | The run reached `maxNotices`. The next run delivers the rest. |
| `charge-limit` | The run reached your maximum cost per run. The next run delivers the rest. |
| `run-time-limit` | The run reached its time limit. The next run reads the window again. |
| `aborted` | The run was stopped. |
| `read-limit` | The source had more than one run reads (2,000 TED notices, 400 pages from the UK services), or TED's pace would pass the time limit. The next run goes on. |
| `rate-limited` | The source answered "too many requests": at once for TED, three times in a row for the others. |
| `server-error`, `http-error` | The source answered with an error status, after three retries for a server error. |
| `network-error`, `timeout` | The source could not be reached, or did not answer or send data in time. |
| `too-large`, `bad-response`, `unexpected-redirect` | A file over the size limit, an answer not in the expected shape (the source may have changed), or a redirect to another site. |

### How to get new public tenders by code, keyword and country

1. Open the Actor in Apify Console and select the **Input** tab.
2. The form is prefilled with a small run: TED and SAM.gov, open tenders, CPV 72 (IT services) for TED, NAICS 5415 (computer systems design) for SAM.gov and the keyword "software" in titles, 3 days back, at most 20 notices. Press **Start** to see a result straight away. On this computer on 16 Oct 2026 it delivered 20 notices (10 from each source) in about 12 seconds, and the same input run again delivered 20 others and nothing twice.
3. Pick the **Sources** (`sources`) and **Notice types** (`noticeTypes`): `open` for tenders you can still bid on, `planning` for prior information and market research, `award` for awards, `update` for changes and cancellations.
4. Add your codes: **CPV codes** (`cpv`) for TED and the UK services, **NAICS codes** (`naics`) for SAM.gov and **UNSPSC codes** (`unspsc`) for CanadaBuys. A short code covers everything under it: CPV `72` is all IT services, `7221` software programming.
5. Add **Keywords** (`keywords`), such as `cybersecurity` or `cyber*`, and choose **Search keywords in** (`keywordsIn`) and **Codes and keywords** (`match`). **Exclude keywords** (`excludeKeywords`) drop notices that hold them.
6. Optional: **Buyer countries** (`countries`), such as `DE`, `FR` and `IE` for TED, and **Minimum value** (`minValue`).
7. Set **First run days** (`firstRunDays`) and **Most notices per run** (`maxNotices`). The first run reads that many days back; later runs read from where the last one stopped (`since` set to `lastRun`).
8. Save the input as a task, or set **Memory name** (`stateStoreName`), such as `my-it-tenders`. Then you can change the filters and keep the memory.
9. Press **Start**. Open the **Output** tab for the table and export it as JSON, CSV or Excel. Then create a schedule with the same input, for example every weekday at 7:00.

To run it from code, send the same input to the Actor through the API:

```json
{
    "sources": ["ted", "uk_fts", "sam", "canadabuys"],
    "noticeTypes": ["open"],
    "cpv": ["72", "48"],
    "naics": ["5415"],
    "unspsc": ["43"],
    "keywords": ["cyber*", "cloud"],
    "excludeKeywords": ["training"],
    "countries": ["DE", "FR", "GB", "US", "CA"],
    "firstRunDays": 7,
    "maxNotices": 500,
    "stateStoreName": "my-it-tenders"
}
```

### How to send new tenders to Slack or Google Sheets (Make, Zapier, no code)

After the first run, a scheduled feed holds only the notices that are new since the last run, so you can pass every run's results straight on.

**Slack, built in.** On the Actor's **Integrations** tab, add **Slack**, connect your workspace, pick a channel and choose the event "Run succeeded". Each finished run posts a message with a link to its results.

**Google Sheets with Make.com.**

1. In Make, create a scenario and add the Apify module that watches for finished Actor runs, set to this Actor.
2. Add the Apify module that gets dataset items, and map the run's default dataset ID.
3. Add Google Sheets "Add a Row" and map the fields you want, for example `source`, `title`, `buyerName`, `buyerCountry`, `deadline`, `value` and `url`.
4. Add a filter so only rows with `status` `ok` reach the sheet, or only rows of one `noticeType`, or only summary rows with an `errorCode` for an alert.

**Google Sheets or Slack with Zapier.** Use the Apify trigger for a finished Actor run, then the Apify step that fetches dataset items, then a Google Sheets "Create Spreadsheet Row" or a Slack "Send Channel Message" action. A Zapier filter step works the same way as the Make filter.

**Your own code.** Add a webhook on the **Integrations** tab for the event "Run succeeded", and read the dataset from the URL in the payload.

### How much does it cost to get new public tenders?

You pay per notice delivered, and the Apify platform usage for the run is included. A notice costs $0.0030 on the Free and Bronze plans, so 1,000 notices cost $3.

| Event | When it is charged | Price |
| --- | --- | --- |
| `notice` | One notice row delivered with status `ok`: each notice of the first run, and each new notice after that, once per memory | $0.0030 (Free, Bronze), $0.0027 (Silver), $0.0025 (Gold) |
| Actor start | Once per run, scaled by memory (Apify standard event) | $0.00005 per GB of memory |

Summary rows cost nothing. You can cap a run with the **Maximum cost per run** setting, and the Actor stops cleanly at that limit.

#### How much does one notice cost?

One notice costs $0.0030 on the Free and Bronze plans, $0.0027 on Silver and $0.0025 on Gold. A run adds a start fee of about $0.000025 at the default memory of 512 MB. As a guide: TED listed 72 open tenders for IT services (CPV 72) from German buyers from 12 to 15 Oct 2026, about 18 a publishing day, so about 400 a month: $1.20 a month. The same code across the whole EU gave about 115 a publishing day.

#### How do I keep a run within budget?

Set **Maximum cost per run** when you start it or in the schedule, and **Most notices per run** (`maxNotices`) in the input. The run stops before it would charge past either, and the sources it did not finish get the code `charge-limit` or `max-notices` in their summary rows. Those notices stay out of the memory, so the next run delivers them.

#### What does a run cost when nothing is new?

Only the start fee, about $0.000025 at 512 MB. Each source writes its free summary row, and nothing is charged.

#### How often should I run it?

Once a day on weekdays fits the sources: TED publishes its notices once each weekday (they appear the evening before the date they carry), SAM.gov writes its extract once a day, and CanadaBuys writes its list of open tenders once a day. Find a Tender publishes through the day. You pay per notice, not per run, so a run more often costs you only its start fee.

### Input

Open the **Input** tab to see every setting with a short description. No field is required: an empty input reads the four default sources for open tenders from the last run, 7 days back on the first. The form has three parts:

- **What to read:** `sources` and `noticeTypes`.
- **What to match:** `keywords`, `cpv`, `naics`, `unspsc`, `match`, `keywordsIn`, `excludeKeywords`, `countries` and `minValue`.
- **Window and memory:** `since`, `firstRunDays`, `maxNotices` and `stateStoreName`.

`sources` takes `ted`, `uk_fts`, `sam`, `canadabuys` and `uk_cf`. The first four are the default.

`noticeTypes` takes `open` (the default), `planning`, `award` and `update`. Each source's own types map to these: for TED, competition notices are `open`, prior information `planning`, results and direct award notices `award`, changes and contract modifications `update`. For SAM.gov, solicitations and combined synopses are `open`, presolicitations, sources sought and special notices `planning`, award notices and justifications `award`, modifications `update`. For CanadaBuys, requests for information are `planning`, advance contract award notices `award`, an amendment adds an `update` notice, and every other notice is `open`.

`keywords` takes up to 50 words or phrases. A notice matches when one appears as whole words, any case, accents ignored; a phrase matches as neighbouring words, and a word ending in `*` matches the start of a word. `keywordsIn` is `titleAndDescription` (the default) or `title`. `excludeKeywords` drop a notice when they appear in the same text.

`cpv`, `naics` and `unspsc` take up to 200 codes each and match by prefix. CPV trailing zeros and its check digit are ignored (`72000000-5` works like `72`), UNSPSC trailing `00` pairs too. Each source uses its own scheme: CPV for TED, Find a Tender and Contracts Finder, NAICS for SAM.gov, UNSPSC for CanadaBuys.

`match` is `any` (the default: one of your codes or one of your keywords) or `all` (a code of the source's scheme and a keyword). Give no code and no keyword to get every notice of the chosen types. When you give codes for some schemes only and no keyword, a source of another scheme is skipped with `no-filter-for-source`, so CPV codes alone do not fill your feed with every SAM.gov notice.

`countries` takes up to 60 buyer country codes, two letters (`DE`) or three (`DEU`). TED is asked for those countries. Find a Tender and Contracts Finder (`GB`), SAM.gov (`US`) and CanadaBuys (`CA`) are skipped without a request when their country is not in the list.

`minValue` drops notices whose stated value is below it, in the notice's own currency. Notices without a stated value are kept, because most open tenders state none.

`since` is `lastRun` (the default) or a date such as `2026-10-01`. `firstRunDays` is 7 by default, from 1 to 30. No window reaches more than 30 days back. `maxNotices` is 500 by default and at most 10,000, all sources together, newest first, the sources taking turns.

`stateStoreName` names the key-value store that remembers what was delivered. Left empty, a run of a saved task keeps one memory for that task, and a run outside a task keeps one memory per set of filters, so changing a filter there starts a new memory and the next run delivers (and charges) its first run again.

### Output

You can download the dataset in various formats such as JSON, HTML, CSV, or Excel. A run also writes a `SUMMARY` record to its key-value store with the memory's name, the sources read, the notices delivered and charged, the reason the run stopped early if it did, one entry per source, the run time and the peak memory. A run whose sources all failed and that delivered nothing ends with the status Failed and the reason in its message, so a schedule shows it. A wrong input or a wrong billing set-up is refused before any request, in words.

A notice from Find a Tender, the summary of SAM.gov on the first prefill run, and its summary on the next run look like this:

```json
[
    {
        "status": "ok",
        "errorCode": null,
        "message": null,
        "source": "uk_fts",
        "noticeId": "097467-2026",
        "url": "https://www.find-tender.service.gov.uk/Notice/097467-2026",
        "noticeType": "open",
        "sourceType": "tender / UK4",
        "title": "Programme Management Platform",
        "buyerName": "Cheshire West and Chester Council",
        "buyerCountry": "GB",
        "classification": { "scheme": "CPV", "codes": ["72222300"] },
        "psc": null,
        "value": { "amount": 400000, "currency": "GBP" },
        "deadline": "2026-11-12T10:00:00Z",
        "publishedAt": "2026-10-15T13:11:36+01:00",
        "placeOfPerformance": "UKD",
        "procedure": "Open procedure",
        "setAside": null,
        "documentLinks": ["https://www.find-tender.service.gov.uk/Notice/097467-2026"],
        "matchedKeywords": [],
        "matchedCodes": ["72"],
        "firstSeenAt": "2026-10-15T22:45:35.630Z",
        "licence": "Source: Find a Tender (find-tender.service.gov.uk). Contains public sector information licensed under the Open Government Licence v3.0.",
        "sourceStatus": null,
        "windowFrom": null,
        "windowTo": null,
        "noticesRead": null,
        "noticesMatched": null,
        "noticesNew": null,
        "delivered": null,
        "alreadySeen": null,
        "notDelivered": null,
        "byType": null,
        "filteredOut": null,
        "filtersApplied": null,
        "sourceTotal": null,
        "fileLastModified": null,
        "durationMs": null,
        "stateStore": "my-it-tenders",
        "checkedAt": "2026-10-15T22:45:35.630Z"
    },
    {
        "status": "summary",
        "errorCode": null,
        "message": "SAM.gov (US), notices published from 2026-10-12 (first run): 4,588 notices read, 10 matched your filters (10 open), 0 delivered before, 10 new, 10 delivered. Left out: 1,000 published before the window, 1,128 of other notice types, 2,450 outside your codes and keywords. The extract lists the newest notices first, so reading stopped after 1,000 notices older than the window (11.2 MB of 202.6 MB downloaded). Free row.",
        "source": "sam",
        "sourceStatus": "ok",
        "windowFrom": "2026-10-12T22:26:54.963Z",
        "windowTo": "2026-10-15T22:26:54.963Z",
        "noticesRead": 4588,
        "noticesMatched": 10,
        "noticesNew": 10,
        "delivered": 10,
        "alreadySeen": 0,
        "notDelivered": 0,
        "byType": { "open": 10, "planning": 0, "award": 0, "update": 0 },
        "filteredOut": { "window": 1000, "noticeType": 1128, "country": 0, "value": 0, "topic": 2450, "excluded": 0, "noDate": 0 },
        "filtersApplied": ["types: open", "NAICS: 5415", "keywords (title): software", "match: any"],
        "fileLastModified": "Thu, 15 Oct 2026 03:30:31 GMT",
        "durationMs": 12151
    },
    {
        "status": "summary",
        "message": "SAM.gov (US): the extract has not changed since the last complete read (Last-Modified Thu, 15 Oct 2026 03:30:31 GMT), so it was not downloaded: there is nothing new. Free row.",
        "source": "sam",
        "sourceStatus": "unchanged",
        "noticesRead": 0,
        "delivered": 0
    }
]
```

The rows come from local runs on 16 Oct 2026 against the real sources. The summary rows are shortened. Every row carries every field, with `null` where a value does not apply, so exports keep the same columns.

### Tips for faster runs and lower costs

- Save the input as a task, or set `stateStoreName`, before you change the filters. Outside a task and without a name, a change starts a new memory, and the next run delivers and charges its first run again.
- For SAM.gov, prefer NAICS codes or `keywordsIn` `title`. SAM.gov descriptions are long and carry standard wording, such as a line on the software needed to open the attachments, so a keyword searched in descriptions finds notices on other subjects. On 16 Oct 2026, 5 of the 10 SAM.gov notices that "software" found in descriptions were about snow removal, bearings and equipment.
- Keep the first run small with `firstRunDays` and `maxNotices`, then let the schedule bring the rest.
- Use `countries` to skip whole sources: with `["DE", "FR"]` only TED is read.
- A first run of all four sources 7 days back, all notice types, took about 2.5 minutes on this computer on 16 Oct 2026, most of it a 2-minute wait that Find a Tender asked for with its Retry-After header. A daily run of the same codes took about 12 seconds.
- TED is read at most 500 notices per 6 minutes, below its fair-use limit. A catch-up that needs more than that waits, and a run that read 1,000 TED notices took about 6.5 minutes.
- The default 512 MB is plenty: the SAM.gov extract is streamed, and the highest peak measured was 144 MB.

### FAQ, disclaimers and support

#### Where does the data come from, and may I use it?

From the official publishers, through their public interfaces and files:

- **TED:** the TED search API of the Publications Office of the European Union. Reuse is allowed under Commission Decision 2011/833/EU, with the source named.
- **Find a Tender and Contracts Finder:** the UK services' OCDS interfaces. Public sector information licensed under the Open Government Licence v3.0.
- **SAM.gov:** the Contract Opportunities public data extract on data.gov. US federal government data, public domain.
- **CanadaBuys:** the open data files of tender notices. Information licensed under the Open Government Licence - Canada.

Every row carries its source's licence line in `licence`. Keep it when you pass the rows on. How you may use what you collect depends on your purpose and your country, so check that for your use case. This is general information, not legal advice.

#### Is this an official tool of any of the publishers?

No. The Actor is an independent tool, not affiliated with, made, endorsed or checked by the Publications Office of the EU, the UK Cabinet Office, the US General Services Administration or Public Services and Procurement Canada. The data comes as the publishers give it. Open the notice page (the `url` column) and the tender documents before you bid or act on a row.

#### What counts as new, and where can it be wrong?

A notice is new when the memory has not delivered it before. The memory knows a notice by the TED publication number, the Find a Tender or Contracts Finder release ID, the SAM.gov notice type and ID, or the CanadaBuys reference number (with the amendment number for an update notice). The memory keeps the keys delivered in the last 60 days, at most 100,000 per source, longer than any window reaches back. Each source reads from the start of its last complete read, a day earlier for the sources that give dates without a time, and the repeats this brings are counted in `alreadySeen` and not delivered. A read is complete when the source was read to the end and every notice that matched was delivered. Where the result can be wrong:

- **One tender, several notices.** A tender, its changes and its award are separate notices with their own IDs at every source, so with `award` and `update` on you get each of them once.
- **A SAM.gov notice that changes type.** A solicitation that becomes an award notice keeps its ID, and the type is part of the key, so the award notice is delivered as new.
- **A new memory.** A changed set of filters outside a task and without `stateStoreName` starts a new memory, which delivers its first run again.
- **A notice older than the window.** A notice published before the window, or one a source adds late with an old date, is not read.
- **A run that stops early.** A source cut by `maxNotices`, the maximum cost per run or the time limit keeps its window, and the next run delivers what was left.

#### How does a notice match, and where can it be wrong?

Codes match by prefix in the scheme of the source: CPV for TED and the UK services, NAICS for SAM.gov, UNSPSC for CanadaBuys. A notice's codes are those of the notice and of its lots or items. Keywords match whole words in the title and, with `titleAndDescription`, in the description: TED's English heading and lot titles and its procedure and lot descriptions, the UK tender title and description, the SAM.gov title and description, the CanadaBuys title and description in English and French. Text is compared in lower case without accents. `match` `any` takes a code or a keyword, `all` needs both. Where the result can be wrong:

- **SAM.gov descriptions.** They carry standard wording, so a keyword can match a notice on another subject (see the tips).
- **TED titles in other languages.** TED's English heading names the country and the main CPV label in English, then the buyer's own title, often in the buyer's language. An English keyword matches the label and English titles, not a German title. Codes work across languages.
- **Notices without codes.** Many Find a Tender award releases list no CPV code, so a CPV filter misses them. Add a keyword to find them.
- **Codes the buyer chose.** Each buyer picks its own codes, and a notice for IT support may carry only a building services code.
- **Values.** `minValue` compares the stated value in the notice's own currency, without conversion. Most open tenders state no value and are kept.
- **CanadaBuys types.** A CanadaBuys notice without a notice type counts as `open`. Notices without a publication date are left out and counted in `filteredOut` as `noDate`.

#### How fresh are the notices?

TED's notices carry the date of the next weekday and appear in its search the evening before: a run at 22:21 UTC on 15 Oct 2026 delivered notices dated 16 Oct. Find a Tender publishes through the day, and a run reads its notices up to the time the run starts. SAM.gov writes its extract once a day (the 15 Oct 2026 file was written at 03:30 UTC), so a SAM.gov notice reaches you up to a day after it was posted. CanadaBuys writes its file of open tenders once a day and its file of new tenders through the day, and the Actor reads both. Where the result can be wrong:

- **SAM.gov notices that close fast.** The extract lists active notices, so a notice posted and closed between two files is never in it.
- **Time zones.** SAM.gov gives its posting time without a time zone, and CanadaBuys and TED give dates, so the window for those sources starts on a whole day (UTC).

#### Is personal data involved?

The Actor takes no contact names, emails or phone numbers from any source, and it searches descriptions without copying them, because they often name a contracting officer. Buyer names are organisations. A title or a buyer name is published as the buyer wrote it, and a few can name a person, such as a sole trader. If a row holds personal data it should not, open an issue on the **Issues** tab with the notice ID, and the field will be fixed in the Actor.

#### How does the Actor remember what it delivered, and how do I start over?

After each run it keeps, per source, where its next window starts, the SAM.gov extract's Last-Modified, and the notice IDs it delivered with the day of delivery (the last 60 days, at most 100,000 per source). They are kept in a named key-value store listed in each row as `stateStore`. No notice text is stored there. To start over, delete that store under **Storage** in Console, or set a new `stateStoreName`.

#### What happens when a source fails or limits the requests?

Each source is read on its own, one request at a time, with a User-Agent that names the Actor. A server error or a network error is retried three times, after 2, 4 and 8 seconds. Find a Tender's "too many requests" answer (HTTP 429) is waited out for as long as its Retry-After header says, up to 2 minutes, and three in a row stop it for the run. TED is read at most 500 notices per 6 minutes, below its fair-use limit of 600, and a limit answer stops TED for the run at once. A page that does not answer within 60 seconds, or a download that sends nothing for 90 seconds, is a `timeout`. In every case the source's summary row says what happened, its window is kept so the next run reads those days again, and the other sources deliver as usual. The run fails only when every source failed and nothing was delivered.

#### What happens when a run reaches its time limit?

The Actor reads the time limit of the run from the platform and starts no new work in the last 45 seconds. What it did not finish gets the code `run-time-limit` in its summary row, and the next run reads that window again.

#### What if something looks wrong?

Open the Issues tab on the Actor page and describe your input and what you expected. A run link helps. Issues are answered within two days.

### Other Actors by the same developer

- [News Monitor](https://apify.com/lone_rachis/news-mentions-watch): new news articles for the companies, people, keywords and GDELT themes you watch, from the GDELT Project's 15-minute news files, with only articles not delivered before.
- [New Scholarly Papers Watch](https://apify.com/lone_rachis/scholarly-papers-watch): new journal articles, preprints and other works for the keywords, ORCID iDs, journals and funders you watch, with retraction and correction notices for your DOIs.
- [SEC EDGAR Filing Monitor](https://apify.com/lone_rachis/sec-edgar-filing-monitor): new SEC filings for the tickers or CIKs you watch.
- [SEC Form D Feed](https://apify.com/lone_rachis/sec-form-d-raises-feed): new private fundraising rounds filed with the SEC on Form D, with amounts and industry.
- [SEC XBRL Fundamentals](https://apify.com/lone_rachis/sec-xbrl-fundamentals): revenue, EPS, cash flow and other reported figures per company and period, from SEC XBRL filings.
- [UK Company Check](https://apify.com/lone_rachis/uk-company-check): Companies House status, outstanding charges and risk flags for UK companies, or a list of newly registered ones.
- [US Trademark Status and Watch](https://apify.com/lone_rachis/us-trademark-status-watch): USPTO status and upcoming deadlines for US trademarks, and what changed since your last run for marks you watch.
- [Full Page Website Screenshot](https://apify.com/lone_rachis/full-page-website-screenshot): full-page PNG or JPEG screenshots, including pages that load content as you scroll.

# Actor input Schema

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

The official sources to read. ted: EU and EEA tenders from TED (Tenders Electronic Daily). uk_fts: UK Find a Tender. sam: US federal opportunities from the SAM.gov public extract (about 200 MB, streamed, skipped when unchanged). canadabuys: Canadian federal tenders. uk_cf: the UK's legacy Contracts Finder (lower-value UK notices), off by default.

## `noticeTypes` (type: `array`):

open: calls for tenders you can still bid on (the default). planning: prior information, market research, sources sought, requests for information. award: contract awards and award notices. update: changes, corrections, amendments and cancellations.

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

Up to 50 words or phrases. A notice matches when one appears as whole words in its title (and description, see Search keywords in), any case, accents ignored. End a word with \* to match its start: cyber\* finds cybersecurity. Leave empty to filter by codes only.

## `cpv` (type: `array`):

Up to 200 Common Procurement Vocabulary codes. A short code covers everything under it: 72 is all IT services, 7221 is software programming. Trailing zeros are ignored, so 72000000 works like 72.

## `naics` (type: `array`):

Up to 200 NAICS codes of 2 to 6 digits. 5415 covers 541511, 541512 and the rest of computer systems design.

## `unspsc` (type: `array`):

Up to 200 UNSPSC codes. 43 covers all IT equipment and software, 4323 covers software. Trailing 00 pairs are ignored.

## `match` (type: `string`):

any: a notice matches with one of your codes or one of your keywords (the default). all: it needs one of the codes of its source and one of the keywords. A source with neither a code of its scheme nor a keyword is skipped when you gave codes for other schemes.

## `keywordsIn` (type: `string`):

titleAndDescription (the default) or title only. Descriptions are searched but never copied into the rows.

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

Up to 50 words or phrases that drop a notice when they appear in the same text the keywords are searched in. Same rules as Keywords.

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

Up to 60 country codes of the buyer, such as DE, FR, IE (TED's three-letter codes such as DEU work too). Mostly for TED. A single-country source whose country is not in the list is skipped without a download: GB for uk_fts and uk_cf, US for sam, CA for canadabuys. Leave empty for all countries.

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

Drops notices whose stated value is below this amount, in the notice's own currency (no conversion). Notices without a stated value are kept, because most open tenders publish none.

## `since` (type: `string`):

lastRun (the default): each run reads from where the last complete run of this memory stopped, and the first run reads First run days back. Or a date such as 2026-10-01 to read from that day (UTC). Never more than 30 days back. Notices delivered before are never delivered again either way.

## `firstRunDays` (type: `integer`):

How many days back the first run of a memory reads, 1 to 30.

## `maxNotices` (type: `integer`):

The most notices one run delivers and charges, all sources together, newest first and taking turns between sources. Notices left over are delivered by the next run.

## `stateStoreName` (type: `string`):

Optional. The named key-value store that remembers what was delivered. Leave empty: a saved task gets its own memory, and other runs share one memory per set of filters. Give a name (letters, digits and dashes) to share one memory between tasks or to start a fresh one.

## Actor input object example

```json
{
  "sources": [
    "ted",
    "sam"
  ],
  "noticeTypes": [
    "open"
  ],
  "keywords": [
    "software"
  ],
  "cpv": [
    "72"
  ],
  "naics": [
    "5415"
  ],
  "match": "any",
  "keywordsIn": "title",
  "since": "lastRun",
  "firstRunDays": 3,
  "maxNotices": 20
}
```

# Actor output Schema

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

Dataset with one row per delivered notice: source, notice ID and page, type, title, buyer and country, classification codes, value, deadline, publication time, place, procedure, document links, the keywords and codes that matched, and the licence line. Each source also gets a free summary row with what it read, matched and delivered.

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

Counts per source, the HTTP requests and bytes, the stop reason, the memory name and the run time.

# 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": [
        "ted",
        "sam"
    ],
    "noticeTypes": [
        "open"
    ],
    "keywords": [
        "software"
    ],
    "cpv": [
        "72"
    ],
    "naics": [
        "5415"
    ],
    "keywordsIn": "title",
    "firstRunDays": 3,
    "maxNotices": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("lone_rachis/public-tenders-feed").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": [
        "ted",
        "sam",
    ],
    "noticeTypes": ["open"],
    "keywords": ["software"],
    "cpv": ["72"],
    "naics": ["5415"],
    "keywordsIn": "title",
    "firstRunDays": 3,
    "maxNotices": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("lone_rachis/public-tenders-feed").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": [
    "ted",
    "sam"
  ],
  "noticeTypes": [
    "open"
  ],
  "keywords": [
    "software"
  ],
  "cpv": [
    "72"
  ],
  "naics": [
    "5415"
  ],
  "keywordsIn": "title",
  "firstRunDays": 3,
  "maxNotices": 20
}' |
apify call lone_rachis/public-tenders-feed --silent --output-dataset

```

## MCP server setup

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

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/8dfkk7aAOvnEJwTme/builds/8jgjXoFfPNVGfxRwe/openapi.json
