# EU TED Tenders Scraper (`devilscrapes/eu-ted-tenders-scraper`) Actor

Export EU TED (Tenders Electronic Daily) public procurement notices as flat, buyer-ready rows: buyer, title, CPV code, estimated value, deadline, and winner info. Keyless EU API — filter by CPV code, buyer country, notice type, and publication date.

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

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

<div align="center">
  <img src=".actor/icon.svg" width="160" alt="Devil Scrapes mark" />

## EU TED Tenders Scraper

**💰 $4.20 / 1 000 results**  ·  pay only for results  ·  no credit card to try

*We do the dirty work so your dataset stays clean.* 😈

Export EU TED (Tenders Electronic Daily) public procurement notices as flat, buyer-ready rows: buyer, title, CPV code, estimated value, deadline, and winner info. Keyless EU API — filter by CPV code, buyer country, notice type, and publication date.

</div>

***

### 🎯 What this scrapes

TED (Tenders Electronic Daily) is the EU-wide official journal for public procurement — every EU/EEA member state's contracting authorities publish notices there, from a Polish ministry's IT framework agreement to a German city's construction tender. TED's own v3 search API is keyless and public, but it returns deeply nested eForms-taxonomy JSON with 24-language multilingual fields (`{"eng": [...], "pol": [...]}`) that are painful to parse by hand. This Actor queries TED's public search API and flattens every notice into one buyer-ready row — buyer, title, CPV classification, estimated value, deadline, and award/winner info once a contract has been won. No API key, no login, no eForms-schema puzzle.

### 🔥 What we handle for you

- 🔁 **Retries with exponential backoff** on `408`/`429`/`503`/network errors — up to 5 attempts, `Retry-After` honoured.
- 🧱 **Per-notice fault isolation** — one malformed notice is skipped and logged, never crashes the whole run.
- 🌐 **Multilingual-first field extraction** — English preferred, falling back through 24 EU languages so a title/buyer name is never silently dropped.
- 🧊 **Clean, typed dataset rows** — Pydantic-validated, ISO-8601 timestamps, never the raw eForms JSON blob.
- 💰 **Pay-Per-Event pricing** — you only pay for rows that land in your dataset. No data, no charge.

### 💡 Use cases

- Bid-team pipeline — pull new EU-wide tenders matching a CPV code or country every day.
- Market intelligence — track which EU public bodies are buying what, and at what value, across all 27+ member states.
- Award tracking — monitor closed notices for winner names and countries on contracts you bid on.
- Lead generation — build a cross-border prospect list of buyers issuing contracts in your sector.

### ⚙️ How to use it

1. Click **Try for free** at the top of the page.
2. Fill in the input form — most fields have sensible defaults.
3. Click **Start**. Output streams into the run's dataset.
4. Export from **Storage → Dataset** as JSON, CSV, or Excel — or fetch via the API.

### 📥 Input

| Field | Type | Required | Default | Notes |
|---|---|:--:|---|---|
| `query` | `string` | no | '—' | Raw TED expert-query string (e.g. <code>classification-cpv=72000000 AND buyer-country=POL</code>). When set, used… |
| `cpvCodes` | `array` | no | \['72000000'] | CPV classification codes, OR-combined (e.g. <code>72000000</code> for IT services). Default is a confirmed-live,… |
| `buyerCountries` | `array` | no | \[] | Buyer country codes, OR-combined (ISO3, e.g. <code>POL</code>, <code>DEU</code>). Leave empty to match all EU/EEA… |
| `noticeTypes` | `array` | no | \[] | Notice types, OR-combined (e.g. <code>cn-standard</code>, <code>can-standard</code>). Leave empty to match all notice… |
| `publishedFrom` | `string` | no | '—' | Only include notices published on or after this date, e.g. <code>2026-01-01</code>. |
| `publishedTo` | `string` | no | '—' | Only include notices published on or before this date, e.g. <code>2026-08-01</code>. Must be on/after… |
| `maxResults` | `integer` | no | 20 | Hard cap on the number of notice rows returned by this run. |
| `useProxy` | `boolean` | no | False | Route requests through Apify Proxy. TED is EU public infrastructure with no observed anti-bot, so this defaults off —… |

#### Example input

```json
{
  "cpvCodes": [
    "72000000"
  ],
  "maxResults": 5,
  "useProxy": true
}
```

### 📤 Output

Every row is one dataset item.

