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

Use this when you need public tenders from the EU's TED (Tenders Electronic Daily): contract notices and awards by keyword, country, CPV code and date, with buyer, deadline, estimated value and link. Official API, incremental mode for daily runs. Pay per result.

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

## Pricing

$5.00 / 1,000 tender (result)s

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?

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 Tenders (TED) Scraper

**EU Tenders (TED) Scraper** returns public procurement notices from TED (Tenders Electronic Daily, the official EU journal for public tenders) as clean JSON: contract notices, award notices and prior information notices filtered by keyword, buyer country, CPV code, notice type and publication date, with buyer, deadline, estimated value and the notice link. It uses the official TED Search API (no key, no HTML scraping), runs incrementally for daily schedules, and you **pay per result**.

### What you get

One item per notice, same field names as every Hexadata Actor:

| Field | Type | Description |
| --- | --- | --- |
| `id` | string | TED publication number, e.g. `745163-2026` (unique) |
| `title` | string | English title as published by TED: "Country – CPV description – buyer's title" |
| `originalTitle` | string | null | Title in the notice language when it differs |
| `language` | string | null | Original language (ISO 639-2: `deu`, `fra`, `spa`…) |
| `buyerName` | string | Contracting authority (organisation) |
| `buyerCity` | string | null | |
| `country` | string | null | Buyer country, ISO alpha-2 (`DE`, `FR`) |
| `buyerCountry` | string | null | Same, alpha-3 as stored by TED (`DEU`, `FRA`) |
| `noticeType` / `noticeTypeLabel` | string | eForms code (`cn-standard`, `can-standard`, `pin-only`…) and label ("Contract notice") |
| `formType` / `formTypeLabel` | string | null | `planning`, `competition`, `result`, `dir-awa-pre`, `cont-modif` |
| `procedureType` | string | null | `open`, `restricted`, `neg-w-call`, `comp-dial`… |
| `contractNature` | string | null | `services`, `supplies`, `works` |
| `cpvCodes` | string\[] | All CPV codes (8 digits), deduplicated |
| `mainCpv` | string | null | Main CPV of the procedure |
| `placeOfPerformance` | string\[] | NUTS / country codes |
| `publishedAt` | string | Publication date, `YYYY-MM-DD` |
| `deadline` | string | null | Earliest deadline for receipt of tenders, ISO 8601 with offset |
| `estimatedValue` / `currency` | number | null, string | null | Estimated value of the procedure (sum of lots when only lots carry one) |
| `awardedValue` / `awardedCurrency` | number | null, string | null | Awarded amount (award and modification notices only) |
| `winnerNames` | string\[] | Winning organisations (award notices) |
| `description` | string | null | Procedure description in the original language, up to 2,000 characters |
| `url` / `pdfUrl` | string | Notice page and PDF on ted.europa.eu |
| `source`, `scrapedAt` | string | Attribution and extraction timestamp |

With `includeDetails: true` you also get `lots[]` (id, title, description, value, deadline per lot), `submissionUrl`, `documentsUrl`, `buyerLegalType`, `frameworkAgreement`, `durationMonths` and `tendersReceived`. With `includeRaw: true` the original API object is attached as `raw`.

### Use cases

- **Bid managers and sales teams at B2B suppliers** (IT, construction, medical devices, consulting) get every new call for tenders in their CPV codes and countries each morning, with the deadline and estimated budget, instead of browsing TED by hand.
- **Procurement and tender consultants** build client alerts: `searchQueries` + `countries` + `onlyNewSinceLastRun` on a daily Schedule, pushed to e-mail, Slack or a CRM through an Apify integration.
- **Market-intelligence and competitive-intelligence analysts** pull contract award notices (`noticeTypes: "result"`) to see who wins which public contracts, at what value, in which country.
- **Public-sector researchers, journalists and NGOs** monitor spending by buyer, CPV category or procedure type with a reproducible, citable extraction from the official source.
- **AI agents and RAG pipelines** call the Actor as an MCP tool ("find open tenders for cloud hosting in the Netherlands closing after December 1") and reason over structured results instead of PDFs.

