# Netherlands Insolvency Register - Bankruptcy Feed (`scrapersdelight/rechtspraak-insolvency-nl-scraper`) Actor

From $2 per 1,000 records, no start fee. The Dutch national insolvency register: every bankruptcy, suspension-of-payment and debt-restructuring publication in its rolling 30-day window - company name, KvK number, address, court, case number, and the court-appointed trustee with a phone number.

- **URL**: https://apify.com/scrapersdelight/rechtspraak-insolvency-nl-scraper.md
- **Developed by:** [Scrapers Delight](https://apify.com/scrapersdelight) (community)
- **Categories:** Lead generation, Business, News
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 1,000 per insolvency publication returneds

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

## 🇳🇱 Netherlands Insolvency Register — new bankruptcies, structured

**Every bankruptcy, suspension of payment and personal debt restructuring published by the Dutch
courts, from the judiciary's own register — with the KvK number, the business address, and the
phone number of the trustee the court put in charge of the estate.**

The Centraal Insolventieregister (`insolventies.rechtspraak.nl`) is where the eleven Dutch district
courts are legally required to publish insolvency decisions. It is the authoritative, same-day
record that a company has gone under. This Actor turns it into a table.

***

### 📊 The numbers, measured on 2026-09-16/17

Every figure below was counted against the live register on the day this Actor was built. Nothing
here is an estimate.

| | |
|---|---|
| Publications in the register's whole rolling window | **2,574** |
| Business days that window covers | **22** (2026-08-18 → 2026-09-16) |
| Publications per business day | **~117** |
| New bankruptcies declared in those 30 days | **653** |
| Full sweep, everything, at the default settings | **103 seconds** |
| Company rows a full sweep delivered | **859** |
| Detail requests that failed in that sweep | **0 of 2,574** |

#### Field fill — counted on all 859 company rows of a full live sweep

| Field | Fill | | Field | Fill |
|---|---|---|---|---|
| `subjectName` | **100%** | | `trusteeName` | **100%** |
| `kvkNumber` | **100%** | | `trusteePhone` | **95.7%** |
| `court` | **100%** | | `businessAddress` | **98.4%** |
| `caseNumber` | **100%** | | `businessCity` | **98.4%** |
| `caseType` | **100%** | | `postalAddress` | **93.6%** |
| `publicationDate` | **100%** | | `tradeNames` | **93.2%** |

A second, independent count over a random 300-record sample (companies *and* private individuals,
recounted by `offline_validate.mjs` on every run) agrees: `supervisionCaseNumber` 100%,
`supervisingJudge` 100%, `trusteeAddress` 100%, `hearingLocation` 23.3%, `previousCaseNumber` 10.0%,
`claimsDeadline` 6.0%.

"Filled" excludes `""`, `null`, `"N/A"`, `"-"` and `"Onbekend"`.

***

### ❌ What this register does NOT have — read this before you buy

Two columns you would expect on a lead list are **not here**, and no scraper can conjure them:

**There is no email address. None.** Not sparse — absent. A regex over 300 complete detail payloads
returned **zero** matches, a regex over a whole day's bulletin returned **zero**, and not one of the
register's 106 schema keys even contains the string "mail". This Actor does not ship an `email`
column, and you should not believe any tool that says it can get you one from this source.

**The phone number is the lawyer's, not the company's.** The register has a `telefoonnummer` field
on the insolvent party's address, and across **703** subject address records it was populated
**0 times**. The only phone number in this dataset is the court-appointed trustee's office line
(95.7% filled, 142 distinct numbers across 286 in the sample — these are real, different law firms).
That is deliberate on the register's part: once a company is bankrupt, the person you are legally
meant to contact about the estate *is* the trustee.

So: **this is a distress-signal feed and a trustee directory, not a company-contact scraper.** If
you want the company's own switchboard, join `kvkNumber` to a Chamber-of-Commerce source.

***

### 🧩 What one row is

One row per **publication**, which is the grain the register itself publishes in. Across a 300-record
sample, 300 publications carried 300 distinct national case numbers, so in a 30-day window a
publication is effectively one case event.

Publication types, with the count each drew over the measured 30-day window:

| Type | 30 days | What it means |
|---|---|---|
| `uitspraken faillissement` | 653 | **A company has just been declared bankrupt.** The lead. |
| `uitspraken schuldsanering` | 512 | A private individual entered debt restructuring. |
| `einde faillissementen` | 433 | A bankruptcy closed — estate wound up or no assets found. |
| `einde schuldsaneringen` | 299 | A debt restructuring ended. |
| `uitspraken surseance` | 29 | Suspension of payment granted (the step before bankruptcy). |
| `einde surseances` | 21 | A suspension ended. |
| `overig` | 936 | Hearings, corrections, trustee and judge replacements, distribution lists. |

Pick `uitspraken faillissement` alone and you get a clean stream of newly-failed companies. Leave it
empty and you get the whole docket, which is what you want if you are tracking cases rather than
sourcing leads.

***

### 🔒 Privacy — why this ships companies only by default

The register covers **named private individuals** as well as companies, with their **birth date**
(99.4% filled) and **home address** (87.4%) attached. Measured on the 300-record sample: 125
companies, 175 private individuals.

Two gates, both shut by default:

1. **`subjectType` defaults to `companies`.** A default run never returns a private individual at all.
2. **`includePersonalData` defaults to `false`.** Even if you switch to individuals, `birthDate`,
   `birthPlace` and `homeAddress` are omitted entirely (the keys are absent, not null) and the row is
   flagged `personalDataWithheld: true`. Name, case and trustee still come through, because that is
   the publication.

The register also has its own suppression flag, `geheimAdres` — 21 of the 300 records — and it blanks
the addresses server-side when set. Those rows come back with `addressWithheld: true`.

This is public-register data from a court, but it is still EU personal data about named people in
financial distress. **Your lawful basis for processing it is yours, not ours.**

Company/individual is decided by the register's own `KvKNummer` field, never by the name. We tested
a name-suffix rule (`B.V.`, `V.O.F.`, `Stichting`…) and it missed a real company — "EANA Transport" —
1 time in 125, so it is not used anywhere in this Actor, not even to skip a lookup.

***

### ⏳ The hard limit: there is no archive

**The register serves a rolling window of about one calendar month and nothing behind it.** A search
with an older start date is refused outright with `StartDate mag niet liggen voor 17-08-2026`, and
asking the daily bulletin for a day outside the window returns HTTP 200 with zero publications — a
success-shaped empty.

There is no way to backfill. If you want Dutch insolvency history, **run this on a daily or weekly
schedule and keep your own.** `daysBack` maxes out at 31 for that reason, and if you ask for more
than the register is serving, the Actor reads the real floor out of the refusal message and clamps
to it rather than guessing, recording the clamp in `RUN_SUMMARY`.

#### And the date search does not filter on the publication's date

Measured, and worth knowing: of 300 publications returned by a 30-day query, **275 (91.7%) were
dated inside that window and 25 were not** — the oldest from 2023. The register re-serves an older
publication when its case record is touched. The same check on a 7-day query gave 55 of 61 (90.2%).

`onlyPublicationsDatedInWindow` is **on by default**, so "the last 7 days" means the last 7 days.
Those strays are fetched, found to be out of window, dropped, and **not charged**. Turn it off if
you want them — an old case being re-published is itself a signal.

***

### 🚧 Two things that will silently corrupt a naive scrape

**1. The register caps every search at 1,005 rows, reports 1,005 as the total, and has no
pagination.** The model has no offset, page or limit field. Asked for the whole window unfiltered it
answers `aantalResultaten: 1005`; the truth for that same window is **2,574**. So this Actor never
issues one broad query. It always splits by district court — eleven cheap POSTs, 4.2 seconds — where
the largest single court over a full window is 494 of the 1,005 ceiling. If a court ever does reach
the ceiling it is split again by publication type (worst measured cell: 240), and if anything is
*still* truncated it is named in `RUN_SUMMARY` rather than quietly shipped as complete.

That partitioning is verified, not hoped for: partitioning the same window **by court** and **by
publication type** produced the identical set of 2,574 publication ids, and those 2,574 ids carry
exactly the eleven court tokens. Note that 134 publications appear under two courts, so
deduplication is mandatory — this Actor dedupes by publication id.

**2. The register throttles with a zero-byte HTTP 403 that looks exactly like a block.** Past roughly
45 requests/second it starts refusing requests with a completely empty 403 — no body, no message, no
`Retry-After`. Measured 403 rate over 300 calls: **0% at 13.6, 22.2, 29.5 and 44.6 req/s; ~15% at
\~60 req/s.** It is not a block: **150 of 150 refused ids returned HTTP 200** when asked again more
slowly. A scraper that treats that 403 as a dead record silently drops ~15% of the register — which
is exactly what happened on our first full sweep, losing 108 of 2,574 records. This Actor retries a
403 on a fresh proxy session and defaults to concurrency 4. The full sweep at those defaults: **0
failures out of 2,574.**

***

### 📦 What you get in a row

```jsonc
{
  "publicationId": "13.ams.26.290.F.1300.1.26",
  "publicationUrl": "https://insolventies.rechtspraak.nl/#!/details/13.ams.26.290.F.1300.1.26",
  "publicationDescription": "Uitspraak faillissement op 15 september 2026",
  "publicationDate": "2026-09-16",
  "subjectType": "company",
  "subjectName": "World Logistics Services B.V.",
  "kvkNumber": "76306593",
  "tradeNames": ["Class Opleidingen", "Transportservice Holland"],
  "formerTradeNames": [],
  "businessAddress": "Hagenau 52, 1025CT Amsterdam",
  "businessStreet": "Hagenau 52",
  "businessPostalCode": "1025CT",
  "businessCity": "Amsterdam",
  "postalAddress": null,
  "court": "Rechtbank Amsterdam",
  "caseNumber": "F.13/26/290",
  "caseType": "faillissement",
  "supervisionCaseNumber": "NL:TZ:0000540207:F001",
  "previousCaseNumber": null,
  "supervisingJudge": "mr. V.G.T. van Emstede",
  "trusteeRole": "curator",
  "trusteeName": "mr V.N. Gijlstra",
  "trusteePhone": "020-5747474",
  "trusteePhoneE164": "+31205747474",
  "trusteeAddress": "Dijsselhofplantsoen 16, 1077BL AMSTERDAM",
  "trusteePostalCode": "1077BL",
  "trusteeCity": "AMSTERDAM",
  "claimsDeadline": null,
  "hearingLocation": null,
  "publicationCount": 1,
  "publicationHistory": [
    { "publicationId": "13.ams.26.290.F.1300.1.26",
      "description": "Uitspraak faillissement op 15 september 2026",
      "date": "2026-09-16" }
  ],
  "addressWithheld": false,
  "personalDataWithheld": false,
  "scrapedAt": "2026-09-17T01:12:44.108Z"
}
```

`caseType` is derived from the national case number's prefix — `F.` faillissement, `R.`
schuldsanering, `S.` surseance — and was correct on 300 of 300. `trusteePhoneE164` is derived; the
register's own string is kept untouched in `trusteePhone`, because the register writes numbers four
different ways (`0492-749990`, `071-5358000`, `0546-58 88 19`, `0113211666`).

***

### ⚙️ Suggested inputs

**A weekly feed of newly-bankrupt Dutch companies** — the main use:

```json
{
  "daysBack": 7,
  "publicationTypes": ["uitspraken faillissement"],
  "subjectType": "companies",
  "maxRecords": 500
}
```

Measured live: 97 bankruptcy publications in the last 7 days, 25 company rows delivered in 3.7
seconds at a cap of 25.

**One region only** — add `"courts": ["amsterdam", "noord-holland"]`.

**Everything the register currently holds** — `"daysBack": 31, "maxRecords": 0`. 2,574 publications
examined, 859 company rows, 103 seconds.

A capped run returns the **newest publications first**: the register orders every result set
newest-first, verified across a whole 494-row court partition (42 of 42 consecutive dated pairs in
non-increasing order).

***

### 💰 Pricing

**$0.002 per delivered row. No start fee. Both Apify auto-events removed.**

You are charged once per row that reaches your dataset, and for nothing else:

- a publication read but excluded by your filters — **not charged**
- a publication dated outside your window — **not charged**
- a publication the register has no detail record for — **not charged**
- a publication we could not reach — **not charged**

All four are counted separately in `RUN_SUMMARY`, so the bill always reconciles. A run that matches
nothing costs nothing at all, because there is no run-start fee.

***

### ⚖️ Source and robots.txt

Data: Centraal Insolventieregister, published by the Raad voor de rechtspraak. The endpoints used
are the register's own public JSON services, unauthenticated.

`robots.txt` at the time of building, verbatim:

```
user-agent: *
Disallow: /Services/VerslagenService/getPdf/*
Disallow: /default*
Disallow: /#!/result*
Disallow: /#!/detail*
Disallow: /pdf*
Sitemap: https://insolventies.rechtspraak.nl/sitemap.xml
```

The `WebInsolventieService` and `BekendmakingenService` endpoints this Actor reads are **not** in
that Disallow list. The Actor does not touch `VerslagenService/getPdf`.

***

### 🧪 Verification

`node offline_validate.mjs` runs **305 assertions** with no network and no platform, against bytes
captured from the live register: the real CSRF form page, the real 400 the service returns when the
handshake is missing, all eleven per-court search responses, the 203-byte refusal that names the
register's rolling floor, a 33-day sweep of start-date boundaries, and 300 real detail records. The
field-fill percentages in this README are recounted by that validator on every run, and the dataset
schema's field list is diffed against the fields the code actually produces.

# Actor input Schema

## `daysBack` (type: `integer`):

How many days of publications to cover, ending today (Dutch calendar days — the register's own day boundary is midnight in Amsterdam). Measured over a full window: ~98 publications per business day, 2,574 in 30 days. Weekends and public holidays have none.

## `publicationTypes` (type: `array`):

Leave empty for all seven. For a distressed-lead list you almost always want just "uitspraken faillissement" — that is the moment a company is declared bankrupt and a trustee is appointed.

## `courts` (type: `array`):

Leave empty for the whole country. The actor queries court by court either way — the register truncates any single search at 1,005 rows with no pagination and still reports the truncated figure as the total, and splitting by court is what keeps every query under that ceiling.

## `subjectType` (type: `string`):

Decided from the register's own `KvKNummer` field on the subject, not from the name. A name-suffix rule (B.V./V.O.F./Stichting…) was tested and missed 1 real company in 125 — "EANA Transport" — so it is not used.

## `includePersonalData` (type: `boolean`):

OFF by default. When off, rows about a private individual still carry the name, the case and the trustee, but `birthDate`, `birthPlace` and `homeAddress` are omitted and `personalDataWithheld` is set. Measured on the 175 individuals in the sample: birth date 99.4%, home address 87.4% — this is real personal data about named people in financial distress, and it is your lawful basis, not ours. Has no effect on company rows, where all three fields are 0% filled anyway.

## `onlyPublicationsDatedInWindow` (type: `boolean`):

ON by default, and it matters more than it sounds. The register's date search is NOT a filter on a publication's own date: it re-serves an older publication whenever the case record is touched. Measured — of 300 publications returned by a 30-day query, 275 (91.7%) were dated inside it and 25 were not, one from 2023; the same check on a 7-day query gave 55 of 61 (90.2%). With this on, those are dropped and never charged.

## `onlyWithKvk` (type: `boolean`):

A Dutch Chamber of Commerce number is the join key to every other Dutch company database. Measured: 100% of company rows carry one; 25.7% of individual rows do too (sole traders). Redundant when Subject is set to companies.

## `nameContains` (type: `string`):

Case-insensitive substring, matched against the subject name and every current and former trade name on the record. Applied after the record is read, so it never risks the near-miss that a server-side keyword search would.

## `postcodePrefixes` (type: `array`):

Dutch postcode prefixes, e.g. "10" for Amsterdam-centre ranges or "3011" for one exact code. Spaces are ignored and matching is case-insensitive. A row matches if its business, postal or home postcode starts with any of them.

## `maxRecords` (type: `integer`):

Hard cap on rows delivered — and therefore on rows charged. 0 means no cap. A capped run returns the newest publications first: the register orders every result set newest-first, verified across a whole 494-row court partition (42 of 42 consecutive dated pairs in non-increasing order). The whole 30-day window is 2,574 publications, about 3.5 minutes.

## `detailConcurrency` (type: `integer`):

The register does rate-limit, and it does it quietly: past roughly 45 requests/second it answers a zero-byte HTTP 403 with no body and no Retry-After, which looks exactly like a block and is not — 150 of 150 refused ids returned 200 when asked again more slowly. Measured 403 rate over 300 calls: 0% at concurrency 2, 3, 4 and 6 (13.6-44.6 req/s), ~15% at concurrency 8-10 (~60 req/s). The default of 4 sits comfortably inside the clean band, and every 403 is retried on a fresh proxy session anyway. Raise it only if you are in a hurry and are watching the run log.

## `proxyConfiguration` (type: `object`):

Optional. insolventies.rechtspraak.nl has no anti-bot, no CAPTCHA and no user-agent gate — it answered plain requests with no proxy at all throughout testing. Datacenter is plenty. If you ever do get blocked, switch to residential here. NOTE: the search leg pins ONE proxy session for the whole run, because the register's anti-CSRF token is only valid from the address that minted it.

## Actor input object example

```json
{
  "daysBack": 7,
  "publicationTypes": [],
  "courts": [],
  "subjectType": "companies",
  "includePersonalData": false,
  "onlyPublicationsDatedInWindow": true,
  "onlyWithKvk": false,
  "postcodePrefixes": [],
  "maxRecords": 25,
  "detailConcurrency": 4,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `items` (type: `string`):

One row per register publication: the subject (company name, KvK number, trade names, business and postal address), the case (court, national case number, proceeding type, supervising judge), the court-appointed trustee with a phone number, and the publication itself with its date and full history.

## `runSummary` (type: `string`):

RUN\_SUMMARY: the window actually covered, the per-court totals the register reported, whether any partition hit the register's 1,005-row ceiling, and a billing reconciliation that keeps the three kinds of nothing apart - filtered out, no detail record, unreachable.

# 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 = {
    "daysBack": 7,
    "subjectType": "companies",
    "onlyPublicationsDatedInWindow": true,
    "maxRecords": 25,
    "detailConcurrency": 4
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapersdelight/rechtspraak-insolvency-nl-scraper").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 = {
    "daysBack": 7,
    "subjectType": "companies",
    "onlyPublicationsDatedInWindow": True,
    "maxRecords": 25,
    "detailConcurrency": 4,
}

# Run the Actor and wait for it to finish
run = client.actor("scrapersdelight/rechtspraak-insolvency-nl-scraper").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 '{
  "daysBack": 7,
  "subjectType": "companies",
  "onlyPublicationsDatedInWindow": true,
  "maxRecords": 25,
  "detailConcurrency": 4
}' |
apify call scrapersdelight/rechtspraak-insolvency-nl-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapersdelight/rechtspraak-insolvency-nl-scraper"
        }
    }
}
```

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/GEWr0pjVfWVi5Ux2l/builds/VVM4Mnocr82cYH6zK/openapi.json
