# Belgian Gazette Monitor — Staatsblad Delta (KBO) (`masterly_function/belgian-gazette-monitor`) Actor

Delta monitor for the Belgian Official Gazette (Belgisch Staatsblad / Moniteur belge) and KBO company filings: new filings, board changes, dissolutions, new publications — run-over-run events, not a stateless re-export. Full-text + KBO extraction. FPS Justice CC0 open data.

- **URL**: https://apify.com/masterly\_function/belgian-gazette-monitor.md
- **Developed by:** [Vesper](https://apify.com/masterly_function) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 results

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Belgian Gazette Monitor

**Structured, run-over-run monitoring of the Belgian Official Gazette (Belgisch Staatsblad / Moniteur belge) and company registrations (Bijlage van de Rechtspersonen) — as delta events, not a stateless re-export.**

Point it at a company, a postal code, a keyword, or a date window, and each run tells you **what changed since the last run**: new filings, board changes, dissolutions, name changes, registered-office moves — plus every gazette publication, full-text searchable.

Built on FPS Justice open data (dataset `fpsjust-moniteur`, **license CC0**, publisher: FPS Justice, coverage 1924 → today).

### Why a monitor instead of a scraper

The source portal is free but has **no API, no full-text search, no change alerts, and no feed**. This actor adds the three things a compliance, legal-tech, credit, or lead-gen workflow actually needs:

1. **Delta events.** State persists per mode between runs. First run seeds; every later run emits only what is new (`eventType: "new"`) or changed (`eventType: "change"`, with the prior fingerprint). Set *Emit delta events only* to `false` for a plain full export of the window instead.
2. **Full-text search where the portal has none.** Gazette publications are searched through their article text; registration filings through the **text layer of each filing PDF** (a keyword like `rolnummer` that never appears in any index title still finds exactly the right document). Matching rows carry `fullTextMatch: true` and the KBO number extracted from the PDF itself.
3. **Native company filters.** Registrations mode queries the registry's own search endpoint, so you can filter by **enterprise number** (accepts `0758528122`, `758528122`, `0758.528.122`), **postal code**, and **category** (`Oprichting`, `Ontslagen - benoemingen`, `Maatschappelijke zetel`, …), server-side, over your whole date window.

Both access paths (the daily index and the registry search) key on the same document id, so they dedupe against **one** persistent state — no duplicate events when you mix filters.

### Who this is for

- **Insolvency & credit risk** — watch dissolutions (`Einde`) and board changes among customers, debtors, or an entire postal code; a daily watch on 50 companies costs cents a month.
- **Legal & compliance** — statute amendments (`Statuten`), registered-office moves, name changes, and every gazette publication, full-text searchable.
- **Market & lead research** — new companies (`Oprichting`) appearing in a region or category, delivered daily as delta events instead of a hand-run export.

### Two modes

| | `mode: "gazette"` | `mode: "registrations"` |
|---|---|---|
| Source | Belgisch Staatsblad — laws, decrees, orders | Bijlage van de Rechtspersonen — KBO company filings |
| Native filters | document type | enterprise number, postal code, category |
| Full-text | article HTML | filing PDF text layer |
| Row keys | NUMAC | publication number (docid) |

### Input

| Field | Mode | Notes |
|---|---|---|
| `dateFrom` / `dateTo` | both | `YYYY-MM-DD` (required format — the portal silently drops other formats); defaults: yesterday / today |
| `enterpriseNumber` | registrations | monitor one company across its lifetime |
| `postalCode` | registrations | registered-office postal code |
| `category` | registrations | see the portal's category rubrics |
| `searchQuery` | both | full-text; kept rows flagged `fullTextMatch: true` |
| `documentType` | gazette | e.g. `Wet`, `Koninklijk besluit` |
| `emitDeltaOnly` | both | `true` (default): only delta events. `false`: full export of the window |
| `language` | both | `nl` / `fr` interface; the filing's own language is reported per row |
| `maxResults` | both | default 500 |

### Output

Dataset items are events (delta mode) or rows (export mode), plus a `recordType: "run_status"` row per run so a silent run is never ambiguous:

```json
{
  "companyName": "AARKSH TRADING",
  "enterpriseNumber": "758528122",
  "publicationDate": "2026-09-04",
  "publicationNumber": "26358534",
  "pdfUrl": "/tsv_pdf/2026/09/04/26358534.pdf",
  "language": "nl",
  "category": "ONTSLAGEN - BENOEMINGEN",
  "eventType": "new",
  "recordType": "gazette_event"
}
```

Full-export rows additionally carry `fullTextMatch` and `kboEnterpriseNumber` when a `searchQuery` was used.

### Typical runs

- **Watch one company:** `registrations`, `enterpriseNumber: "0758.528.122"`, no window limit — every future filing arrives as a `new` event.
- **Watch a region:** `registrations`, `postalCode: "2000"`, daily schedule — every filing in Antwerp, as it lands.
- **Watch a keyword:** `registrations` + `searchQuery` on a term that only appears inside filing PDFs.
- **Legal research backfill:** `gazette`, a date range, `documentType` + `searchQuery`, `emitDeltaOnly: false`.

### Getting the events where you work

Each run writes to a standard Apify dataset, so the notification half is platform plumbing, not actor code:

- **Google Sheets / Excel** — export the run's dataset from the Console, or pipe it through a Zapier/Make/Sheets step.
- **Slack / Discord / Teams** — add an Apify **webhook** on `RUN.SUCCEEDED` to your channel's relay, and include the run's dataset URL in the payload.
- **n8n / Zapier / Make** — trigger on `RUN.SUCCEEDED`, then fetch `GET /v2/datasets/{datasetId}/items` and branch on `eventType`.
- **Your own backend** — schedule with `POST /v2/acts/{actorId}/runs`, then read `{datasetId}/items?clean=true`.

Because state persists between runs, every destination only ever receives what actually changed.

### Provenance, compliance, and limits — stated plainly

- **Data**: CC0-licensed public records (FPS Justice open data). The actor discloses itself with a descriptive user agent, spaces requests (~400 ms), and reads per-run on demand — it does not crawl the portal.
- The portal's `robots.txt` disallows its scraping surface; there is no machine-readable bulk distribution to prefer (verified 2026-09: the data.gov.be portal links back to the same portal). If a sanctioned bulk path appears, this actor will move to it. If that position does not work for you, please do not use the actor.
- **Server-side cap**: broad registration queries (e.g. a postal code over a long window) can exceed the portal's 10,000-result limit. This surfaces as a `run_status` row and an `over-10000-cap` error — never as silent partial data. Narrow the window.
- The portal itself is authoritative; this actor re-publishes structured access, not the data. Expect Dutch/French source text as published.

### Pricing

Apify pay-per-event: $0.00005 per run start plus $0.005 per dataset result — scheduled delta monitoring costs cents per month (a quiet day bills the per-run minimum only), full exports undercut comparable Belgian legal-data actors while returning strictly more per row. Status/error rows are dataset items and bill like results. Full breakdown and cost examples: `PRICING.md` in the actor repository.

### About

Built by **Vesper** — an independent developer of small, verified, open-data monitors. Every parser is fixture-tested, every input path is checked against the live source before release, and the full source code ships with the actor: read it, fork a run, or pin a build. Corrections and feature requests are welcome.

***

*Development notes, probe logs, and verification evidence: `PROBE.md` in the actor repository. Not affiliated with FPS Justice; data is CC0 public-record material.*

# Actor input Schema

## `mode` (type: `string`):

gazette = Belgisch Staatsblad publications (laws, decrees, orders). registrations = Bijlage van de Rechtspersonen (KBO company filings).

## `dateFrom` (type: `string`):

YYYY-MM-DD. Default: yesterday.

## `dateTo` (type: `string`):

YYYY-MM-DD. Default: today.

## `language` (type: `string`):

Which ejustice interface to query. The per-filing language is reported separately from this choice (incumbent conflates the two).

## `searchQuery` (type: `string`):

Keyword filter. Gazette mode: full text of each publication (article.pl HTML). Registrations mode: full text of each filing PDF (tsv\_pdf, pdf-parse — text layer verified 2026-09-05); KBO near the 'Ondernemingsnr' label is extracted as kboEnterpriseNumber.

## `documentType` (type: `string`):

e.g. Wet, Koninklijk besluit, Ministerieel besluit.

## `category` (type: `string`):

e.g. Oprichting, Einde, Benaming, Maatschappelijke zetel, Kapitaal, Ontslagen - benoemingen, Statuten, Jaarrekeningen.

## `companyName` (type: `string`):

Case-insensitive substring match on the registered company name (client-side). Deliberately NOT the server-side naam parameter: naam+postalCode combined is a server-side quirk on ejustice (returns 'Geen' when both individually match), so this actor filters the parsed rows instead.

## `legalForm` (type: `string`):

Case-insensitive substring match on the filing's legal form (e.g. BV, NV, VZW, ASBL) as published in the Bijlage row.

## `enterpriseNumber` (type: `string`):

KBO/BTW number to monitor a specific company. LIVE via the native Bijlage search endpoint (rech\_res.pl): accepts 0758528122 / 758528122 / 0758.528.122. Server-side date window; result rows key on the same docid as the day index, so both paths dedupe against one state.

## `postalCode` (type: `string`):

Registered-office postal code (rech\_res.pl postkode). Broad queries over long windows trip the server-side 10.000-result cap — surfaced as a run\_status row, never silence; narrow the window.

## `maxResults` (type: `integer`):

Upper bound on rows emitted per run. Delta monitors rarely need more than a few hundred.

## `emitDeltaOnly` (type: `boolean`):

true = only filings/publications not seen in any prior run (persistent state per mode). false = full export of the window.

## `resetState` (type: `boolean`):

Wipes tracking state before the run. Never enable on a schedule.

## Actor input object example

```json
{
  "mode": "registrations",
  "dateFrom": "YESTERDAY",
  "dateTo": "TODAY",
  "language": "nl",
  "maxResults": 500,
  "emitDeltaOnly": true,
  "resetState": false
}
```

# Actor output Schema

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

All rows: gazette\_event / lifecycle\_event delta events, full-export rows, and run\_status lines.

# 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("masterly_function/belgian-gazette-monitor").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("masterly_function/belgian-gazette-monitor").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 masterly_function/belgian-gazette-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,masterly_function/belgian-gazette-monitor"
        }
    }
}

```

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/epMfD8NxDdlN5Ng08/builds/AIacOC9CGuftpchh4/openapi.json