### Input

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `searchQueries` | string\[] | `[]` (prefill `["software"]`) | Full-text phrases, OR-ed. TED searches in the notice language; add translations to widen. |
| `countries` | string\[] | `[]` = all | Buyer countries as ISO alpha-2 (`DE`, `FR`, `ES`…); alpha-3 accepted. |
| `cpvCodes` | string\[] | `[]` | CPV codes, hierarchical: `72000000` or just `72` (IT services), `45` (construction). |
| `noticeTypes` | select | `all` | `competition` (calls for tenders), `result` (awards), `planning`, `direct-award`, `modification`. |
| `dateFrom` / `dateTo` | date | – | Publication date bounds (`YYYY-MM-DD`). With no other filter, the last 7 days are used. |
| `onlyNewSinceLastRun` | boolean | `false` | For schedules: only notices published since the previous run that were not delivered before (state in the named store `hexadata-eu-tenders-ted`). |
| `maxResults` | integer | `100` | Hard cap on results and cost. Newest first. |
| `includeDetails` | boolean | `false` | Per-lot data, submission/document URLs, duration, framework flag, tenders received. Same price. |
| `includeRaw` | boolean | `false` | Attach the original TED API notice. |
| `proxyConfiguration` | object | off | Not needed for TED (open API). |

Invalid input (unknown country code, malformed CPV or date) stops the run before the first request, so it costs nothing.

### Output example

Two real items from the runs saved in `tests/` (2026-10-27). A contract notice (`noticeTypes: "competition"`, `searchQueries: ["software"]`, `countries: ["DE"]`):

```json
{
  "id": "745163-2026",
  "title": "Germany – Software package and information systems – PolBln 423_26 EU Beschaffung Storagesystem",
  "originalTitle": "PolBln 423_26 EU Beschaffung Storagesystem",
  "language": "deu",
  "buyerName": "Polizei Berlin",
  "buyerCity": "Berlin",
  "country": "DE",
  "buyerCountry": "DEU",
  "noticeType": "cn-standard",
  "noticeTypeLabel": "Contract notice",
  "formType": "competition",
  "formTypeLabel": "Competition (call for tenders)",
  "procedureType": "open",
  "contractNature": "supplies",
  "cpvCodes": ["48000000"],
  "mainCpv": "48000000",
  "placeOfPerformance": ["DE300", "DEU"],
  "publishedAt": "2026-10-27",
  "deadline": "2026-11-26T07:00:00+01:00",
  "estimatedValue": 1680672.27,
  "currency": "EUR",
  "awardedValue": null,
  "awardedCurrency": null,
  "winnerNames": [],
  "description": "Lieferung, Installation und Inbetriebnahme von zwei baugleichen Speichersystemen einschließlich aller erforderlichen Lizenzen sowie die Erbringung von Wartungs- und Supportleistungen.",
  "url": "https://ted.europa.eu/en/notice/-/detail/745163-2026",
  "pdfUrl": "https://ted.europa.eu/en/notice/745163-2026/pdf",
  "source": "TED (Tenders Electronic Daily), Publications Office of the European Union",
  "scrapedAt": "2026-10-27T05:11:29.991Z"
}
```

A contract award notice with `includeDetails: true` (`cpvCodes: ["72"]`, `countries: ["FR", "ES"]`, `noticeTypes: "result"`):

```json
{
  "id": "742656-2026",
  "title": "France – Computer-related services – Acquisition d’une solution logicielle de gestion des essais cliniques pour les Pharmacies à Usage Intérieur et le Département d’Essais Cliniques de l’Agence Générale des Equipements et Produits de Santé",
  "language": "fra",
  "buyerName": "AGEPS DA",
  "buyerCity": "PARIS",
  "country": "FR",
  "noticeType": "can-standard",
  "noticeTypeLabel": "Contract award notice",
  "formType": "result",
  "procedureType": "open",
  "contractNature": "services",
  "cpvCodes": ["72500000", "72514200"],
  "mainCpv": "72500000",
  "publishedAt": "2026-10-27",
  "deadline": null,
  "estimatedValue": null,
  "awardedValue": 1959880,
  "awardedCurrency": "EUR",
  "winnerNames": ["LOGHOSP"],
  "url": "https://ted.europa.eu/en/notice/-/detail/742656-2026",
  "lots": [{ "id": "LOT-0001", "title": "Lot unique", "estimatedValue": null, "currency": null, "deadline": null }],
  "buyerLegalType": "body-pl-la",
  "frameworkAgreement": "fa-wo-rc",
  "tendersReceived": 4,
  "source": "TED (Tenders Electronic Daily), Publications Office of the European Union",
  "scrapedAt": "2026-10-27T05:12:32.933Z"
}
```

### Pricing & cost examples

**$5.00 per 1,000 tenders** (pay per event, charged only for items saved to the dataset; duplicates, invalid notices and failed runs are never billed). `maxResults` is a hard cost cap and Apify platform usage is included.

- A first look: `maxResults: 50` ≈ **$0.25**.
- A daily alert for IT tenders in 3 countries with `onlyNewSinceLastRun` (typically 20–60 new notices per business day) ≈ **$0.10–0.30 per day**, about $5 per month.
- A market study: every contract award notice in CPV 72 across the EU for one quarter (≈ 10,000 notices) ≈ **$50**.

