# TenderNed Tenders - Dutch Public Procurement Notices (`ryuuk/tenderned-tenders`) Actor

Get Dutch public procurement notices (aanbestedingen) from TenderNed, the official Dutch tender platform. Filter by date, CPV code, keywords, notice type, procedure and contract type. Clean, flat JSON with English field names and TED cross-references; contact details removed (GDPR). Pay per notice.

- **URL**: https://apify.com/ryuuk/tenderned-tenders.md
- **Developed by:** [Jishnu Harinandansingh](https://apify.com/ryuuk) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 notices

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

## TenderNed Tenders – Dutch Public Procurement Notices

Get Dutch public procurement notices from **TenderNed**, the official tender platform of the Dutch
government. You can filter by publication date, CPV code, keywords, notice type, procedure and
contract type.

Each notice becomes one flat JSON item with stable English field names, ready for spreadsheets, CRMs
or a daily tender-alert workflow. Where the meaning is the same, field names match our
**TED EU Tenders** Actor (also on the Apify Store), so the two feeds can be merged. European notices
include their TED publication number for de-duplication. Contact persons, e-mail addresses and phone
numbers are removed (GDPR).

### What it does

- Reads TenderNed's public JSON web service (TNS, no login). It does not scrape HTML.
- Covers every publication type on TenderNed: contract notices (open tenders), award notices, prior
  information notices, market consultations, corrections and contract modifications. It includes
  national (below EU threshold) and European procedures.
- Returns notices newest first. Each item has the notice ID and link, a PDF link, the tender ID
  shared by all publications of one tender, the TED notice number, title, buyer, CPV codes (main code
  first), publication date, deadline, procedure, notice and contract type, place of performance
  (NUTS), description, and the external tender platform where the documents are.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `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. For example `72000000` or `72` (IT services) also matches `72260000`. The check digit (`-5`) is optional. |
| `keywords` | list | `[]` | Free-text search, mostly in Dutch. A notice matches if it contains **any** keyword. A keyword with several words (`data platform`) is searched as an exact phrase. Max 20. |
| `noticeTypes` | list | `[]` (all) | `contract-notice`, `award-notice`, `prior-information`, `market-consultation`, `correction`, `modification`. |
| `procedureTypes` | list | `[]` (all) | `open`, `restricted`, `neg-w-call`, `neg-wo-call`, `comp-dial`, `innovation`, `comp-tend`, `oth-single`, `oth-mult`, `private` (national procedure below EU thresholds), `open-house`, `market-consultation`. |
| `contractTypes` | list | `[]` (all) | `works`, `supplies`, `services`. |
| `maxItems` | integer | `100` | Maximum number of notices returned (1–10,000). |

Example: open IT service tenders from the last 3 days:

```json
{
  "lastDays": 3,
  "cpvCodes": ["72000000"],
  "keywords": [],
  "noticeTypes": ["contract-notice"],
  "contractTypes": ["services"],
  "maxItems": 200
}
```

### Output

One item per publication (real example from 2026-10-31):

```json
{
  "noticeId": "447002",
  "noticeUrl": "https://www.tenderned.nl/aankondigingen/overzicht/447002",
  "pdfUrl": "https://www.tenderned.nl/papi/tenderned-rs-tns/v2/publicaties/447002/pdf",
  "tenderId": "621831",
  "tedNoticeId": null,
  "title": "Implementatiepartner Financiën, Inkoop en Projectadministratie (FIP)",
  "buyerName": "Gemeente Den Haag",
  "cpvCodes": ["72000000", "72200000"],
  "mainCpvCode": "72000000",
  "publicationDate": "2026-10-31",
  "deadline": "2027-02-08T10:00:00+01:00",
  "procedureType": "open",
  "noticeType": "contract-notice",
  "contractType": "services",
  "european": true,
  "placeOfPerformance": ["NL"],
  "description": "De gemeente Den Haag wil de komende jaren het huidige bedrijfsvoeringssysteem vervangen en vernieuwen voor de portefeuilles Financiën, Inkoop en Projectadministratie. ...",
  "documentsUrl": "https://s2c.mercell.com/today/211277",
  "tenderPlatform": "Mercell",
  "source": "TenderNed (https://www.tenderned.nl), CC0 1.0"
}
```

| Field | Notes |
|---|---|
| `noticeId` | TenderNed publication ID. It is unique and stable, so use it for de-duplication. Every correction or award is a new publication with its own ID. |
| `tenderId` | TenderNed "kenmerk", shared by all publications of the same tender (notice, corrections, award). |
| `tedNoticeId` | TED publication number (`123456-2026`) when the notice was also published on TED, otherwise `null`. It equals `noticeId` in the TED Actor. It can also be empty for European notices, e.g. when TenderNed does not have the TED number yet. |
| `title`, `buyerName`, `description` | Original language (almost always Dutch). E-mail addresses and phone numbers are redacted. |
| `cpvCodes`, `mainCpvCode` | 8-digit CPV codes without check digit, main code first. |
| `deadline` | Tender submission deadline, ISO 8601 with Dutch time offset (`+01:00` or `+02:00`). For market consultations it is the response date (date only). `null` when there is none, as with most award notices. |
| `procedureType` | Same codes as the TED Actor where the meaning is the same (`open`, `restricted`, `neg-w-call`, ...), plus `private`, `open-house`, `market-consultation` and `specific-contract` (call-off under a framework or DPS). Unknown TenderNed codes pass through unchanged. |
| `noticeType` | `contract-notice`, `award-notice`, `prior-information`, `market-consultation`, `correction`, `modification` or `early-termination`. These are TenderNed categories, not TED eForms codes. |
| `contractType` | `works`, `supplies` or `services`, as in the TED Actor. |
| `european` | `true` for procedures above EU thresholds, `false` for national ones. |
| `placeOfPerformance` | NUTS codes, e.g. `NL366` (Groot-Rijnmond), or `NL` for the whole country. |
| `documentsUrl`, `tenderPlatform` | External tender platform (e.g. Mercell) where the documents are, when the buyer uses one. Otherwise the documents are on the TenderNed notice page (`noticeUrl`). |
| `source` | Data source and license. |

There is no estimated contract value: TenderNed's public JSON service does not provide one.

### Pricing

Pay per result: **$0.002 per notice** (launch price). 1,000 notices cost $2. You only pay for
notices written to the dataset. Set `maxItems` or a maximum cost per run to cap spend.

### Limits and notes

- **Personal data:** the TenderNed JSON service has no contact-person fields, and the Actor never
  requests the full notice text, which is where contact names, e-mail addresses and phone numbers
  appear. Buyers sometimes paste an e-mail address or phone number into the description; these are
  replaced with `[email redacted]` and `[phone redacted]`. Personal names typed into 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 and rate limits:** TenderNed does not publish limits. The Actor sends at most one request
  per second: one per 100 search results, plus one per notice for the details (CPV codes, places,
  TED number). 100 notices take about 2 minutes.
- **Max 10,000 results per search:** TenderNed returns at most 100 pages of 100 results. For bigger
  backfills, run several date ranges.
- Keyword search uses TenderNed's own matching. Each keyword is a separate search and the results are
  merged, newest first.
- The date filter applies to the **publication** date, so corrections and awards for older tenders
  show up in recent date ranges.
- TenderNed may change its web service without notice. When the response format changes, the Actor
  fails with a clear error instead of returning an empty dataset.
- This is not an official TenderNed or Dutch government product.

### Data source and license

Data from [TenderNed](https://www.tenderned.nl), published as open data under
[CC0 1.0](https://data.overheid.nl/dataset/aankondigingen-van-overheidsopdrachten---tenderned).
No attribution is required; each item still names the source in the `source` field.

### Development

```bash
## from the repo root
pip install -r requirements-dev.txt
pytest -m "not live"          # unit tests on recorded fixtures
pytest -m live                # live smoke test against the real TenderNed service
cd actors/tenderned && PYTHONPATH=../.. apify run   # local run, input in storage/key_value_stores/default/INPUT.json
```

The Docker build context is the repo root (`dockerContextDir`), so the shared helpers in `shared/` are
included in the image. To re-record the fixture, run `python actors/tenderned/tests/record_fixture.py`
(e-mail addresses and phone numbers are scrubbed before saving).

# Actor input Schema

## `lastDays` (type: `integer`):

Used when 'Published from' is empty.

## `publishedFrom` (type: `string`):

Start date (YYYY-MM-DD). Overrides 'last N days'.

## `publishedTo` (type: `string`):

End date (YYYY-MM-DD), inclusive. Empty = up to today.

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

Only notices with a CPV code under these codes, e.g. 72000000 or 72 (IT services) also matches 72260000. Check digit (-5) is optional. Leave empty for all.

## `keywords` (type: `array`):

Free-text search (mostly Dutch, e.g. 'schoonmaak'). A notice matches if it contains any keyword. A keyword with several words is searched as an exact phrase. Max 20.

## `noticeTypes` (type: `array`):

Only these notice types. Leave empty for all.

## `procedureTypes` (type: `array`):

Only these procedures. Leave empty for all.

## `contractTypes` (type: `array`):

Only works, supplies and/or services. Leave empty for all.

## `maxItems` (type: `integer`):

Maximum number of notices to return (newest first). You pay per notice. TenderNed serves at most 10,000 results per search.

## Actor input object example

```json
{
  "lastDays": 7,
  "maxItems": 100
}
```

# Actor output Schema

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

One flat JSON item per notice in the default dataset.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("ryuuk/tenderned-tenders").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("ryuuk/tenderned-tenders").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 '{}' |
apify call ryuuk/tenderned-tenders --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ryuuk/tenderned-tenders"
        }
    }
}
```

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/B1j4avgJaGGg0Pzbc/builds/bspn48MfWpdck4yOL/openapi.json
