# Belgium NBB Annual Accounts & Financial Health Score (`goobertje/belgium-nbb-financial-scorer`) Actor

Get the latest Belgian annual accounts from the National Bank of Belgium (CBSO) for any enterprise number, parsed into key figures, liquidity and solvency ratios and a transparent 0-100 financial health score. Invalid numbers and companies without structured filings are free.

- **URL**: https://apify.com/goobertje/belgium-nbb-financial-scorer.md
- **Developed by:** [goober](https://apify.com/goobertje) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 balance sheet scrapeds

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

## Belgian company data API: annual accounts, ratios and financial health score

Look up Belgian companies by enterprise number and get their latest filed annual accounts as clean JSON. Built for credit and counterparty checks, sales teams and analysts who need official figures without reading PDFs. You get key balance-sheet and income-statement figures, standard ratios and a transparent 0-100 health score.

The data comes from the National Bank of Belgium (NBB) Central Balance Sheet Office. Figures are the rubric codes of the filed accounts, and anything a filing does not report is `null`.

### Why use this Actor

- **Official source**: the NBB's public Consult service, not a third-party aggregator.
- **Any number format**: `0403.227.515`, `BE0403227515`, 10 or 9 digits. Numbers are checked with the mod-97 check digits before any request.
- **Multi-year view**: the last 1 to 10 fiscal years per company in one run.
- **Free rows for failures**: invalid numbers, companies without filings and errors cost nothing.
- **No API key, proxy or browser**: plain HTTP, about 1 request per second.

### Quick start

Click Start with the prefilled input:

```json
{ "enterpriseNumbers": ["0403.227.515", "BE0402206045"], "filingsPerCompany": 1, "maxItems": 5 }
```

You get 2 dataset items (KBC Groep and Delhaize) with key figures, ratios and a health score. It takes about 5 seconds.

### What you get

One dataset item per filed balance sheet (`status: "ok"`). Inputs that cannot be resolved give a free status row: `invalid_enterprise_number`, `duplicate`, `skipped_max_items`, `no_filings`, `no_structured_filings` or `error`.

| Field | Meaning |
|---|---|
| `enterpriseNumber`, `enterpriseNumberFormatted`, `name`, `legalForm`, `address` | Identity as stated in the filing |
| `filing.fiscalYear` | Calendar year of the fiscal year end (a year ending 2025-03-31 is 2025) |
| `filing.reference`, `depositDate`, `periodStart`, `fiscalYearEnd`, `modelCode`, `modelName`, `depositType`, `currency` | Filing metadata (`m02-f` full model, `m87-f` micro model, others possible) |
| `keyFigures` | Named figures from rubric codes (table below) |
| `keyFiguresPreviousYear` | Comparative values the same filing prints for the previous year (only some rubrics; may be `null`) |
| `ratios` | `currentRatio`, `quickRatio`, `solvencyPct`, `debtToEquity`, `operatingMarginPct`, `netMarginPct`, `returnOnEquityPct`, `returnOnAssetsPct` |
| `healthScore` | `score` (0-100 or `null`), `band`, `coverage`, per-component `points`, `flags` |
| `sourceUrl` | The exact NBB CSV export the figures came from |

Input fields: `enterpriseNumbers` (required), `filingsPerCompany` (1-10, default 1), `maxItems` (default 50), `includeAllRubrics` (default false, adds raw `rubrics` and `rubricsPreviousYear`), `requestDelaySecs` (default and minimum 1).

#### Rubric codes used (verified on real filings)

| Key figure | Rubric | Notes |
|---|---|---|
| `totalAssets` | `20/58` | Equals `21/28` + `29/58` (checked on a real filing) |
| `equity` | `10/15` | Equals capital + premiums + reserves + result carried forward (checked) |
| `totalLiabilities` / `totalDebts` / `longTermDebts` / `currentDebts` | `10/49` / `17/49` / `17` / `42/48` | |
| `fixedAssets`, `currentAssets`, `inventories`, `tradeAndOtherReceivables`, `currentInvestments`, `cashAtBankAndInHand` | `21/28`, `29/58`, `3`, `40/41`, `50/53`, `54/58` | |
| `turnover` | `70` | Not filed by micro companies (`m87-f`): `null` |
| `operatingIncome` | `70/76A` | |
| `grossMargin` | `9900` | Only for companies using the gross-margin presentation |
| `operatingResult` | `9901` | |
| `profitBeforeTax` / `incomeTaxes` / `netResult` | `9903` / `67/77` / `9904` | `9904 = 9903 - 67/77` (checked) |
| `personnelCosts`, `depreciation` | `62`, `630` | |
| `fte` | `9087`, falling back to `1003` | Average FTE from the social balance sheet |

#### Financial health score (formula)

A simple, fully visible heuristic. It is not a credit rating.

| Component | Max points | Rule (linear between the bounds, clamped) |
|---|---|---|
| Solvency | 30 | `equity (10/15) / total assets (20/58)`: 0 points at 0% or below, 30 at 40% or above |
| Liquidity | 25 | `current assets (29/58) / current debts (42/48)`: 0 points at 0.5 or below, 25 at 1.5 or above. Full points if current debts are exactly 0 and current assets are above 0 |
| Profitability | 25 | `net result (9904) / total assets`: 0 points at -5% or below, 25 at 8% or above |
| Operating result | 10 | `9901 > 0`: 10 points, otherwise 0 |
| Equity | 10 | `10/15 > 0`: 10 points, otherwise 0 |

`score = round(points earned / max points of the components that could be computed * 100, 1)`. `coverage` is the share of the 100 points that could be computed; below 0.5 the score is `null`. Bands: 80+ excellent, 60+ good, 40+ moderate, 20+ weak, below 20 critical. `flags` can contain `negativeEquity`, `operatingLoss`, `netLoss`, `currentRatioBelow1`.

Other ratios: `quickRatio = (40/41 + 50/53 + 54/58) / 42/48` (a missing rubric counts as 0 inside the sum, `null` if all three are missing); `solvencyPct = 10/15 / 20/58 * 100`; `debtToEquity = 17/49 / 10/15` (positive equity only); margins use turnover `70`, or `70/76A` if there is no turnover; `returnOnEquityPct` needs positive equity. Division by zero gives `null`.

#### Sample output (real run, 2026-10-18)

Input: `{"enterpriseNumbers": ["BE0402206045"], "filingsPerCompany": 1}`

```json
{
  "status": "ok",
  "input": "BE0402206045",
  "enterpriseNumber": "0402206045",
  "enterpriseNumberFormatted": "0402.206.045",
  "name": "Delhaize Le Lion/De Leeuw",
  "legalForm": "Naamloze vennootschap",
  "address": { "street": "Brusselsesteenweg", "number": "347", "postalCode": "1730", "city": "Asse", "country": "België" },
  "filing": {
    "reference": "2026-00158106",
    "depositId": "65334ee0-632f-11f1-b5ca-e3295b0e7b47",
    "depositDate": "2026-06-08T11:47:56.043Z",
    "fiscalYear": 2025,
    "periodStart": "2025-01-01",
    "fiscalYearEnd": "2025-12-31",
    "modelCode": "m02-f",
    "modelName": "Volledig model kapitaalvennootschap",
    "depositType": "Initial",
    "language": "nl",
    "currency": "EUR",
    "generalAssemblyDate": "2026-05-28",
    "importFileType": "ZIP",
    "taxonomy": "Modellen 2021 (25.0.12)",
    "liquidationFlag": false,
    "correctionFlag": false
  },
  "keyFigures": {
    "totalAssets": 2023612498.52,
    "fixedAssets": 824073807.0,
    "currentAssets": 1199538691.52,
    "inventories": 156510451.45,
    "tradeAndOtherReceivables": 628133622.49,
    "currentInvestments": null,
    "cashAtBankAndInHand": 352880812.95,
    "equity": 239392751.69,
    "totalLiabilities": 2023612498.52,
    "totalDebts": 1745628523.86,
    "longTermDebts": 171871263.45,
    "currentDebts": 1555728055.26,
    "turnover": 5492209172.95,
    "operatingIncome": 5757488503.08,
    "grossMargin": null,
    "operatingResult": 207342298.47,
    "profitBeforeTax": 191447663.03,
    "incomeTaxes": 13782493.6,
    "netResult": 177827804.16,
    "personnelCosts": 326883544.17,
    "depreciation": 124017144.0,
    "fte": 3423.3
  },
  "keyFiguresPreviousYear": { "fte": 6013.7 },
  "ratios": {
    "currentRatio": 0.77,
    "quickRatio": 0.63,
    "solvencyPct": 11.83,
    "debtToEquity": 7.29,
    "operatingMarginPct": 3.78,
    "netMarginPct": 3.24,
    "returnOnEquityPct": 74.28,
    "returnOnAssetsPct": 8.79
  },
  "healthScore": {
    "score": 60.6,
    "band": "good",
    "coverage": 1.0,
    "components": [
      { "name": "solvency", "points": 8.9, "maxPoints": 30.0, "available": true, "basis": "equity / total assets: 0 pts at <= 0%, full at >= 40%" },
      { "name": "liquidity", "points": 6.8, "maxPoints": 25.0, "available": true, "basis": "current ratio: 0 pts at <= 0.5, full at >= 1.5" },
      { "name": "profitability", "points": 25.0, "maxPoints": 25.0, "available": true, "basis": "net result / total assets: 0 pts at <= -5%, full at >= 8%" },
      { "name": "operatingResult", "points": 10.0, "maxPoints": 10.0, "available": true, "basis": "operating result (9901) > 0: full points, otherwise 0" },
      { "name": "equity", "points": 10.0, "maxPoints": 10.0, "available": true, "basis": "equity (10/15) > 0: full points, otherwise 0" }
    ],
    "flags": ["currentRatioBelow1"]
  },
  "sourceUrl": "https://consult.cbso.nbb.be/api/external/broker/public/deposits/consult/csv/65334ee0-632f-11f1-b5ca-e3295b0e7b47",
  "scrapedAt": "2026-10-18T22:15:50Z"
}
```

### Pricing

Pay per event: **$5.00 per 1,000 balance sheets** (event `balance-sheet-scraped`, $0.005 each). You are charged once for each item with `status: "ok"`. Invalid numbers, duplicates, skipped inputs, companies without filings and errors are free status rows. `maxItems` caps the number of charged items per run.

Example: 100 companies with `filingsPerCompany: 3` returns up to 300 balance sheets, which is at most $1.50. The prefilled run (2 companies, 1 filing each) costs $0.01.

### Use with AI agents (MCP, Claude, Cursor, n8n, Make)

AI agents can call this Actor through the Apify MCP server. Add it to your MCP client config (you sign in with OAuth in the browser on first use):

```json
{ "mcpServers": { "apify": { "url": "https://mcp.apify.com?tools=goobertje/belgium-nbb-financial-scorer" } } }
```

Example prompts and the input each one maps to:

- "How financially healthy is the company with number BE0402.206.045?" -> `{ "enterpriseNumbers": ["BE0402206045"] }`
- "Show the last 3 years of accounts and score for 0403.227.515." -> `{ "enterpriseNumbers": ["0403227515"], "filingsPerCompany": 3 }`
- "Check these two suppliers and give me the raw rubric codes too." -> `{ "enterpriseNumbers": ["0403227515", "0402206045"], "includeAllRubrics": true }`

In n8n, Make or Zapier, use the Apify node or module, pick this Actor and paste the same JSON.

### Use cases

- Credit and counterparty checks on Belgian customers and suppliers (KYB).
- Ranking sales leads by company size and financial strength.
- Multi-year trend screening for investment, M\&A and competitor research.
- Enriching a CRM or data warehouse with official statutory figures.

### FAQ

**Where does the data come from and is it allowed?** From the NBB Central Balance Sheet Office, which publishes annual accounts free of charge. The Actor uses the public endpoints behind consult.cbso.nbb.be. NBB's copyright notice allows reuse if the content is accurate and NBB is named as source.

**How fresh is it?** Accounts appear when companies deposit them, usually within 7 months of year end. The Actor returns what NBB publishes at run time.

**What does a typical run cost?** About $0.005 per balance sheet. Ten companies with the latest year cost $0.05.

**What if the source blocks or changes?** After repeated 403/429 answers the run stops with a clear message and keeps the items already pushed. It never tries to get around blocking.

**Can I schedule it or get only new items?** You can schedule runs with Apify schedules. There is no built-in delta mode, so compare `filing.reference` or `filing.depositId` between runs yourself.

**What is not included?** Consolidated accounts, PDF-only or paper filings, annual-report text, directors and shareholders.

### Limits and honest caveats

- **Statutory (non-consolidated) accounts only.** Consolidated accounts are skipped.
- **Only structured filings carry figures.** Many large groups and older filings are deposited as PDF, paper or microfilm. The Actor skips them, uses the next most recent structured filing, and returns a free `no_structured_filings` row if there is none.
- **A deposit only counts as a balance sheet if it holds at least one core figure** (total assets, equity, current assets or net result). Otherwise it is skipped and the next one is tried.
- If a later year fails to download, earlier years already collected are still returned.
- **One filing per fiscal year**: when a company filed both Dutch and French versions or a correction, the most recently deposited one is used.
- **Non-profit and other models** (associations, foundations) have different rubrics; unreported figures are `null` and the score may be `null` (low coverage).
- Amounts are in the filing currency (normally EUR), not rescaled.
- Throughput: roughly 2-3 requests per filing at 1 request per second.

#### Legal and disclaimer

The Actor uses only the public endpoints behind consult.cbso.nbb.be, honours `robots.txt` (the site publishes no crawl restrictions), identifies itself in its User-Agent, limits itself to about 1 request per second and stops on repeated 403/429 responses. The same data remains available free of charge from the NBB site.

**Source: National Bank of Belgium, Central Balance Sheet Office.** This Actor is not affiliated with, endorsed by or sponsored by the National Bank of Belgium. The health score is an automated heuristic for screening, not a credit rating, financial advice or a statement about any company's solvency. Filed accounts may contain errors or be outdated. Check the original filing before relying on it. Personal data in filings is public company data, but you are responsible for GDPR-compliant use.

Found a problem or want a field added? Open an issue on the Actor page.

# Actor input Schema

## `enterpriseNumbers` (type: `array`):

Belgian enterprise (CBE / KBO / BCE) numbers. Accepted formats: 0403.227.515, BE0403.227.515, BE 0403227515, 0403227515 (10 digits) or 403227515 (9 digits). Each number is normalised and checked with the mod-97 check digits; invalid numbers produce a free status row. Example: \["0403.227.515", "BE0402206045"].

## `filingsPerCompany` (type: `integer`):

How many of the most recent fiscal years to return per company (one dataset item and one charged balance sheet per fiscal year). 1 returns only the latest filing; up to 10 gives a multi-year view. Integer 1-10, default 1. Example: 3.

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

Hard cap on balance sheets returned (and charged) in this run, across all companies. Status rows for invalid or missing companies are free and do not count. Integer, default 50. Example: 100.

## `includeAllRubrics` (type: `boolean`):

Also return every NBB rubric code from the filing (current and previous year) in the fields rubrics and rubricsPreviousYear. Off by default to keep items small.

## `requestDelaySecs` (type: `number`):

Minimum pause between requests to consult.cbso.nbb.be. Cannot be lower than 1 second. Example: 1.5.

## Actor input object example

```json
{
  "enterpriseNumbers": [
    "0403.227.515",
    "BE0402206045"
  ],
  "filingsPerCompany": 1,
  "maxItems": 5,
  "includeAllRubrics": false,
  "requestDelaySecs": 1
}
```

# Actor output Schema

## `balanceSheets` (type: `string`):

Annual accounts with key figures, ratios and health score (plus free status rows for numbers that could not be resolved).

# 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 = {
    "enterpriseNumbers": [
        "0403.227.515",
        "BE0402206045"
    ],
    "filingsPerCompany": 1,
    "maxItems": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("goobertje/belgium-nbb-financial-scorer").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 = {
    "enterpriseNumbers": [
        "0403.227.515",
        "BE0402206045",
    ],
    "filingsPerCompany": 1,
    "maxItems": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("goobertje/belgium-nbb-financial-scorer").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 '{
  "enterpriseNumbers": [
    "0403.227.515",
    "BE0402206045"
  ],
  "filingsPerCompany": 1,
  "maxItems": 5
}' |
apify call goobertje/belgium-nbb-financial-scorer --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,goobertje/belgium-nbb-financial-scorer"
        }
    }
}
```

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/pkFIid8o0Hh9HloBB/builds/5fp4Rf7yqEbnRQFUc/openapi.json