| Field | Type | Notes |
|---|---|---|
| `publication_number` | `string` | TED publication number, e.g. `13-2026`. Always present. |
| `notice_id` | `['string', 'null']` | Stable per-notice UUID. |
| `title` | `string` | Notice title, multilingual-first (English preferred). |
| `notice_url` | `string` | Public TED notice URL, built from the publication number. |
| `notice_type` | `['string', 'null']` | TED notice type enum, e.g. `cn-standard` (contract notice), `can-standard` (contract award notice). |
| `form_type` | `['string', 'null']` | TED form type enum, e.g. `competition`, `result`. |
| `procedure_type` | `['string', 'null']` | Procurement procedure type, e.g. `open`, `neg-w-call`, `oth-single`. |
| `buyer_name` | `['string', 'null']` | Contracting authority / buyer name, multilingual-first. |
| `buyer_country` | `['string', 'null']` | Buyer country, ISO3 code (e.g. `POL`). |
| `buyer_city` | `['string', 'null']` | Buyer city, multilingual-first. |
| `place_of_performance_country` | `['string', 'null']` | Place-of-performance country, ISO3 code. |
| `cpv_codes` | `array` | Deduplicated CPV classification codes; empty list when none present. |
| `description` | `['string', 'null']` | Lot description, multilingual-first. |
| `estimated_value_amount` | `['number', 'null']` | Estimated value of the first lot, parsed as a float. |
| `estimated_value_currency` | `['string', 'null']` | Currency of estimated\_value\_amount. |
| `deadline_date` | `['string', 'null']` | Submission deadline of the first lot; null on award notices (no open deadline). |
| `publication_date` | `['string', 'null']` | Notice publication date, as returned by TED. |
| `winner_name` | `['string', 'null']` | Winning supplier name, multilingual-first; populated on award notices only. |
| `winner_country` | `['string', 'null']` | Winner country, ISO3 code; populated on award notices only. |
| `scraped_at` | `string` | ISO 8601 UTC timestamp when this row was scraped. |

#### Example output

```json
{}
```

### 💰 Pricing

Pay-Per-Event — you pay only when these events fire:

| Event | USD | What it is |
|---|---:|---|
| `actor-start` | $0.2 | One-off warm-up charge per run |
| `result-row` | $0.004 | Per unique dataset item |

Example: 1 000 results at the rates above ≈ **$4.20**. No subscription, no minimum, no card to start — Apify gives every new account $5 of free credit.

### 🚧 Limitations

- TED (EU-wide) only — national procurement portals are out of scope (see our UK Contracts Finder Actor for UK-specific coverage).
- No eForms XML/PDF document downloads, only the structured notice metadata.
- Real-time per-query only — no historical bulk export and no cross-run deduplication.

### ❓ FAQ

**Do I need a TED account or API key?**

No. TED's v3 search API is public and keyless — this Actor calls it directly.

**Why does the default CPV code return so many results?**

`72000000` (IT services) alone matches 396k+ live notices — confirmed on TED. `maxResults` defaults to 20 to keep a first run fast and cheap; raise it once you've confirmed the filter shape you want.

**What if a notice's title isn't in English?**

TED's multilingual fields aren't guaranteed to carry an `"eng"` key. This Actor falls back to the generic `"mul"` key, then the first available language, so `title`/`buyer_name`/`description`/`winner_name` are never silently dropped.

**Can I write my own TED expert query?**

Yes — set `query` to any raw TED expert-query string (e.g. `classification-cpv=72000000 AND buyer-country=POL`) and it's sent verbatim, overriding every structured filter.

### 💬 Your feedback

Spotted a bug, hit a weird edge case, or need a new field? Open an
issue on the Actor's **Issues** tab on Apify Console — we ship
fixes weekly and we read every report.

***

<div align="center">

Built by **[Devil Scrapes](https://apify.com/DevilScrapes)** 😈 — a small fleet of
opinionated public-data Actors. Honest pricing, real engineering, zero fine print.

</div>

# Actor input Schema

## `query` (type: `string`):

Raw TED expert-query string (e.g. <code>classification-cpv=72000000 AND buyer-country=POL</code>). When set, used verbatim and overrides every structured filter below. Power-user escape hatch.

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

CPV classification codes, OR-combined (e.g. <code>72000000</code> for IT services). Default is a confirmed-live, high-volume code so the prefilled demo returns real rows.

## `buyerCountries` (type: `array`):

Buyer country codes, OR-combined (ISO3, e.g. <code>POL</code>, <code>DEU</code>). Leave empty to match all EU/EEA countries.

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

Notice types, OR-combined (e.g. <code>cn-standard</code>, <code>can-standard</code>). Leave empty to match all notice types; unconfirmed values pass through and surface TED's own rejection rather than being silently dropped.

## `publishedFrom` (type: `string`):

Only include notices published on or after this date, e.g. <code>2026-01-01</code>.

## `publishedTo` (type: `string`):

Only include notices published on or before this date, e.g. <code>2026-08-01</code>. Must be on/after <code>publishedFrom</code> when both are set.

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

Hard cap on the number of notice rows returned by this run.

## `useProxy` (type: `boolean`):

Route requests through Apify Proxy. TED is EU public infrastructure with no observed anti-bot, so this defaults off — enable only if you see rate-limiting.

## Actor input object example

```json
{
  "query": "classification-cpv=72000000 AND buyer-country=POL",
  "cpvCodes": [
    "72000000"
  ],
  "buyerCountries": [],
  "noticeTypes": [],
  "publishedFrom": "2026-01-01",
  "publishedTo": "2026-08-01",
  "maxResults": 20,
  "useProxy": false
}
```

# Actor output Schema

## `datasetItems` (type: `string`):

All dataset items as JSON.

## `datasetItemsCsv` (type: `string`):

Same data exported to CSV.

## `datasetView` (type: `string`):

Open the run dataset in the Console.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("devilscrapes/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 = {}

# Run the Actor and wait for it to finish
run = client.actor("devilscrapes/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 '{}' |
apify call devilscrapes/eu-ted-tenders-scraper --silent --output-dataset

```

## MCP server setup

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