# TED Tenders Scraper - EU Public Procurement (`dataio/ted-eu-tenders-procurement`) Actor

Scrape EU public tenders from TED (Tenders Electronic Daily): open notices with the days remaining to bid, buyer, CPV codes, estimated value and place of performance, or contract awards with the winner. Titles in a readable language. Filter by CPV, country, date.

- **URL**: https://apify.com/dataio/ted-eu-tenders-procurement.md
- **Developed by:** [Tom Awake](https://apify.com/dataio) (community)
- **Categories:** Lead generation, MCP servers
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

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

### What does TED Tenders Scraper do?

Open EU public procurement notices with **the days remaining to bid** —
buyer, country, CPV codes, place of performance and value.

**No login. No API key. No proxies.**

### The countdown, and a readable title

TED publishes a submission deadline on **100 %** of tender notices and never
publishes how long is left. That is the only number that matters to a
company deciding whether to respond.

It also returns the title in whichever language happens to be indexed
first — measured 13 September 2026, a German buyer with a Hungarian title.
This Actor resolves the title to **English** where available, falls back to
the buyer country's language, and records which language it used in
`titleLanguage`.

A real run, IT services closing soon:

| Tender | Country | Days left |
|---|---|---|
| Sweden – IT services: consulting, software development | SWE | **1** |
| France – Technical computer support services | FRA | **1** |
| Belgium – IT services: consulting, software development | BEL | **1** |

52 of 80 results closing within a fortnight.

### Why the deadline window matters

**TED cannot sort.** Every sort parameter returns an error, and its natural
order mixes deadlines in 2027 with framework agreements running to 2038.

So "give me the 500 most urgent tenders in Europe" is not a query TED can
answer — and an Actor that sorted an arbitrary sample would be quietly
lying. Instead, **Closing within (days)** bounds the window at the source.
Inside that window the result is **complete**, and ordering it by urgency
means something.

It defaults to 60 days. Widen it to look further out, narrow it to 14 for a
weekly bid review.

This also matters for cost: filtering deadlines server-side cut the notices
fetched and discarded from **1,954 to 1** on the same query.

### Output

| Field | Example |
|---|---|
| `title`, `titleLanguage` | France – Sewerage management services, `eng` |
| `buyerName`, `buyerCountry` | Stadt Essen, DEU |
| `submissionDeadline` | 2026-09-22T14:00:00+02:00 |
| **`daysUntilDeadline`** | **1** |
| `deadlinePassed` | false |
| `cpvMain`, `cpvCodes` | 72000000 |
| `contractNature` | services / supplies / works |
| `procedureType` | open, restricted, negotiated |
| `placeOfPerformance` | NUTS codes |
| `totalValueEur` | present on about 42 % of notices |
| `winnerName`, `winnerCountry` | on award notices |
| `publicationNumber`, `publicationDate` | 599726-2026 |
| `noticeUrl`, `xmlUrl` | the notice and its machine-readable form |

#### Which columns fill depends on `noticeType`

This is not a defect, and it is worth knowing before you open the
spreadsheet. A tender that is still open has no winner yet; a contract
that has been awarded no longer accepts bids. Measured 17 September 2026
on 200 rows of each type:

| Field | `open-tenders` | `awards` |
|---|---|---|
| `daysUntilDeadline`, `submissionDeadline` | **100%** | 0% |
| `winnerName`, `winnerCountry` | 0% | **88.5%** |
| `totalValueEur` | ~42% | **93%** |
| `buyerName`, `title`, `cpvCodes` | 100% | 100% |

Use `open-tenders` to find contracts you can still bid on, and `awards`
to find out who won what — including, on the award side, the declared
contract value. The run's status message says which case you are in.

### Input

```json
{
  "noticeType": "open-tenders",
  "cpvCodes": ["72000000"],
  "maxDaysUntilDeadline": 60,
  "maxItems": 500
}
```

| Field | Default | Notes |
|---|---|---|
| `noticeType` | `open-tenders` | Tenders, awards, prior information, social |
| `openOnly` | `true` | Drops closed deadlines, at the source |
| `maxDaysUntilDeadline` | `60` | The window that makes results complete |
| `cpvCodes` | `["72000000"]` | 45000000 construction, 33100000 medical |
| `countries` | — | Three-letter codes: `FRA`, `DEU`, `ESP` |
| `searchText` | — | See the caveat below |
| `maxItems` | `500` | One row per notice |
| `publishedAfter` | — | Only used when the deadline filter is off |

### Use cases

- **Bid pipeline** — every open tender in your CPV codes and countries,
  ordered by how soon it closes.
- **Market entry** — which public buyers purchase what you sell, and where.
- **Competitor tracking** — switch to award notices to see who wins.
- **Partner search** — foreign buyers and suppliers active in your sector.
- **Market sizing** — declared contract values by CPV and country.

### Running this as a scheduled watch

Tenders close. A notice you see three days late may already be out of
reach, which is why this Actor computes the days remaining rather than
just printing a deadline.

Set `publishedWithinDays` instead of `publishedAfter`, then schedule the
Actor. The window moves with each run.

```json
{
  "noticeType": "open-tenders",
  "publishedWithinDays": 7,
  "maxDaysUntilDeadline": 30,
  "cpvCodes": ["72000000"],
  "maxItems": 500
}
```

**Why not a fixed `publishedAfter` date?** A schedule replays the same
input every time. With a fixed date, every run returns the same notices
and charges you again for them. The rolling window returns only what
appeared since the last run.

***

### Limits, honestly

- **Free-text search is language-bound.** Notices are published in the
  buyer's own language, so a French keyword finds French notices and misses
  the German ones. Prefer CPV codes, which are language-neutral.
- **`totalValueEur` is present on about 42 % of tender notices.** Many
  buyers do not publish an estimate before bids.
- Award notices carry no submission deadline, so `daysUntilDeadline` is
  empty there by design.
- `winnerName` appears on roughly 90 % of award notices. Where several
  suppliers won different lots, all are listed together — TED does not say
  which lot each won, and this Actor does not guess.
- Deadlines reaching 2035 and beyond are real: long framework agreements.
  They are not errors, but they will dominate an unbounded window.
- Requests are paced out of courtesy to a free public service.
- Not affiliated with TED or the Publications Office of the European Union.

### How much does it cost?

You pay per tender notice returned: **$0.006 each**, that is $6.00 per 1,000. There is no start fee, and subscription plans pay less per tender notice.

The example input below asks for up to 500 tender notices, so it costs **$3.00 at most**.

If a run reaches the spending limit you set, the output stops at that limit and never goes past it. You are never charged for rows that were not delivered.

### Use TED Tenders Scraper as an API

Call it from your own code with the Apify client, here in Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("DataIO/ted-eu-tenders-procurement").call(run_input={
    'noticeType': 'open-tenders',
    'cpvCodes': ['72000000'],
    'maxDaysUntilDeadline': 60,
    'maxItems': 500,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
```

It also works from JavaScript, Make, Zapier, n8n, and from AI agents through the Apify MCP server.

### Other actors you might like

- [UK Contracts Finder Scraper](https://apify.com/DataIO/uk-public-contract-awards): Tenders & Awards
- [USAspending Scraper](https://apify.com/DataIO/usaspending-federal-awards): Federal Contract Awards
- [Federal Register Scraper](https://apify.com/DataIO/federal-register-comment-deadlines): Comment Deadlines

### FAQ

#### Is it legal to use this data?

The actor reads public data from its official source, without logging in and without bypassing any access control. What you do with the data, for example contacting people listed in it, is your responsibility under the laws that apply to you, such as GDPR in Europe.

#### Can I run it on a schedule?

Yes. See *Running this as a scheduled watch* above: use the rolling-window setting so each run returns only what is new.

#### Can AI agents use it?

Yes. It is available through the Apify MCP server, and every input field is described in its input schema, so an agent can call it directly.

# Actor input Schema

## `noticeType` (type: `string`):

Open tenders are the ones you can still bid on. Awards show who won a past contract.

## `openOnly` (type: `boolean`):

Drops notices whose submission deadline has gone. This is the actionable set.

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

EU procurement classification, for example 72000000 for IT services, 45000000 for construction, 33100000 for medical equipment. Prefixes match broadly.

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

Three-letter codes, for example \["FRA", "DEU", "ESP"].

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

One row per notice, sorted by the nearest deadline first.

## `maxDaysUntilDeadline` (type: `integer`):

The most important setting. TED cannot sort by deadline, so this window is what makes a result complete rather than an arbitrary sample: inside it you get every matching notice, ordered by urgency. Defaults to 60 days.

## `searchText` (type: `string`):

Searched across the notice. Note that notices are published in the buyer's own language, so a French keyword finds French notices.

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

Rolling window, for a scheduled watch: 7 returns notices published in the last seven days. Unlike a fixed date, this moves on its own, so a weekly schedule returns only what is new instead of re-charging you for the same rows. Overrides Published after.

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

YYYY-MM-DD. Defaults to 90 days ago — older notices have almost all closed.

## Actor input object example

```json
{
  "noticeType": "open-tenders",
  "openOnly": true,
  "cpvCodes": [
    "72000000"
  ],
  "maxItems": 500,
  "maxDaysUntilDeadline": 60,
  "publishedWithinDays": 7
}
```

# Actor output Schema

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

Every row produced by the run, in JSON.

# 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"
    ],
    "maxItems": 500,
    "maxDaysUntilDeadline": 60,
    "publishedWithinDays": 7
};

// Run the Actor and wait for it to finish
const run = await client.actor("dataio/ted-eu-tenders-procurement").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"],
    "maxItems": 500,
    "maxDaysUntilDeadline": 60,
    "publishedWithinDays": 7,
}

# Run the Actor and wait for it to finish
run = client.actor("dataio/ted-eu-tenders-procurement").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"
  ],
  "maxItems": 500,
  "maxDaysUntilDeadline": 60,
  "publishedWithinDays": 7
}' |
apify call dataio/ted-eu-tenders-procurement --silent --output-dataset

```

## MCP server setup

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

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/zs1IdGFYizAeygpzP/builds/CNnGXJ4sZIEK792uh/openapi.json
