# EU TED Tenders Scraper: Public Procurement Notices (`creatortsv/eu-ted-tenders-scraper`) Actor

\[$1.50-$2.50/1K, no usage fees] Export EU public tenders from the official TED Search API. Filter by keyword, CPV code, buyer country and publication date. Get buyer, submission deadline, value and notice links. Not affiliated with the EU Publications Office.

- **URL**: https://apify.com/creatortsv/eu-ted-tenders-scraper.md
- **Developed by:** [Vladimir Tsaplin](https://apify.com/creatortsv) (community)
- **Categories:** Business, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 results

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

## EU TED Tenders Scraper: Public Procurement Notices

Scrape EU tenders from TED, the official journal of EU public procurement, into a clean table. Bid teams and public-sector suppliers filter by keyword, CPV code, buyer country and date, and get the buyer, submission deadline, value and notice links. Sample record from a real run on 2026-10-31:

```json
{
  "publication_number": "744140-2026",
  "publication_date": "2026-10-27",
  "title": "Germany – IT services: consulting, software development, Internet and support – Rahmenvereinbarungen Programmier- und Beratungsleistungen zur Anwendungsentwicklung im Ressort des StMELF",
  "buyer_name": "Freistaat Bayern, vertreten durch das Bayer. StM f. Ernährung, Landwirtschaft, Forsten und Tourismus",
  "country_code": "DEU",
  "cpv_codes": [
    "72000000",
    "72211000",
    "72200000",
    "72243000",
    "72262000",
    "72263000"
  ],
  "submission_deadline": "2026-11-23T10:00:00+01:00",
  "estimated_value": null,
  "lots_estimated_value_sum": 46875000.0,
  "lots_estimated_value_currency": "EUR",
  "ted_url": "https://ted.europa.eu/en/notice/-/detail/744140-2026"
}
```

Not affiliated with the Publications Office of the European Union. Data source: the official TED Search API.

### What you get

| Field | Example | Notes |
|---|---|---|
| `title` | Germany – IT services: consulting, software development, Internet a... | In English where TED has it |
| `buyer_name` | Freistaat Bayern, vertreten durch das Bayer. StM f. Ernährung, Landwirtschaft, Forsten und Tourismus | Contracting authority |
| `submission_deadline` | 2026-11-23T10:00:00+01:00 | The next tender deadline still ahead when the run started, as the buyer published it. Multi-lot notices list all of them in `submission_deadlines` |
| `participation_request_deadline` | null | The next deadline for requests to participate, with its time, in restricted and negotiated procedures, or for expressions of interest on a prior information notice used as a call for competition |
| `cpv_codes` | 72000000, 72211000, 72200000, 72243000, 72262000, 72263000 | Classification codes |
| `country_code` | DEU | First buyer country. Joint procurements list all of them in `buyer_countries` |
| `estimated_value` | null | Value of the whole procedure, as published. Buyers sometimes enter a placeholder such as 1 |
| `lots_estimated_value_sum` | 46875000.0 | Sum of the lot estimates, only when the notice gives no procedure value. A buyer can repeat one figure on each lot, so the sum can overstate the value |
| `total_value` | null | Total value field of the notice, where it has one. On an award notice this is usually the awarded value |
| `ted_url` | Link | Notice page, with PDF and XML links in their own fields |

### How much does it cost?

You pay per notice in the output, with no usage fees.

| Apify plan | Price per 1,000 notices | 2,000 notices a month |
|---|---|---|
| Free | $2.50 | $5.00 |
| Starter (Bronze) | $2.00 | $4.00 |
| Scale (Silver) | $1.75 | $3.50 |
| Business (Gold) | $1.50 | $3.00 |

A run also carries Apify's small start fee of $0.00005 per run for each GB of memory. Set **Maximum notices** to cap a run.

### Set up daily monitoring in 3 steps

1. Enter your keyword, CPV codes and countries, and turn on **Only new notices**.
2. Save the input as a task.
3. Add a daily schedule to the task. Each run returns just the notices that earlier runs of this search have not delivered, so you do not pay twice for the same notice.

The list of delivered notices is kept in a storage named `eu-ted-tenders-seen-notices` in your own Apify account. It is saved after every 100 stored notices and at the end of the run, so a run that is aborted or crashes keeps what it delivered. There is one gap: a run stopped in the moment between storing a batch and saving the list repeats that batch in the next run, and you pay for those notices twice. That is 100 notices at most.

If the list cannot be read, the run fails rather than repeat old notices. If it cannot be saved, the run stops and fails, because the next one may repeat its notices. The list keeps the latest 20,000 notices of a search, so a search that delivers more than that can repeat older ones. Two runs of the same search at the same time can deliver the same notices.

### Sorting and open tenders

- **Sort by** newest publication, oldest publication or nearest submission deadline. The nearest deadline is the one in `submission_deadline`: for a multi-lot notice, its next lot deadline still ahead. Notices whose deadlines have all passed come after those, latest first, and notices with no tender deadline come last. This sort reads the whole result before it stores anything, so it takes longer, and a search that matches more than 15,000 notices is refused, because that is the most this Actor reads. Narrow the search or sort by publication date.
- **Open tenders only** is on by default and keeps notices with a deadline still ahead when the run starts, for tenders, requests to participate or expressions of interest. Deadlines are compared as moments, time zone included, so a deadline that passed earlier today does not count: those notices are not stored and not charged. Restricted and negotiated procedures often take requests first and set no tender deadline yet, so they show `participation_request_deadline` and a null `submission_deadline`. Turn the option off to include contract awards and other notices.
- **Deadlines are taken as published.** Some buyers enter 00:00, which is the start of that day: a notice with its only deadline at 2026-10-31T00:00:00+01:00 counts as closed for the whole of 31 October. Read the notice before you rule out a tender near midnight.

### Compared with the alternatives

TED is free, and its own tools may be all you need. Every option below uses the same public data: this Actor has no extra source. You pay for a ready table, the form filters and scheduled only-new runs.

| Option | Cost | Filters | What you get |
|---|---|---|---|
| TED website | Free | Search form | Web pages, with PDF and XML per notice |
| TED registered account | Free access, registration needed | Search profiles | Email alerts and personalised RSS feeds |
| TED RSS feeds | Free, no account | A feed per business sector or country. A hand-built feed URL can also carry an expert query | Title, link, publication date and notice type, and on request the buyer name, estimated value and tender deadline with its time, as text. A query feed tested on 2026-10-31 held only the latest daily issue: 277 items for CPV 72000000, all dated 30 October, and nothing for any earlier date. Buyer names came with 27 of those 277 items in an English feed |
| TED XML packages | Free, no sign-in | None: every notice of a day or a month | One archive per day or month, with XML to unpack and parse yourself |
| TED Search API | Free, no API key | Expert query language | JSON to parse, with code to write and maintain |
| This Actor | Per notice, as above | Keyword, CPV, country and date fields, or an expert query | One table as CSV, JSON or Excel with a column per field, past notices as well as new ones, sorting by deadline, up to 5,000 notices per run |

### Limits

- Up to 5,000 notices per run.
- The Actor reads at most the first 15,000 notices of a search, because it pages by number and TED stops that paging there. **Only new notices** on a bigger search can reach that point once it skips many delivered notices. The run then fails with `REJECTED_LATER_PAGE`, and the notices it stored stay.
- If the TED API fails, or the time limit ends before the result has been read, the run fails with its error code. It never fills in other notices. Notices stored before a failure stay in the dataset, so check the run status before you treat a result as complete.
- At the end of a search the Actor compares what it read with the total TED reports. If notices are missing, the run fails with `NOTICES_MISSING` instead of looking complete. That can happen when TED publishes new notices during a long run: run it again.
- A field stays null when TED's search data has no value for it, even if the notice PDF or XML shows one. Check the notice before you rely on a null.
- Old-format notices, nearly all published before 2024, hold less than eForms notices:
  - One time limit covers tenders and requests to participate. It goes in `submission_deadline` on an open procedure and in `participation_request_deadline` on any other.
  - It is the buyer's local time with no time zone, so it has no offset here, and sorting and **Open tenders only** read it as UTC, which can be a few hours out. TED writes 00:00 for a notice that gives no time, so only the date is kept, and it counts to the end of that day.
  - TED's date can differ from the notice text. Of 66 old-format notices checked on 2026-10-31, 2 had a later date than the notice, one of them 2031 for a tender whose notice says January 2021. Such a notice counts as open, so read it before you act on it.
  - A correction is a notice of its own, with `notice_type` corr and the changed date, so the first notice can show a date that has since moved.
  - `estimated_value` comes from the notice total on planning and competition notices. Award notices keep their value in `total_value` with a null estimate. `lots_estimated_value_sum` stays null, and so does a value published as a range.
  - `procedure_type` can be a one-character TED code, such as 4 for a negotiated procedure.
- A country code goes to TED as you enter it, once 2-letter codes are converted. If TED does not know the code, the run fails with `INVALID_INPUT` and no notice is charged.

### FAQ

**What is a CPV code?** The Common Procurement Vocabulary classifies what a tender buys: 72000000 is IT services, 45000000 is construction work. A search by a code also finds its narrower codes: in a run on 2026-10-31, 72000000 returned notices coded 72224000 and other 72 codes.

**Can I use this data commercially?** TED notices are published by the EU Publications Office, which allows reuse, including commercial reuse, under its reuse policy. Name the source when you publish the data, and do not use the TED logo.

**Can I write my own query?** Yes. Put a TED expert query expression in the keyword field. The Actor adds the date, country and CPV filters around it.

### Call it from your own code

```bash
curl -X POST "https://api.apify.com/v2/acts/creatortsv~eu-ted-tenders-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"cpvCodes": ["72000000"], "countries": ["DEU"], "sortBy": "deadline", "maxItems": 50}'
```

### Scope

**What's Included:** notices from the official TED Search API, filters by keyword, CPV, country and date, submission deadlines, value fields as published, sorting, only-new mode for schedules.

**What's Not Included:** tender documents, notices below the EU thresholds that are published only nationally, translations, bid advice and award predictions.

Keywords: ted tenders scraper, eu tenders api, public procurement data, cpv code search, tenders electronic daily, government contracts europe.

# Actor input Schema

## `searchQuery` (type: `string`):

Full-text search across the notice, for example 'cybersecurity'. It finds the words as written, so use the language of the notices, for example 'cybersécurité'. Advanced users can give a TED expert query expression instead: a field, an operator and a value, such as buyer-country IN (DEU).

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

8-digit Common Procurement Vocabulary codes, for example 72000000 for IT services.

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

ISO country codes of the buyer: 2 or 3 letters, for example DE or DEU. TED checks 3-letter codes itself: if it does not know one, the run fails and no notice is charged.

## `publishedWithinDays` (type: `integer`):

Moves with each run, which suits scheduled monitoring. Notices from before 2024 are mostly in the old TED format, with fewer fields filled: see Limits in the README.

## `publishedAfter` (type: `string`):

A fixed date, YYYY-MM-DD. Overrides the N days above. Notices from before 2024 are mostly in the old TED format, with fewer fields filled: see Limits in the README.

## `openTendersOnly` (type: `boolean`):

Keeps notices with a deadline still ahead when the run starts, for tenders, requests to participate or expressions of interest. A deadline that passed earlier today does not count. Turn it off to include award and other notices.

## `sortBy` (type: `string`):

Order of the results. Nearest deadline reads the whole result first and uses the next deadline still ahead. It is refused for a search that matches more than 15,000 notices.

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

Skips notices that earlier runs of the same search already delivered, so you do not pay for them twice. The list is kept in a storage named eu-ted-tenders-seen-notices in your account and saved after every 100 notices, so a run that is aborted can repeat at most its last 100 in the next run.

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

The run stops at this many notices (1 to 5000). This caps what you pay.

## Actor input object example

```json
{
  "cpvCodes": [
    "72000000"
  ],
  "countries": [
    "DEU"
  ],
  "publishedWithinDays": 14,
  "openTendersOnly": true,
  "sortBy": "newest",
  "onlyNew": false,
  "maxItems": 10
}
```

# Actor output Schema

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

Procurement notices

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

How the run ended: notices stored, the stop reason and any error code. A run that fails partway keeps the notices it already stored, and those are charged.

# 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 = {
    "cpvCodes": [
        "72000000"
    ],
    "countries": [
        "DEU"
    ],
    "publishedWithinDays": 14,
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("creatortsv/eu-ted-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 = {
    "cpvCodes": ["72000000"],
    "countries": ["DEU"],
    "publishedWithinDays": 14,
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("creatortsv/eu-ted-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 '{
  "cpvCodes": [
    "72000000"
  ],
  "countries": [
    "DEU"
  ],
  "publishedWithinDays": 14,
  "maxItems": 10
}' |
apify call creatortsv/eu-ted-tenders-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,creatortsv/eu-ted-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/jhRRLLrrZ3KapUw52/builds/elDgxSwaWbdrS01tC/openapi.json