### Use with AI agents (MCP)

Every Apify Actor is available as a tool through the [Apify MCP server](https://mcp.apify.com). Add this server to Claude, Cursor, VS Code or any MCP client (OAuth sign-in on first use, or an `Authorization: Bearer <APIFY_TOKEN>` header):

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=hexadatalabs/eu-tenders-ted-scraper"
    }
  }
}
```

Then ask, for example:

> "Which contract notices for road maintenance were published in Poland and Czechia this week, and which ones close before December 15?"

The agent calls the Actor with `{"searchQueries":["road maintenance"],"countries":["PL","CZ"],"noticeTypes":"competition","dateFrom":"2026-10-20"}` and reads `deadline`, `estimatedValue` and `url` from the results. The Actor description and every input field start with "Use this when…" so tool selection works without extra prompting.

#### Pay with USDC (x402, no account needed)

AI agents can pay for this Actor directly with USDC through Apify's x402 support, with no Apify account: the agent buys a prepaid token from Apify AGI with a [Coinbase Agentic Wallet](https://docs.cdp.coinbase.com/agentic-wallet/cli/welcome) (USDC on Base, minimum $1) and uses that token as a normal API token. This Actor runs with **limited permissions**, which is what makes it eligible. Current steps are in [Apify's x402 guide](https://docs.apify.com/integrations/x402):

```bash
npx -y awal auth login <email> && npx awal auth verify <code>   # one-time wallet setup; then fund it with USDC and a little ETH on Base
TOKEN=$(npx awal x402 pay 'https://agi.apify.com/protocols/x402/prepaid-tokens?amount=1&currency=usd' --max-amount 1000000 --json | jq -r .token)
curl -s -X POST "https://api.apify.com/v2/acts/hexadatalabs~eu-tenders-ted-scraper/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"searchQueries":["software"],"countries":["DE"],"noticeTypes":"competition","maxResults":50}'
```

Cost: **$1 USDC ≈ 200 tenders** at this Actor's price; the token balance is a hard spending cap, and the token expires 14 days after purchase.

### API & integrations

```bash
curl -X POST "https://api.apify.com/v2/acts/hexadatalabs~eu-tenders-ted-scraper/run-sync-get-dataset-items?token=<APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"searchQueries":["software"],"countries":["DE"],"noticeTypes":"competition","maxResults":50}'
```

```python
from apify_client import ApifyClient
client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("hexadatalabs/eu-tenders-ted-scraper").call(run_input={
    "cpvCodes": ["72"], "countries": ["FR", "ES"], "noticeTypes": "result", "dateFrom": "2026-10-01", "maxResults": 200,
})
for tender in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(tender["publishedAt"], tender["buyerName"], tender["awardedValue"], tender["winnerNames"], tender["url"])
```

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<APIFY_TOKEN>' });
const run = await client.actor('hexadatalabs/eu-tenders-ted-scraper').call({
    searchQueries: ['cloud'], countries: ['NL', 'BE'], onlyNewSinceLastRun: true, maxResults: 500,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.map((t) => `${t.deadline ?? 'no deadline'} · ${t.title}`).join('\n'));
```

**Schedules and webhooks:** create a Schedule in Apify Console (e.g. every weekday at 08:00 UTC; TED publishes on business days) with `onlyNewSinceLastRun: true`, then add a webhook or integration (e-mail, Slack, Google Sheets, Make, Zapier, HTTP) on the `ACTOR.RUN.SUCCEEDED` event. Each run delivers only the notices published since the previous one.

### Limitations & legal

- **Source:** the official TED Search API v3 (`api.ted.europa.eu`), which needs no key. The Actor sends one request per page of up to 250 notices, identifies itself with a User-Agent and waits between pages; no HTML is scraped.
- **Licence:** TED content may be reused under the European Commission's reuse policy (Decision 2011/833/EU, Creative Commons Attribution 4.0). Keep the attribution in `source` when you republish data: © European Union, https://ted.europa.eu. Hexadata is not affiliated with the Publications Office of the EU.
- **No personal data:** `buyerName` and `winnerNames` are organisations. TED's contact-person names, e-mails and phone numbers are never requested.
- **Coverage:** notices in TED's eForms format and the previous standard forms; TED publishes on business days, so a daily schedule sees new notices Monday to Friday. Corrigenda are returned as the latest version of a notice (`onlyLatestVersions`).
- **Full-text search** (`searchQueries`) matches the notice text in its own language; the English `title` is TED's translated heading, so an English keyword finds English notices and the many notices whose title contains the term (e.g. "software"). Add local-language terms to widen a search.
- **Result window:** TED serves up to 15,000 notices per query in page mode; above that the Actor switches to iteration mode automatically. Narrow by date or country for very large extractions.
- **Values** are returned as published by the buyer; `estimatedValue` may be absent for negotiated procedures, and `deadline` is the earliest lot deadline.
- If TED changes its API response and three consecutive pages come back without the mandatory fields, the run fails with `SITE_LAYOUT_CHANGED` and nothing is charged.

