NL Tender Radar - Dutch Tenders from TED + TenderNed, Only New
Pricing
from $2.00 / 1,000 notices
NL Tender Radar - Dutch Tenders from TED + TenderNed, Only New
All Dutch public tenders in one feed: TED (EU) and TenderNed (NL) combined and de-duplicated, with only notices you have not seen before. Filter by date, CPV code and keywords. Clean, flat JSON with English field names; contact details removed (GDPR). Pay per notice; optional AI summary per notice.
Pricing
from $2.00 / 1,000 notices
Rating
0.0
(0)
Developer
Jishnu Harinandansingh
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
4 days ago
Last modified
Categories
Security
Limited permissionsShare
NL Tender Radar – Dutch Tenders from TED + TenderNed, Only New
All Dutch public procurement notices in one feed. The Actor combines TED (Tenders Electronic Daily, the EU's official journal for tenders) and TenderNed (the Dutch national tender platform). A notice that is published in both appears once. With a daily schedule, each run returns only the notices you have not received before.
You can filter by publication date, CPV code and keywords. Each notice is one flat JSON item with stable English field names, ready for spreadsheets, CRMs or a tender-alert workflow. Contact persons, e-mail addresses and phone numbers are removed (GDPR).
What it does
- Reads the official TED Search API (buyers in the Netherlands by default) and TenderNed's public JSON web service. Both are used without a login. The Actor does not scrape HTML.
- Covers EU notices above the EU thresholds (on both platforms) and national Dutch notices below them (TenderNed only): contract notices, awards, prior information notices, market consultations, corrections and modifications.
- De-duplicates the two feeds on the TED publication number (see below).
- Only new: remembers which notices earlier runs returned and skips them (see below).
- Returns notices newest first (by publication date).
Input
| Field | Type | Default | Description |
|---|---|---|---|
sources | list | ["ted", "tenderned"] | Which feeds to include. |
lastDays | integer | 7 | Notices published in the last N days. Ignored when publishedFrom is set. |
publishedFrom | date YYYY-MM-DD | – | Start of the publication date range. |
publishedTo | date YYYY-MM-DD | – | End of the range (inclusive). Empty means up to today. |
cpvCodes | list | [] | CPV codes; sub-codes match too. 72000000 or 72 (IT services) also matches 72260000. At least 2 digits; the check digit (-5) is optional. |
keywords | list | [] | Free-text search. A notice matches if it contains any keyword. Dutch works best for TenderNed. Max 20. |
maxItems | integer | 100 | Maximum number of notices returned per run (1–10,000). |
onlyNew | boolean | true | Skip notices already returned by earlier runs that used the same stateStoreName. |
resetSeen | boolean | false | Forget all notices returned before. With onlyNew on, this run returns everything in the date range and becomes the new starting point. |
stateStoreName | string | nl-tender-radar-state | Name of the key-value store that remembers returned notices. Use one name per search profile (radar-it, radar-cleaning, ...) so profiles do not hide each other's notices. Letters, digits and hyphens. |
seenRetentionDays | integer | 90 | Remembered notices published more than N days ago are forgotten, so the store stays small. Notices inside the current date range are kept, up to a cap of 100,000 remembered ids (oldest dropped first). |
tedCountries | list | ["NL"] | Buyer countries for the TED feed (ISO codes, e.g. NL, BE). TenderNed always covers Dutch notices. |
enrich | boolean | false | Add AI fields to each notice: English summary, sector, key requirements. See AI enrichment. |
maxEnrichedNotices | integer | 10 | Only the first N notices of a run (newest first) are enriched (1–1,000). |
Example: a daily alert for new IT tenders:
{"lastDays": 3,"cpvCodes": ["72"],"maxItems": 500,"onlyNew": true,"stateStoreName": "radar-it"}
Schedule it once a day. lastDays: 3 gives some overlap, so a late or failed run misses
nothing, and onlyNew makes sure you still get each notice only once.
Output
One item per notice. Real example of a notice found in both feeds (published 2026-10-30):
{"noticeId": "754284-2026","sources": ["ted","tenderned"],"tedNoticeId": "754284-2026","tenderNedNoticeId": "446753","tenderId": "613963","noticeUrl": "https://ted.europa.eu/en/notice/-/detail/754284-2026","pdfUrl": "https://ted.europa.eu/en/notice/754284-2026/pdf","tedUrl": "https://ted.europa.eu/en/notice/-/detail/754284-2026","tenderNedUrl": "https://www.tenderned.nl/aankondigingen/overzicht/446753","title": "Integraal Maaien 2027-2030","titleEnglish": "Netherlands – Horticultural services – Integraal Maaien 2027-2030","buyerName": "Gemeente Krimpenerwaard","buyerCity": "STOLWIJK","buyerCountry": "NL","cpvCodes": ["77300000","45233229"],"mainCpvCode": "77300000","estimatedValue": null,"currency": null,"publicationDate": "2026-10-30","deadline": "2026-11-04T12:00:00+01:00","procedureType": "open","noticeType": "correction","contractType": "services","european": true,"placeOfPerformance": ["NL"],"description": "Maaien grasvelden Maaien bermen Maaien watergangen Verkeersmaatregelen Maaien grasvelden - 462.800 m2 Maaien bermen - 244.603 m2 Maaien watergangen - 134.149 m2 Verkeersmaatregelen","documentsUrl": "https://www.tenderned.nl/aankondigingen/overzicht/446753","tenderPlatform": null,"source": "TED - Tenders Electronic Daily (https://ted.europa.eu); TenderNed (https://www.tenderned.nl), CC0 1.0","attribution": "Contains data from TED - Tenders Electronic Daily (https://ted.europa.eu), (c) European Union, reused under Commission Decision 2011/833/EU and CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/). Changes: fields selected, reformatted and merged with TenderNed data, notice types and country codes mapped, contact details removed."}
| Field | Notes |
|---|---|
noticeId | The TED publication number (123456-2026) when the notice is on TED, otherwise the TenderNed publication ID (447002). Unique within the feed. |
sources | ["ted"], ["tenderned"] or ["ted", "tenderned"]: which feeds this notice came from. |
tedNoticeId, tenderNedNoticeId | The ID in each feed, or null. A TenderNed-only item can still have a tedNoticeId when its TED notice was outside this run (e.g. older or not yet indexed by TED). |
tenderId | TenderNed "kenmerk", shared by all publications of one tender (notice, corrections, award). TenderNed only. |
noticeUrl, pdfUrl | Notice page and PDF: on TED when the notice is on TED, otherwise on TenderNed. tedUrl and tenderNedUrl give both links when available. |
title, buyerName, description | Original language (mostly Dutch). E-mail addresses and phone numbers are redacted. titleEnglish (TED only) is TED's English summary title. |
buyerCity, buyerCountry | From TED; null for TenderNed-only notices. |
cpvCodes, mainCpvCode | 8-digit CPV codes without check digit. mainCpvCode comes from TenderNed. |
estimatedValue, currency | From TED (TenderNed does not publish values). Placeholder values of 0 or 1 are dropped. |
deadline | Tender submission deadline, ISO 8601 with time zone when known. null for most awards. |
procedureType | open, restricted, neg-w-call, neg-wo-call, comp-dial, innovation, ... (TED eForms codes; TenderNed values use the same codes), plus TenderNed's private, open-house, market-consultation, specific-contract. |
noticeType | contract-notice, award-notice, prior-information, market-consultation, correction, modification, early-termination. TED eForms codes are mapped to these (cn-* → contract-notice, can-* and veat → award-notice, pin-* → prior-information, pmc → market-consultation, can-modif → modification); unknown codes pass through unchanged. |
contractType | works, supplies or services. |
european | TenderNed: true for procedures above EU thresholds, false for national ones. null for TED-only notices. |
placeOfPerformance | NUTS codes (NL33) or country codes (NL). |
documentsUrl, tenderPlatform | Where the tender documents are, e.g. Mercell or Negometrix. |
source | Data source(s) and license. |
attribution | Required TED credit (CC BY 4.0) and list of changes, on every item with TED data; null for TenderNed-only items. |
How de-duplication works
EU notices are usually published on both platforms. Each TenderNed publication carries the
TED publication number of its TED copy (tedNoticeId). The Actor normalises both numbers
(trims spaces and leading zeros, so 00012345-2026 equals 12345-2026) and merges a TED notice
with the newest TenderNed publication that has its number.
Which fields win: for fields both feeds have (title, buyer, CPV codes, dates, deadline,
procedure, notice and contract type, place, description, documents link), TED wins when it
has a value. TED is the official EU publication with structured data, and TED-only notices then
look the same as merged ones. TenderNed fills the fields TED left empty (often the description)
and adds its own fields (tenderNedNoticeId, tenderId, mainCpvCode, european,
tenderPlatform, tenderNedUrl). noticeUrl and pdfUrl point to TED.
One exception: noticeType comes from TenderNed. TED reports a change notice under its
original type (cn-standard), while TenderNed marks it correction or early-termination
(74 of 365 merged notices in the live sample below).
Limits: a match is only possible when both copies are found in the same run. Each feed is
fetched newest first up to maxItems, so when maxItems is smaller than the number of
notices in the date range, the two feeds can cover different days and fewer duplicates are
merged (the log then shows a warning). For a daily schedule, set maxItems above the daily
volume (roughly 100 TED + 100 TenderNed notices per working day for NL). TED notices can
show up a day before or after TenderNed. The overlap in lastDays and the only-new memory
(which stores both IDs of a merged notice) cover most of this. A TenderNed notice delivered
before its TED number was known can come back once as a TED item.
On live data from 2026-10-26 to 2026-10-30 (all notices, NL buyers on TED): 468 TED and 482 TenderNed notices, 365 of them in both feeds, so 585 items instead of 950. All 365 TED numbers on TenderNed matched exactly; no padded or otherwise different IDs were seen. In 88 of the 365, the two feeds give a different publication date (TED's is used).
How "only new" works
- After each run, the Actor saves the IDs of every notice it returned (
ted:<number>and/ortenderned:<id>) in a named key-value store (stateStoreName). Named stores persist between runs. - The next run skips any notice whose TED or TenderNed ID is in that list, including a
TenderNed copy of a TED notice you already received. Already-seen TenderNed publications are
dropped before their details are downloaded, so repeat runs are fast. (A TenderNed copy of a
TED notice you received is only recognised after its details are downloaded; it is skipped
and does not count towards
maxItems.) - Only notices actually written to the dataset are remembered. If a run stops at
maxItemsor at your maximum charge, the rest come in the next run. - The list is pruned at the end of each run: IDs of notices published more than
seenRetentionDaysago are dropped, except notices inside the run's date range, and the list is capped at 100,000 IDs. onlyNew: falsereturns everything and leaves the list untouched.resetSeen: trueclears it.- Runs that share a store should not run at the same time; the last one to finish wins.
- If a source fails, the run fails before anything is written, and the list is unchanged.
- If the stored list cannot be read (e.g. the store holds something else), the run fails
instead of returning and charging everything again; set
resetSeenor use another name.
AI enrichment
With enrich: true, each notice gets four extra fields. Notices are enriched newest first, up
to maxEnrichedNotices per run.
| Field | What | How |
|---|---|---|
ai_summary | 2–3 sentences in English: what is bought, by which organisation, value and deadline when the notice states them. | Claude Haiku 5.5 (Anthropic) |
sector | One of ~20 English sector labels (e.g. IT & telecom, Construction & works), from the main CPV code. | Fixed lookup table, no AI |
key_requirements | Up to 5 items {type, requirement, quote}: selection criteria, certifications, turnover or experience thresholds, award criteria. type is selection_criterion, certification, turnover, experience, award_criterion or other. | Claude; every quote is checked to occur verbatim in the notice text, and items whose quote is not found are dropped |
ai_meta | model, generated_at, disclaimer, skipped, enrichment_truncated | – |
Example (real TED notice, 2026-11-02, fields added to the item above):
{"ai_summary": "Gemeente Deventer is tendering a multi-year contract for cleaning and inspecting the main sewer systems of Deventer, Olst-Wijhe and Raalte, split into three lots. Annual budgets are about €300,000 (Deventer), €50,000 (Olst-Wijhe) and €120,000 (Raalte), with ceiling amounts over four years of €1,800,000, €300,000 and €720,000 respectively. The submission deadline is 10 December 2026 at 14:30 (+01:00); the award is based on the lowest price.","sector": "Cleaning, waste & environment","key_requirements": [{"type": "award_criterion","requirement": "Award on the basis of the lowest price","quote": "Laagste prijs: De opdracht wordt gegund op grond van de laagste prijs."}],"ai_meta": {"model": "claude-haiku-5-5","generated_at": "2026-11-02T17:37:24+00:00","disclaimer": "AI-generated from the notice; may contain errors","skipped": null,"enrichment_truncated": false}}
Disclaimer: ai_summary and key_requirements are AI-generated from the notice and may
contain errors. Always check the original notice (noticeUrl) before you bid. The original
fields are never changed; for TED data the AI fields are derived content (CC BY 4.0, changes
marked by ai_meta).
Notices without AI fields. When a notice is not enriched, ai_summary and
key_requirements are null (sector is still set) and ai_meta.skipped says why:
run_cap: beyondmaxEnrichedNoticesfor this run.monthly_budget: the Actor's monthly AI budget is used up (see below).charge_limit: your maximum cost per run cannot cover the enrichment fee for this notice.llm_error: the AI call failed for this notice (after one retry) or returned unusable output.
You only pay the enrichment fee for notices that were actually enriched (skipped is null).
If every AI call of a run fails, the run is marked as failed (the base notices are still in
the dataset).
What is sent to the AI. Only these notice fields: title, description, lot titles and
descriptions, CPV codes, estimated value, deadline, selection and award criteria (lots and
criteria fetched from TED for TED notices) and the buyer organisation name. Never sent:
contact points, contact-person fields, addresses (other than what the description itself
says), links. E-mail addresses and phone numbers are removed
from all text before the call. Very long notices are cut to about 10,000 tokens
(ai_meta.enrichment_truncated: true). Personal names that buyers type into free text cannot
be detected reliably, so such a name can reach the AI provider (Anthropic, as processor; API
data is not used for training) and, rarely, the summary. The AI only returns
schema-constrained JSON and has no tools.
Cost guards. Besides your maxEnrichedNotices, the Actor keeps a monthly budget for its
own AI spend (€15 per calendar month, UTC). It records the actual cost of each call from the
token usage and checks the total before every call; once the budget is reached, enrichment
stops until the next month (skipped: "monthly_budget", warning in the log) while base notices
are still returned and charged at the base price. Each run logs its token usage and AI cost.
Speed: one AI call per enriched notice, about 2–6 seconds each, one at a time.
Pricing
Pay per result: $0.002 per notice (launch price, event notice). A notice found in both
feeds is one item and costs $0.002 once. 1,000 notices cost $2. You only pay for notices written
to the dataset; repeat runs with onlyNew only charge for new notices. Set maxItems or a
maximum cost per run to cap spend. When the maximum cost per run is lower than maxItems, the
Actor fetches only as many notices as it can charge for.
AI enrichment: $0.006 per enriched notice on top of $0.002 (event notice-enriched),
so $0.008 for an enriched notice. 100 enriched notices cost $0.80. Only notices that were
actually enriched are charged the extra fee. With a maximum cost per run, a notice is only
enriched while the remaining budget covers both fees.
Limits and notes
- Personal data: contact fields are never requested from either source. E-mail addresses
and phone numbers that buyers type into free text are replaced with
[email redacted]and[phone redacted]. Personal names in free text cannot be detected reliably, so they may remain. If you spot personal data in the output, report it in the Issues tab of this Actor and the redaction will be extended. - Speed: neither source publishes rate limits. The Actor sends at most one TED request per 1.5 s (250 notices each) and one TenderNed request per second, plus one TenderNed request per notice for details (CPV codes, places, TED number). 100 TenderNed notices take about 2 minutes. The two sources are fetched in parallel.
- Max 10,000 notices per run, and TenderNed returns at most 10,000 results per search. For bigger backfills, run several date ranges.
- Keywords and CPV codes are passed to each source's own search, so matching can differ slightly: TED searches all language versions, TenderNed searches a multi-word keyword as an exact phrase.
- The date filter applies to the publication date.
- TED and TenderNed may change their services. When a response format changes, the Actor fails with a clear error instead of returning an empty or partial dataset.
- This is not an official TED, EU or Dutch government product.
Data sources and licenses
- TED – ted.europa.eu, (c) European Union. Notices may be reused
for commercial and non-commercial purposes (Commission Decision 2011/833/EU, CC BY 4.0). Each
item with TED data names the source and the changes made in
attribution. - TenderNed – tenderned.nl, open data under CC0 1.0.