EU Public Tenders Scraper — TED + national bulletins avatar

EU Public Tenders Scraper — TED + national bulletins

Under maintenance

Pricing

from $6.00 / 1,000 unique tender returneds

Go to Apify Store
EU Public Tenders Scraper — TED + national bulletins

EU Public Tenders Scraper — TED + national bulletins

Under maintenance

Scrape 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

Baptiste Mairesse

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

4 days ago

Last modified

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)
Lotsabsentone entry per lot
CPVone flat, duplicated arrayone code per lot
Deadlinesone flat, duplicated arrayone deadline per lot
Contract natureabsentworks / supplies / services
Buyer addressname onlycity, 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 notices
TED 1 338 notices
──────────────────────
collected 2 994
unique after dedup 2 166
duplicates removed 82827.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

SourceScope
TEDAll 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.

FieldWhat it does
countriesFRA, FR, France, DEU, Germany… Leave empty for the whole EU + EEA.
publishedSince / publishedUntilDate window. Defaults to the last 7 days.
noticeTypestender (open calls), award (who won), correction, … Defaults to tender.
onlyOpenTendersDrop notices whose deadline has passed. On by default.
cpvCodesCPV code or prefix. 45 = construction, 72 = IT, 85 = health, 90 = waste.
departmentsFrench department codes: 75, 13, 974. France only.
keywordsFree-text terms matched against the notice subject.
maxItemsHard 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 award notices 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:

  1. 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.
  2. 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.
  3. 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 install
npm test # 113 tests, real frozen fixtures
npm run fixtures # refresh fixtures from live APIs
node scripts/verify-live.js 300 # end-to-end run against live sources
node 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.