### Changelog

- **0.1.0** (2026-10-27): first release. Keyword, country, CPV, notice-type and date filters; incremental mode; lot details; dataset table view; MCP-ready descriptions.

### Support

Open an issue in the **Issues** tab of this Actor (or e-mail Hexadata through the Apify profile). Issues are answered within 24 hours and confirmed bugs are fixed in a `beta` build within 48 hours.

#### En español

**EU Tenders (TED) Scraper** extrae licitaciones públicas europeas del diario oficial TED (Tenders Electronic Daily) mediante su API oficial: anuncios de licitación, adjudicaciones y anuncios previos filtrados por palabra clave, país del comprador, código CPV, tipo de aviso y fecha. Devuelve comprador, plazo, valor estimado, adjudicatario y enlace en JSON limpio, con modo incremental (`onlyNewSinceLastRun`) para alertas diarias. Precio: **$5.00 por cada 1,000 resultados**; `maxResults` limita el costo. Sin datos personales: compradores y adjudicatarios son organizaciones.

# Actor input Schema

## `searchQueries` (type: `array`):

Full-text search over the whole notice, one phrase per line, e.g. "software", "cloud", "road maintenance". TED searches in the language of the notice, so an English word finds English notices plus titles that contain it; add translations to widen ("Software" is the same in German, "logiciel" in French). Leave empty to filter only by country / CPV / date.

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

ISO 3166-1 alpha-2 codes of the buyer's country, e.g. DE, FR, ES, IT, NL, PL (alpha-3 like DEU also works). Empty = all countries publishing on TED (EU, EEA and others).

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

Common Procurement Vocabulary codes, hierarchical: "72000000" = all IT services, "45" = all construction work, "33600000" = pharmaceuticals. A shorter prefix is padded with zeros. Find codes at https://ted.europa.eu/en/simap/cpv

## `noticeTypes` (type: `string`):

Use "competition" for open calls you can still bid on (contract notices), "result" for contract award notices with winner and amount, "planning" for prior information notices.

## `dateFrom` (type: `string`):

Only notices published on TED on or after this date (YYYY-MM-DD), e.g. 2026-10-01. Empty = no lower bound (when no other filter is set, the last 7 days are used).

## `dateTo` (type: `string`):

Only notices published on or before this date (YYYY-MM-DD). Empty = today.

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

Use this on a schedule: the Actor remembers when it last ran (shared store "hexadata-eu-tenders-ted" in your account) and returns only notices published since then that it has not delivered before. The first run saves everything that matches.

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

Stop after this many tenders. Newest notices come first. Above 15,000 the Actor switches to TED's iteration mode automatically.

## `includeDetails` (type: `boolean`):

Add per-lot data (lot title, description, value, deadline), submission and document URLs, contract duration, framework-agreement flag and number of tenders received. Same price per result; items are larger.

## `includeRaw` (type: `boolean`):

Attach the original TED API object under "raw" (all languages, all fields requested). Useful for debugging or custom parsing; makes items much bigger.

## `proxyConfiguration` (type: `object`):

TED is an open API that does not need a proxy; leave it off. Turn on Apify Proxy only if your runs are blocked from a specific region.

## Actor input object example

```json
{
  "searchQueries": [
    "software"
  ],
  "countries": [
    "DE"
  ],
  "noticeTypes": "all",
  "onlyNewSinceLastRun": false,
  "maxResults": 50,
  "includeDetails": false,
  "includeRaw": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

No description

# 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 = {
    "searchQueries": [
        "software"
    ],
    "countries": [
        "DE"
    ],
    "maxResults": 50,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("hexadatalabs/eu-tenders-ted-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 = {
    "searchQueries": ["software"],
    "countries": ["DE"],
    "maxResults": 50,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("hexadatalabs/eu-tenders-ted-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 '{
  "searchQueries": [
    "software"
  ],
  "countries": [
    "DE"
  ],
  "maxResults": 50,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call hexadatalabs/eu-tenders-ted-scraper --silent --output-dataset

```

## MCP server setup

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