EU TED Tenders — Public Procurement Notice Scraper
Pricing
from $4.00 / 1,000 results
EU TED Tenders — Public Procurement Notice Scraper
Fetches structured public procurement notices from the official EU TED (Tenders Electronic Daily) API. Filter by country, CPV code, date, value, and notice type. Free, unauthenticated API; clean JSON output ready for CRM/dashboard ingestion.
Pricing
from $4.00 / 1,000 results
Rating
5.0
(1)
Developer
Berkan Kaplan
Maintained by CommunityActor stats
0
Bookmarked
42
Total users
12
Monthly active users
15 hours ago
Last modified
Categories
Share
EU Public Tenders — TED 📄
foXLabs procurement series: World Bank · IADB · Ukraine Prozorro · UK Contracts Finder · US federal awards · India
🎉 Turn TED into clean, structured EU public-tender data — no login, no API key, one row per notice, with the buyer, title, value, CPV code, deadline and country. Built for suppliers, bid/proposal teams and public-procurement market intelligence.
🔍 What is the EU Public Tenders — and when should you use it?
Give this actor keywords, buyer countries, CPV sectors, notice types and a date range, and it returns the matching notices from Tenders Electronic Daily (TED), the EU public-procurement journal (official open data) — as clean rows you can filter, export or feed to an AI agent. Every run queries the official TED search API live, so the data is as fresh as the journal itself.
Use it when you need: EU tender opportunities by sector or keyword; the notices that mention a public buyer; or a daily pipeline of new notices.
Use something else when: you need company firmographics — TED is procurement notices, not a company registry.
🤖 Use with AI agents
Already on the Apify MCP server? Ask for this Actor by name: foxlabs/ted-tenders.
Your agent can pay for its own runs. This Actor is pay-per-event with agentic payments, so an agent can discover it, run it and settle the bill over x402 (USDC on Base) or Skyfire — no Apify account or API token of its own. Billing is the same either way: per notice delivered.
Otherwise paste this into Claude, ChatGPT, Cursor or any MCP-enabled assistant:
I want to pull EU public tender notices using the Apify Actor `foxlabs/ted-tenders`.Input: `keywords` (words searched in the notice text, comma = alternatives), `countries` (ISO3 buyer countries, e.g. DEU),`cpvCodes` (CPV divisions), `datePreset` (last_24_hours, last_7_days, last_30_days, last_90_days, ytd) and `maxResults`.Start with: {"keywords":"software","countries":["DEU"],"datePreset":"last_30_days","maxResults":50}Ask me what to look for, run the Actor, then summarise the notices as a table.
The machine-readable API, MCP config and OpenAPI definition live at apify.com/foxlabs/ted-tenders.md.
📋 Overview
Everything you need to turn Tenders Electronic Daily (TED), the EU public-procurement journal (official open data) into clean, structured data — in one actor, with no login, cookies or API key.
Why teams pick this actor:
- ✅ Keyword search on the official feed — words are matched in the full notice text, combined with country, sector, notice-type and date filters.
- 🧹 No empty-promise columns — only fields TED actually fills.
- 🔗 Stable identifiers — every row carries TED's publication number and links, ready to join across runs.
- 💰 Pay only for results — per notice delivered; a search that finds nothing costs only the run start.
- 🤖 Agent-ready — MCP + x402 agentic payments.
✨ Features
- 🔎 Keywords — full-text search across the notice (
software,cloud services); commas separate alternatives. - 🌍 Filters — buyer country, CPV sector, notice type, date presets or a custom range (
20260501,2026-05-01or01.05.2026). - 🧠 Expert mode — paste a native TED expert query when you need more (
total-value>=1000000 AND classification-cpv=72000000). - 🌐 Language — titles, descriptions and buyer names in the language you pick, falling back to English.
- 🧹 Clean schema — camelCase rows, ready for CSV/Excel/JSON.
🎬 Quick Start
curl -X POST "https://api.apify.com/v2/acts/foxlabs~ted-tenders/runs?token=YOUR_TOKEN" \-H "Content-Type: application/json" \-d '{"keywords":"software","countries":["DEU"],"datePreset":"last_30_days","maxResults":50}'
🚀 Getting Started (3 steps)
- Choose your filters — keywords, buyer countries, CPV sectors and a date range.
- Set the cap —
maxResultslimits the rows (0 = no limit). - Run and export — get a clean dataset as JSON, CSV or Excel.
📥 Input
{"keywords":"software","countries":["DEU"],"datePreset":"last_30_days","maxResults":50}
| Field | Type | Description |
|---|---|---|
datePreset | string | last_24_hours, last_7_days (default), last_30_days, last_90_days, ytd or custom. |
publicationDateFrom / publicationDateTo | string | Custom range, used when datePreset is custom: 20260501, 2026-05-01 or 01.05.2026 (day first). |
keywords | string | Words to find in the notice text; commas separate alternatives, each entry is matched as an exact phrase. |
countries | array | Buyer countries as ISO3 codes (DEU, FRA, …). Empty = all. |
cpvCodes | array | CPV main divisions (exact match). Empty = all sectors. |
noticeTypes | array | Notice types to keep. Empty = all. |
language | string | Language for titles, descriptions and buyer names (default ENG). |
maxResults | integer | Most rows in one run (default 500, the form suggests 100; 0 = no limit). |
query | string | Power users: a native TED expert query; it replaces the filters above. Plain words here are searched like keywords. Two-letter countries (DE), CPV codes with a check digit (72000000-5) and the old TED syntax for country, CPV, date and full text (CY=[DE] AND PC=[72000000]) are rewritten automatically. Results come newest first unless the query has its own SORT BY. |
pageSize | integer | Notices per API call (max 250). |
📤 Output
One row per notice, saved to the dataset. Every row carries scrapedAt and links to the notice on TED.
{"publicationNumber": "650347-2026","publicationDate": "2026-09-22","title": "Germany – Health services – Videosprechstunde","noticeType": "cn-standard","procedureType": "open","buyerName": "AOK Bayern - Die Gesundheitskasse","buyerCountry": "DEU","buyerCity": "München","buyerEmail": "vergabestelle1@by.aok.de","totalValue": 2600500,"totalValueCurrency": "EUR","cpvCodes": ["85100000"],"deadlineDate": "2026-10-05","detailUrl": "https://ted.europa.eu/en/notice/-/detail/650347-2026","scrapedAt": "2026-09-29T19:35:39.878Z"}
| Field | Description |
|---|---|
publicationNumber | TED publication number |
publicationDate | Publication date |
title | Notice title |
noticeType | Notice type |
noticeSubtype | Notice subtype |
procedureType | Procedure type |
buyerName | Contracting buyer |
buyerCountry | Buyer country (ISO3) |
buyerCity | Buyer city |
buyerEmail | Buyer email, when published |
buyerPhone | Buyer phone, when published |
buyerUrl | Buyer website |
totalValue | Estimated or awarded value |
totalValueCurrency | Currency of the value |
cpvCodes | CPV codes |
deadlineDate | Tender deadline |
description | Notice description |
placeOfPerformanceCountry | Place of performance country |
placeOfPerformanceCity | Place of performance city |
detailUrl | Notice page on TED |
xmlUrl | Notice XML on TED |
💼 Use cases
1. Bid pipeline — find open EU tenders in your field.
Input: keywords and/or CPV sectors, last_24_hours. Output: new notices with value and deadline. Use: a daily bid feed.
2. Buyer intelligence — follow a public buyer. Input: the buyer's name as a keyword (matched in the full notice text, so notices that mention the buyer appear too). Output: those notices. Use: anticipate demand.
3. Market sizing — size public spend in a category. Input: CPV sectors and a longer date range. Output: notices with values. Use: estimate market size.
🔗 Integration
JavaScript / Node.js
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'YOUR_TOKEN' });const run = await client.actor('foxlabs/ted-tenders').call({"keywords":"software","countries":["DEU"],"datePreset":"last_30_days","maxResults":50});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items[0]);
Python
from apify_client import ApifyClientclient = ApifyClient('YOUR_TOKEN')run = client.actor('foxlabs/ted-tenders').call(run_input={"keywords":"software","countries":["DEU"],"datePreset":"last_30_days","maxResults":50})for item in client.dataset(run.default_dataset_id).iterate_items():print(item)
Written for apify-client 3 for Python, where call() returns a run object; with version 1 or 2 it returns a dict — write run['defaultDatasetId'] there.
Automation (n8n / Zapier / Make): schedule a daily run with datePreset: last_24_hours → handle the JSON dataset → push new notices to a sheet, CRM or chat.
📊 Pricing
Pay-per-event: per notice delivered to the dataset, plus Apify's small run-start event. A search that matches nothing costs only the run start. View current pricing.
❓ FAQ
Do I need an account, login or API key? No. This reads Tenders Electronic Daily (TED), the EU public-procurement journal (official open data).
What do I search by? Keywords, buyer countries, CPV sectors, notice types and dates — or a native TED expert query.
How current is the data? Every run queries TED live, so results are as fresh as the journal.
What does each row represent? One TED notice: the contracting buyer, title, value, CPV codes, deadline and country.
Can I export to CSV / Excel / JSON? Yes — directly from the Apify dataset.
🐛 Troubleshooting
- Fewer rows than expected — raise
maxResults, widen the date range or remove a filter. - No rows for a keyword — each comma-separated entry is an exact phrase; try a shorter word or the term in the notice's language.
- The run failed with "TED rejected the query" — the advanced field needs TED expert syntax (
field operator value); put plain words inkeywordsinstead. Country, CPV, date and full-text fields in the old syntax (CY=[DE],PC=[…],PD=[…],FT=[…]) are translated for you; other old fields such asTD=[3]stop the run with a message naming them — use the notice-type filter instead.
⚖️ Is it legal to scrape this data?
This actor reads public procurement notices published on TED as open data. Results can still contain personal data (e.g. a person’s name); personal data is protected by the GDPR and similar laws, so only process it with a legitimate basis. See Apify’s blog post on the legality of web scraping.
🤝 Support & contact
- 🌐 Website: data.foxlabs.com.tr
- 📧 Email: info@foxlabs.com.tr
- 🐛 Issues: open a ticket in the Actor’s Issues tab
- 🧰 More clean B2B data actors: Fox Labs on Apify
Changelog
0.6.13 — 2026-10-08 — README: the Python example works with the current Apify client
- The README's Python example read the results with
run['defaultDatasetId']. With the current Apify client for Python (version 3)call()returns a run object, not a dict, and that line raisedTypeError: 'Run' object is not subscriptable— after the run itself had finished. The example now readsrun.default_dataset_id; with client version 1 or 2, keeprun['defaultDatasetId']. Checked with apify-client 3.2.1, 2.5.1 and 1.12.2. - No code, output field or pricing change.
0.6.12 — 2026-10-07 — a run that Apify starts again does not write a notice twice
- A run that the platform starts again no longer repeats what it has written. Apify can move a run to another server while it is going; the Actor then starts again from the first page with the same dataset. Until now it wrote every page again: in a test with
maxResults1500 that was started again after 400 rows, the dataset ended with 1,900 rows, 171 notices twice. The Actor now first reads which notices its own dataset already holds; they count towardsmaxResultsand are not written or charged again. - No output field changed, no pricing change.
0.6.11 — 2026-10-02 — SORT BY … ASC no longer fails the run
- TED rejects the
ASCkeyword (SORT BY publication-date ASC→ HTTP 400 "extraneous input 'ASC'"); it sorts ascending without a keyword.ASC/ASCENDINGare now dropped andDESCENDINGbecomesDESCbefore the query is sent.
0.6.10 — 2026-10-02 — every run returns the newest notices first
- Result order changed: newest notices first. TED returns notices oldest first, so a form run used to start at the oldest day of the chosen window —
maxResults: 20on "last 7 days" gave the week's 20 oldest notices. Every run now asks TED forSORT BY publication-date DESC, as the TED website lists them, so a capped run keeps the most recent notices. An expert query with its ownSORT BYkeeps its order. Output fields and price are unchanged.
0.6.9 — 2026-10-02 — expert queries without a date return the newest notices first
- An expert query with no publication date returned the oldest notices first — TED's archive starts in October 2016, and those old-format notices carry no buyer e-mail, phone or deadline (measured:
CY=[DE] AND PC=[72000000]returned 20 notices from 2016-10-01…04). Such a query now asks TED for the newest notices first, as the TED website lists them. Queries that set a publication date or their ownSORT BY, and the form filters, keep their order.
0.6.8 — 2026-10-02 — common expert-query mistakes no longer fail the run
- Three mistakes in the advanced field used to fail the run with HTTP 400 (measured on the TED API): a two-letter country (
buyer-country=DE), a CPV code written with its check digit, as CPV codes are officially printed (classification-cpv=72000000-5), and the old TED expert syntax (CY=[DE] AND PC=[72000000]). They are now rewritten before the query is sent —DE→DEU(alsoEL→GRC,UK→GBR),72000000-5→72000000, and oldCY,PC,PD(dates and<>ranges) andFTfields into the current syntax. The log lists every rewrite. - An old-syntax field that cannot be translated (for example
TD=[3]) now stops the run with a message naming that field, instead of TED's generic syntax error. - No change to output fields, filters or price.
0.6.7 — 2026-09-29 — keyword search; plain words no longer fail the run
- New
keywordsfield. Words are searched in the full notice text (TED full-text search); commas separate alternatives. - Plain words in the advanced field used to fail the run. TED answers anything that is not expert syntax with an error, so a query like
softwareended as a failed run. Text with no field operator is now searched as keywords, together with the filters; a real expert-syntax error now says how to fix it. - Custom dates in more formats:
20260501,2026-05-01and01.05.2026are accepted (the form used to take only20260501). - README corrected against the real input. Every example used input keys this Actor does not have (
queries,maxResultsPerQuery, with a literalundefinedvalue), listed phantom fields (maxConcurrency,includeRaw) and described company-registry features ("name or registry-ID lookup", "legal form, formation date"). Examples, input and output now match the Actor. No price change.
0.6 — 2026-09-07
- Enabled AI-agent payments (x402) + rebuilt the README to the full standard (What-is / when, AI-agents + x402 agentic payments + MCP, Overview, Features, Use cases, Integration, FAQ, Troubleshooting, Support & contact).
0.0
- Initial release: EU procurement notices from Tenders Electronic Daily (TED), the EU public-procurement journal (official open data).