# Tenders Scraper: EU TED, UK Find a Tender & AusTender (`owlabee/tenders-scraper`) Actor

Scrape public tenders from the official EU TED, UK Find a Tender, UK Contracts Finder and AusTender APIs. Filter by keyword, country, CPV code and notice type; get normalized JSON with deadlines, values and buyers, only-new monitoring and webhooks. Pay per tender ($0.002).

- **URL**: https://apify.com/owlabee/tenders-scraper.md
- **Developed by:** [Owlabee](https://apify.com/owlabee) (community)
- **Categories:** Lead generation, News
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.60 / 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

## Tenders Scraper: EU TED, UK Find a Tender & AusTender

![Tenders Scraper: EU TED, UK Find a Tender & AusTender: banner with the main output fields](https://api.apify.com/v2/key-value-stores/r8pLvIribNPHYI9gX/records/tenders-scraper-banner.png)

### What it does

**Tenders Scraper** collects public tenders and contract award notices from four official open procurement APIs and returns them as one normalized JSON record shape:

- **EU TED** (Tenders Electronic Daily): above-threshold public procurement in the 27 EU countries, the EEA and partner countries, searched with TED's own full-text search.
- **UK Find a Tender**: UK above-threshold notices (Procurement Act 2023 and earlier regimes).
- **UK Contracts Finder**: UK central government and below-threshold notices, with the "suitable for SMEs" flag.
- **AusTender**: Australian Commonwealth contract notices (awards).

It is built for bid teams, procurement consultants and suppliers who sell to government and want a daily list of new calls for competition in their sector: filter by keyword, buyer country, CPV or UNSPSC code and notice type, run it on a schedule with **Only new or updated since last run**, and get only the notices you have not seen yet, by dataset, API, webhook or the Apify MCP server. Every record carries the deadline, estimated value and currency, the buyer, the CPV or UNSPSC classifications, lots, links to the notice, the procurement documents and the e-submission page, and the source's licence and attribution line. No API key, no login and no proxy are needed.

The record shape is the same as the opportunity records of [Grants.gov API & SAM.gov Scraper](https://apify.com/owlabee/grants-gov-api) (`opportunityId`, `title`, `agency`, `postedDate`, `closeDate`, `noticeType`, `classifications`, `estimatedValue`), so US and international results can be merged into one table.

### How to use

1. Enter one or more **Keywords** (or `*` for every notice in the window) and pick the **Sources**.
2. Optionally narrow by **Notice types**, **Buyer countries (TED)**, **CPV codes** or **UNSPSC codes (AusTender)**, and set **Published within (days)** and **Max results**.
3. Click **Start**. For alerts, save the input as a task, turn on **Only new or updated since last run**, and schedule it daily with a **Webhook URL** or an Apify integration (Slack, email, Google Sheets).

#### Quick start input

```json
{
  "keywords": ["software"],
  "sources": ["ted"],
  "noticeTypes": ["bid"],
  "postedWithinDays": 7,
  "maxResults": 20
}
```

#### More inputs

- IT services tenders in Germany and France: `{"keywords": ["*"], "sources": ["ted"], "countries": ["DE", "FR"], "cpvCodes": ["72000000"], "postedWithinDays": 3}`
- UK cleaning contracts, open tenders and awards: `{"keywords": ["cleaning"], "sources": ["findATender", "contractsFinder"], "noticeTypes": ["bid", "award"], "postedWithinDays": 14}`
- Australian Government consulting contracts awarded this week: `{"keywords": ["*"], "sources": ["ausTender"], "unspscCodes": ["80100000"], "postedWithinDays": 7}`

### Coverage

| Source | `sources` value | What it returns | Keyword search | Licence |
|---|---|---|---|---|
| EU TED | `ted` | Calls for competition, award and direct-award notices, prior information notices | TED full-text search, on the server | CC BY 4.0 (Commission Decision 2011/833/EU) |
| UK Find a Tender | `findATender` | Tender, award and planning notices (OCDS) | Matched by the Actor in the date window | Open Government Licence v3.0 |
| UK Contracts Finder | `contractsFinder` | Tender, award and pipeline notices (OCDS), SME suitability | Matched by the Actor in the date window | Open Government Licence v3.0 |
| AusTender | `ausTender` | Contract notices (awards) with value, dates and UNSPSC code | Matched by the Actor in the date window | CC BY 3.0 AU |

The UK and Australian APIs have no keyword parameter: the Actor reads their date window once per run and keeps notices whose title, description, buyer name or code descriptions contain every word of the keyword. AusTender's open approaches to market are not in its open API, so AusTender returns awarded contracts only. `countries` narrows TED; the UK and Australian sources are national.

### Input

| Field | Type | Required | Description |
|---|---|---|---|
| `keywords` | string\[] | yes | Search terms; `*` = every notice in the window. One search run per source and keyword |
| `sources` | string\[] | no | `ted`, `findATender`, `contractsFinder`, `ausTender` (default `ted`) |
| `noticeTypes` | string\[] | no | `bid`, `award`, `planning` (default `bid`) |
| `countries` | string\[] | no | Buyer countries for TED, two- or three-letter ISO codes |
| `cpvCodes` | string\[] | no | CPV codes or prefixes (TED and UK); `72000000` matches the whole 72 branch |
| `unspscCodes` | string\[] | no | UNSPSC codes or prefixes (AusTender) |
| `postedWithinDays` | integer | no | 1–90 (default 7) |
| `maxResults` | integer | no | Items per run across all keywords and sources (default 200) |
| `maxPagesPerSource` | integer | no | Pages per source (default 10, max 50) |
| `fetchDetails` | boolean | no | One more TED request per notice for lots, documents and submission links (default off) |
| `maxDetails` | integer | no | Cap on TED detail requests (default 50) |
| `omitContacts` | boolean | no | Leave out buyer contact persons (default **on**) |
| `includeIndividualRecipients` | boolean | no | Name award winners who read as private individuals (default off) |
| `onlyNewSinceLastRun` | boolean | no | Monitor mode: only new or changed notices (default off) |
| `stateStoreName` | string | no | Key-value store for the monitor snapshots (default `tenders-scraper-state`) |
| `webhookUrl` | string | no | POST target for the run summary and up to 500 notices |

### Output example

![Sample output of Tenders Scraper: EU TED, UK Find a Tender & AusTender as a table: the dataset overview columns from the quick-start run](https://api.apify.com/v2/key-value-stores/r8pLvIribNPHYI9gX/records/tenders-scraper-output.png)

A TED call for competition with details fetched (description shortened):

```json
{
  "source": "ted",
  "recordType": "opportunity",
  "keyword": "*",
  "opportunityId": "ted:779189-2026",
  "noticeId": "779189-2026",
  "title": "Slovakia – Financial transaction processing and clearing-house services – Akceptácia platobných kariet prostredníctvom internetu",
  "titleOriginal": "Akceptácia platobných kariet prostredníctvom internetu",
  "agency": "Železničná spoločnosť Slovensko, a.s.",
  "status": "open",
  "postedDate": "2026-11-09",
  "closeDate": "2026-12-08",
  "deadline": "2026-12-08",
  "url": "https://ted.europa.eu/en/notice/-/detail/779189-2026",
  "fetchedAt": "2026-11-09T06:02:33.288Z",
  "jurisdiction": "Slovakia",
  "country": "SK",
  "buyerCountries": ["SK"],
  "noticeType": "bid",
  "type": "cn-standard",
  "procurementMethod": "open",
  "contractNature": ["services"],
  "description": "Predmetom zákazky je poskytovanie služby platobnej brány …",
  "estimatedValue": 2794882,
  "currency": "EUR",
  "awardedValue": null,
  "classifications": [{ "scheme": "CPV", "code": "66172000", "description": null }],
  "buyer": { "name": "Železničná spoločnosť Slovensko, a.s.", "id": null, "country": "SK", "city": "Bratislava", "website": null },
  "awardees": [],
  "placeOfPerformance": ["anyw"],
  "documentsUrl": "https://www.uvo.gov.sk/vyhladavanie/vyhladavanie-zakaziek/dokumenty/566107",
  "submissionUrl": "https://zssk.eranet.sk/#/tenderPublicDetails/85",
  "lots": [{ "lotId": "LOT-0001", "title": "Akceptácia platobných kariet prostredníctvom internetu", "estimatedValue": 2794882, "currency": "EUR", "deadline": "2026-12-08", "durationValue": 48, "durationUnit": "MONTH" }],
  "sourceLicense": "CC-BY-4.0 (Commission Decision 2011/833/EU)",
  "attribution": "© European Union, 1998–2026, data from TED (Decision 2011/833/EU)",
  "detailsFetched": true
}
```

`noticeType` is `bid`, `award`, `planning`, `intent-to-award` (TED direct-award transparency notices) or `other`; `type` keeps the source's own notice type. In monitor mode records also carry `isNew` and `isUpdated`. Error items look like `{ "source": "ted", "keyword": "...", "error": "TED_HTTP_503", "errorMessage": "...", "sourceLicense": "...", "attribution": "..." }` and are charged as one tender notice, because the work to find the problem was done. The run's `SUMMARY` record lists the charges, the sources that stopped at a page cap, the attribution lines and the request count.

### Pricing

Pay per event. You only pay for what the Actor produces; set a **maximum cost per run** in the run options to cap spend.

| Event | Price | Notes |
|---|---|---|
| Tender notice | $0.002 | Per dataset item, including error items |
| TED notice detail | $0.003 | Per TED detail request, only with **Fetch TED notice details** on |
| Search run | $0.005 | Per source and keyword searched, whatever it returns (notices, nothing, only already-seen notices, an error) |
| Actor start | $0.00005 | Apify platform event |

Store discounts: Silver pays $0.0018 / $0.0027 / $0.0045 and Gold $0.0016 / $0.0024 / $0.00425 for the three events.

**Example:** 1,000 TED notices for one keyword ≈ 1,000 × $0.002 + $0.005 = **$2.01**. A daily monitor of 3 keywords on TED that finds 20 new notices a day ≈ 3 × $0.005 + 20 × $0.002 = **$0.055 a day**. Platform compute and transfer are included in the event prices; the default run memory is 512 MB.

### Limits and fair use

- Requests are spaced at least 0.5 s apart per host and retried with backoff on 429 and 5xx; each request has a 20 s deadline including retries, and a source that produces nothing for 60 s is abandoned with a charged error item. Each source-and-keyword search has a 300 s time budget. After two failed keywords the rest of that source's keywords get an error item without new requests.
- TED reads up to **Max pages per source** pages of 100 notices per keyword (default 10). The shared UK and Australian windows read at most 3 Find a Tender pages, 5 Contracts Finder pages or 7 AusTender days per keyword in the run; the summary says when a cap was hit. Narrow the window or the codes to see more.
- Monitor mode keeps one snapshot per source and keyword (notice ids and fingerprints only) in your own key-value store. The first run is a baseline and emits and charges for everything it finds; later runs read at most 3 pages and stop at the first page already seen.
- Only the official public APIs are used: no login, no proxy, no browser. The User-Agent identifies the Actor with the contact `data@owlabee.com`.

### Related Actors

- [Grants.gov API & SAM.gov Scraper — Federal Grants Feed](https://apify.com/owlabee/grants-gov-api) — US federal grants, SAM.gov contract opportunities and state and local bids in the same record shape.
- [Grant Matching & RFP Bid/No-Bid Scorer (AI)](https://apify.com/owlabee/grant-matching) — score US grant and contract opportunities for bid/no-bid against your company profile.

### Use from AI agents (MCP)

Pin this Actor in the Apify MCP server with `https://mcp.apify.com?tools=owlabee/tenders-scraper`, or let the agent find it with `search-actors`. It is pay-per-event with limited permissions. Prompts that map onto the input:

1. "Find EU tenders for cybersecurity services published this week in Germany, Austria and the Netherlands, with deadlines and estimated values."
2. "Every morning, send my webhook the new UK Find a Tender and Contracts Finder notices for facilities management."
3. "Which Australian Government contracts for IT consulting were awarded in the last 14 days, to whom and for how much?"

### FAQ

**Does this need proxies or an API key?** No. All four sources are official open APIs that need no key or account. The Actor runs on Apify's standard infrastructure without proxies.

**How is cost controlled?** Every run is bounded by **Max results**, **Max pages per source**, **Max detail fetches** and the run's maximum total charge; the Actor stops cleanly when any is reached. Each search run is charged once, so the number of sources × keywords sets the fixed part of the cost.

**Why does a UK or Australian keyword cost a search run when nothing matches?** Those APIs have no keyword search, so the Actor still reads the date window to look. The search run pays for that reading.

**Why are buyer contact names missing?** Contact persons are personal data under the GDPR and UK GDPR, so **Omit buyer contact persons** is on by default. Turn it off to get the official contact exactly as the notice publishes it; you are then responsible for processing it lawfully. Supplier contacts are never included.

**Why is an award winner's name null?** When a winner's name reads as a private individual (a sole trader), it is withheld with `nameWithheld: true`; company winners are always named. **Include award winners who are individuals** turns this off if you have a lawful basis.

**Can I get only new tenders every day?** Yes: turn on **Only new or updated since last run** and schedule the task. Use a different **State store name** per schedule.

### Support

Questions, bugs or a source you'd like added? Open an issue on the Actor's **Issues** tab, or email jacob@owlabee.com.

### Changelog

- **0.1 (2026-11-09):** first version: EU TED, UK Find a Tender, UK Contracts Finder and AusTender; CPV and UNSPSC filters; TED notice details; monitor mode; webhooks.

### Legal

The records are public procurement notices as published by the sources. Keep the source's attribution with any redistribution; every record and the run summary carry it in `sourceLicense` and `attribution`:

- TED: "© European Union, 1998–2026, data from TED (Decision 2011/833/EU)", reusable under Commission Decision 2011/833/EU (CC BY 4.0).
- Find a Tender and Contracts Finder: "Contains public sector information licensed under the Open Government Licence v3.0."
- AusTender: "© Commonwealth of Australia, CC BY 3.0 AU."

owlabee imposes no further restriction on the data; the fee is for the run, not a licence on the records. Unofficial: not affiliated with or endorsed by the European Union, the Publications Office of the EU, the UK Cabinet Office, Crown Commercial Service or the Australian Department of Finance; the names identify the source of the records.

Buyer contact persons and award winners who are individuals are left out by default (see the FAQ). If you turn either on, you are the controller of that personal data and responsible for a lawful basis, including for any direct marketing. Not a consumer report. Not for employment, credit, insurance, housing or tenant-screening decisions.

# Actor input Schema

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

Search terms, one per line. Each keyword is searched in every selected source and charged as one search run per source. TED runs its own full-text search; Find a Tender, Contracts Finder and AusTender have no keyword search, so the Actor reads their date window once and keeps notices whose title, description, buyer name or code descriptions contain every word of the keyword. Use \* for every notice in the window. The prefilled value is the cheap example used by Apify's daily automated test.

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

Which official open APIs to read. None needs a key or a login. TED covers above-threshold public procurement in the EU, the EEA and partner countries; Find a Tender covers UK above-threshold notices (Procurement Act 2023); Contracts Finder covers UK central government and below-threshold notices; AusTender covers Australian Commonwealth contract notices (awards only: its open tenders are not in the API).

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

bid = calls for competition (open and recent tenders); award = contract award notices (TED also returns direct-award transparency notices here, as noticeType "intent-to-award"); planning = prior information and pipeline notices. Applies to TED, Find a Tender and Contracts Finder; AusTender always returns contract notices. Each extra UK notice type splits the UK page allowance between stages.

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

Optional ISO country codes of the buyer, e.g. \["DE", "FR"] (two- or three-letter). Narrows TED only; the UK and Australian sources are national. Empty = every country.

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

Optional CPV codes or prefixes, e.g. \["72000000", "48"]. Trailing zeros mean the whole branch: 72000000 matches every IT services code (72xxxxxx). TED filters on the server; the UK sources keep notices with at least one matching code. Empty = every code.

## `unspscCodes` (type: `array`):

Optional UNSPSC codes or prefixes for AusTender, e.g. \["81110000"] for computer services. Same prefix rule as CPV. Empty = every code.

## `postedWithinDays` (type: `integer`):

Only notices published in the last N days (1–90). Find a Tender filters on the notice's last update, AusTender on the contract notice's publication date.

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

Upper bound on dataset items across all keywords and sources per run (notices and error items). Use together with the run's maximum cost to control spend.

## `maxPagesPerSource` (type: `integer`):

Upper bound on result pages read per source: per keyword on TED (100 notices a page), per run on the shared UK and Australian windows (100 releases a page; one day a page on AusTender). The UK and Australian windows also stop at 3 pages (Contracts Finder 5, AusTender 7 days) per keyword in the run, so their reading stays covered by the search-run charge. The run summary lists sources that stopped at the cap.

## `fetchDetails` (type: `boolean`):

Make one more TED request per emitted TED notice to add its lots (title, description, estimated value, deadline, duration), the procurement documents link and the e-submission link (plus the buyer's telephone when contacts are included). Each detail is a separate paid event. The UK sources already return full notices.

## `maxDetails` (type: `integer`):

Cap on TED detail requests per run (only used when "Fetch TED notice details" is on).

## `omitContacts` (type: `boolean`):

On by default: buyer contact names, e-mail addresses and telephone numbers are personal data under the GDPR and UK GDPR, so they are left out. Turn it off to include the official buyer contact exactly as the notice publishes it (you are then the controller of that data). Supplier contacts are never included.

## `includeIndividualRecipients` (type: `boolean`):

Off by default: on award notices, a winner whose name reads as a private person (sole traders, named individuals) is returned with name and id set to null and nameWithheld true; company winners are always named. Turn it on only if you have a lawful basis to process those names.

## `onlyNewSinceLastRun` (type: `boolean`):

Monitor mode for schedules: remember what was seen per source and keyword in the state store and emit only notices that are new or whose key fields changed, flagged with isNew / isUpdated. The first run is a baseline and emits (and charges for) everything; later runs read at most 3 pages per source and stop at the first page already seen.

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

Named key-value store that holds the monitor snapshots (notice ids and fingerprints only). Use a different name per schedule if two schedules share keywords.

## `webhookUrl` (type: `string`):

If set, the Actor POSTs a JSON run summary plus the emitted notices (max 500) to this URL when it finishes. Failures are logged, never fatal.

## Actor input object example

```json
{
  "keywords": [
    "software"
  ],
  "sources": [
    "ted"
  ],
  "noticeTypes": [
    "bid"
  ],
  "countries": [],
  "cpvCodes": [],
  "unspscCodes": [],
  "postedWithinDays": 7,
  "maxResults": 20,
  "maxPagesPerSource": 10,
  "fetchDetails": false,
  "maxDetails": 50,
  "omitContacts": true,
  "includeIndividualRecipients": false,
  "onlyNewSinceLastRun": false,
  "stateStoreName": "tenders-scraper-state"
}
```

# Actor output Schema

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

One item per tender notice in the default dataset (source, country, noticeType, title, agency, postedDate, deadline, estimatedValue, currency, classifications, buyer, awardees, url, sourceLicense, attribution; error items carry error and errorMessage). Fields are described in dataset_schema.json.

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

SUMMARY record in the run's default key-value store: sources queried, records produced, new/updated counts, sources stopped at a page cap, charges, the attribution sentence of each source and webhook delivery status, written when the run finishes.

# 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"
    ],
    "sources": [
        "ted"
    ],
    "noticeTypes": [
        "bid"
    ],
    "postedWithinDays": 7,
    "maxResults": 20,
    "fetchDetails": false,
    "onlyNewSinceLastRun": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("owlabee/tenders-scraper").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"],
    "sources": ["ted"],
    "noticeTypes": ["bid"],
    "postedWithinDays": 7,
    "maxResults": 20,
    "fetchDetails": False,
    "onlyNewSinceLastRun": False,
}

# Run the Actor and wait for it to finish
run = client.actor("owlabee/tenders-scraper").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"
  ],
  "sources": [
    "ted"
  ],
  "noticeTypes": [
    "bid"
  ],
  "postedWithinDays": 7,
  "maxResults": 20,
  "fetchDetails": false,
  "onlyNewSinceLastRun": false
}' |
apify call owlabee/tenders-scraper --silent --output-dataset

```

## MCP server setup

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

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/P72COZm6yzIU9JJNN/builds/FqnTCEmifz9mNaZmQ/openapi.json
