EU Public Tenders Scraper — TED + national bulletins
Under maintenancePricing
from $6.00 / 1,000 unique tender returneds
EU Public Tenders Scraper — TED + national bulletins
Under maintenanceScrape EU public tenders (appels d offres, marches publics, Ausschreibungen) from TED plus national bulletins like BOAMP, which carry below-threshold contracts TED never publishes. Lot-level CPV codes and deadlines from eForms. Deduplicated: ~28% appear twice, you pay once.
Pricing
from $6.00 / 1,000 unique tender returneds
Rating
0.0
(0)
Developer
Baptiste Mairesse
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
4 days ago
Last modified
Categories
Share
Public procurement notices from across the EU and EEA in one clean, deduplicated JSON feed — with lot-level CPV codes, deadlines and buyers extracted from the underlying eForms/UBL documents.
No API key. No login. No XML parsing on your side.
Why this one
1. Lot-level detail, because that is what you bid on
A tender is published as one notice, but it is awarded lot by lot. Each lot has its own CPV code, its own deadline and its own award criteria.
Every other procurement scraper consumes TED's search API, which returns one flat record per notice with no lots at all. This actor parses the eForms/UBL document behind the notice.
| Search API (what others use) | eForms/UBL (what this actor reads) | |
|---|---|---|
| Lots | absent | one entry per lot |
| CPV | one flat, duplicated array | one code per lot |
| Deadlines | one flat, duplicated array | one deadline per lot |
| Contract nature | absent | works / supplies / services |
| Buyer address | name only | city, postal code, NUTS region |
Real example from a live run: a French roadworks notice returned 29 lots, each with its own CPV code. The search API returns that as a single row.
Lots are included at no extra charge — billing is per notice, never per row.
2. Deduplicated across sources
Every contract above EU thresholds is published twice: once in its national bulletin, once on TED. Watch both and you read everything twice; watch one and you miss contracts.
Measured over a real 7-day window, all French tender notices from both sources:
BOAMP 1 656 noticesTED 1 338 notices──────────────────────collected 2 994unique after dedup 2 166duplicates removed 828 ← 27.7%
Around 28% of notices are duplicates. You are charged once per unique contract.
3. Below-threshold contracts TED never carries
TED only publishes contracts above EU thresholds. National bulletins carry the rest — and by count, that is the majority. France is covered today through BOAMP; the same eForms parser applies to other national portals.
Coverage
| Source | Scope |
|---|---|
| TED | All EU member states + EEA. Filter by country, or leave empty for everything. |
| BOAMP (France) | French national bulletin, including below-EU-threshold contracts. Added automatically when France is in scope. |
Output
One record per contract, deduplicated:
{"id": "boamp:26-72006","title": "Travaux de construction et de réparation de branchements","noticeType": "tender","country": "FRA","buyer": {"name": "MINISTERE DE LA CULTURE","city": "Paris","postalCode": "75001","nutsCode": "FR101"},"cpv": {"main": "45232410","all": ["45232410", "45231300"],"label": "Travaux de construction"},"contractNature": "works","procedureType": "OUVERT","departments": ["75"],"deadline": "2026-08-28T13:00:00.000Z","publishedAt": "2026-07-17T00:00:00.000Z","lots": [{ "id": "LOT-0001", "title": "Branchements secteur nord", "cpv": "45232410", "deadline": "2026-08-28T13:00:00.000Z" }],"isMultiSource": true,"urls": {"boamp": "https://www.boamp.fr/pages/avis/?q=idweb:26-72006","ted": "https://ted.europa.eu/fr/notice/502512-2026"}}
isMultiSource: true means the contract was found in both a national bulletin and TED, and the two records were merged into this one. urls links to the original notice on each platform.
All dates are ISO 8601 in UTC, so they are directly comparable. The sources disagree — one publishes local time, the other UTC — and that is normalized here.
Input
Everything is optional. Run it with no input to get the last 7 days of open tenders across the EU.
| Field | What it does |
|---|---|
countries | FRA, FR, France, DEU, Germany… Leave empty for the whole EU + EEA. |
publishedSince / publishedUntil | Date window. Defaults to the last 7 days. |
noticeTypes | tender (open calls), award (who won), correction, … Defaults to tender. |
onlyOpenTenders | Drop notices whose deadline has passed. On by default. |
cpvCodes | CPV code or prefix. 45 = construction, 72 = IT, 85 = health, 90 = waste. |
departments | French department codes: 75, 13, 974. France only. |
keywords | Free-text terms matched against the notice subject. |
maxItems | Hard cap on notices returned — and therefore on what you are charged. |
Example: construction tenders in Germany and the Netherlands
{ "countries": ["DEU", "NLD"], "cpvCodes": ["45"], "onlyOpenTenders": true }
Example: French IT contracts, national and EU sources combined
{ "countries": ["FRA"], "cpvCodes": ["72", "48"], "departments": ["75", "92", "93"] }
Example: who won waste-management contracts last quarter
{ "noticeTypes": ["award"], "cpvCodes": ["90"], "publishedSince": "2026-04-01", "onlyOpenTenders": false }
Typical uses
- Daily tender watch — schedule a morning run filtered to your CPV codes and countries. Pipe it to Slack, email or your CRM.
- Competitive intelligence — pull
awardnotices to see who wins what, for which buyer. - Market sizing — count contracts by CPV, country or region over a period.
- Lead generation — a buyer with an upcoming deadline in your sector is a prospect with a known budget and timeline.
How deduplication works
The sources share no common identifier, so notices are matched on buyer + deadline (to the day) + main CPV code, then confirmed by title similarity before merging.
Three rules keep it honest, because wrongly merging two contracts hides one from you — worse than showing a duplicate:
- Numbers veto. "Rehabilitation of 94 dwellings" and "…of 63 dwellings" are different contracts despite near-identical wording. If both titles carry numbers and none match, they never merge.
- One notice per source per group. Two distinct national notices are two distinct contracts. This stops a generic TED title from chaining unrelated notices together.
- Stricter matching without a deadline. When one source omits the deadline, titles must be substantially closer before anything merges.
Merging is deliberately conservative: you may occasionally see a duplicate, but you will not silently lose a tender.
Reliability
- Retries with exponential backoff on rate limits (TED throttles aggressively) and transient failures, honouring
Retry-After. - Requests are paced, not bursted.
- If one source fails, the other still returns. A TED outage does not cost you the national results — the run reports which source failed in
RUN_SUMMARY. - No silent truncation. If a window is too large to cover fully, the run says so instead of returning a partial result that looks complete.
Data and privacy
All sources are official open-data publications of public procurement notices. This actor:
- reads only public, unauthenticated endpoints — no login, no paywall, no protected pages;
- returns organisation data (public buyers, awarded companies), not personal profiles;
- stores nothing. Results go to the dataset of the run you launched, and nowhere else. No mirror database, no cache, no resale.
Sources: TED — Tenders Electronic Daily · BOAMP open data (DILA)
Development
npm installnpm test # 113 tests, real frozen fixturesnpm run fixtures # refresh fixtures from live APIsnode scripts/verify-live.js 300 # end-to-end run against live sourcesnode scripts/measure-overlap.js 10 7 # measure real cross-source overlap
Tests run against fixtures captured from the live APIs and frozen. The sources change; the normalization logic must not regress silently. The output schema is validated against real output in both directions — no undeclared field, no declared field the code never produces.