TL;DR
- Poland's KRZ (Krajowy Rejestr Zadluzonych) tracks all bankruptcy, restructuring, and enforcement proceedings
- The government has never released a public API despite 4+ years of promises
- The only alternatives are manual web searches or MGBI's paid subscription (200-500 EUR/month)
- I built an Apify actor that captures a JWT token from the portal and calls the internal REST API directly - giving you structured JSON for $0.05/entity check
The Problem
If you work in credit risk, compliance, or debt collection in Poland, you need to check the KRZ constantly. Is this company in bankruptcy? Has restructuring been filed? Are there enforcement proceedings?
The government portal at krz.ms.gov.pl lets you search manually - one company at a time, through a slow AngularJS interface. There's no API, no bulk export, no way to automate it.
The only commercial provider, MGBI, charges 200-500 EUR/month for API access. Great for banks, overkill for everyone else.
The Solution
The KRZ portal is a Single Page Application that loads Angular sub-apps in iframes. These sub-apps talk to REST APIs on the government's OpenShift backend. The APIs require a JWT token that's issued during app initialization.
My actor:
- Launches a headless browser via Puppeteer
- Loads the KRZ portal and triggers the Angular sub-app
- Captures the JWT token from the sub-app's first API request
- Uses the JWT to call the REST API directly - no DOM interaction, no fragile selectors
The result: structured JSON from 9 different search endpoints, covering everything the KRZ portal offers.
What You Can Search
| Search Mode | What It Finds | Example Input |
|---|---|---|
entity |
Companies with proceedings | {"entityName": "Getin"} |
person |
Natural persons by PESEL/NIP | {"identifier": "12345678901"} |
soleTrader |
Sole traders (JDG) | {"entityName": "Uslugi budowlane"} |
signature |
By court case signature | {"caseSignature": "WA1M/GUp/44/2023"} |
announcements |
Court notices and rulings | {"entityName": "Getin", "dateFrom": "2024-01-01"} |
shareholders |
Partners in personal companies | {"identifier": "0000585174"} |
advisors |
Restructuring advisors | {"entityName": "Nowak"} |
proceedingDetails |
Full case deep-dive | {"proceedingId": "uuid-from-search"} |
Step-by-Step: Check 100 Companies in 10 Minutes
1. Create a free Apify account
Go to apify.com and sign up. You get $5 in free credits - enough for about 70 company checks.
2. Navigate to the actor
Open the KRZ Debtor Registry Scraper in the Apify Store.
3. Configure your input
For a single company check:
{
"searchMode": "entity",
"entityName": "Getin Noble Bank",
"maxResults": 10
}
For a batch check by KRS numbers, use the API:
const { ApifyClient } = require('apify-client');
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const krsNumbers = ['0000585174', '0000028860', '0000635012'];
for (const krs of krsNumbers) {
const run = await client.actor('minute_contest/krz-debtor-scraper').call({
searchMode: 'entity',
identifier: krs,
maxResults: 5,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(`KRS ${krs}: ${items.length} proceedings found`);
}
4. Read the results
Each result includes:
{
"entityName": "GETIN RENT SPOLKA Z OGRANICZONA ODPOWIEDZIALNOSCIA",
"nip": "8992777278",
"krs": "0000585174",
"proceedingId": "96dd6ffc-191c-41ce-bcc4-998524e4efd2",
"proceedingType": "restructuring",
"court": "Sad Rejonowy dla Wroclawia-Fabrycznej we Wroclawiu"
}
5. Deep-dive into a specific proceeding
Take the proceedingId from step 4 and get full details:
{
"searchMode": "proceedingDetails",
"proceedingId": "96dd6ffc-191c-41ce-bcc4-998524e4efd2"
}
This returns: case signature, court, start/end dates, all participants, judge history, advisor assignments, address history, and all announcements.
Pricing
| Volume | Cost | vs MGBI |
|---|---|---|
| 1 company | $0.15 | Free trial vs 200 EUR/month |
| 100 companies | $5.10 | 30x cheaper |
| 1,000 companies/month | $50 | 3-7x cheaper |
| 10,000 companies/month | $300 (GOLD tier) | Comparable, no contract |
Part of the Polish Business Data Suite
This actor is one of five covering Polish government registries:
- eKRS Financial Statements - Balance sheets and income statements from KRS
- KRS Board Members - Full (non-anonymized) director names
- KNF Financial Registry - Payment institutions, credit intermediaries
- MSiG Court Gazette - Bankruptcy and liquidation announcements
- KRZ Debtor Registry - Insolvency and restructuring proceedings (this actor)
Together, they give you a complete Polish company intelligence workflow:
Is the company solvent? -> KRZ
Any court proceedings? -> MSiG
What's their financial health -> eKRS
Who runs the company? -> KRS Board Members
Are they properly licensed? -> KNF
Technical Details
For those interested in how the reverse engineering works:
1. Architecture discovery
The KRZ portal (krz.ms.gov.pl) is an AngularJS shell that loads Angular sub-apps via iframes from OpenShift backends (krz-rejpub-gui-krz-pub-prod.apps.ocp.prod.ms.gov.pl). Each module (entity search, announcements, advisors) is a separate Angular app on the same OpenShift cluster.
2. JWT capture
The sub-app obtains a JWT during initialization. We intercept it from the Authorization: bearer ... header on the first API request using Puppeteer's request interception. The JWT is valid for about 30 minutes.
3. API endpoints
30+ REST endpoints discovered by searching the Angular bundle's compiled JavaScript for URL patterns matching krzPubApiUrl + "...". Key endpoints include:
-
postepowanie/wyszukaj-po-podmiocie- entity search -
postepowanie/wyszukaj-po-of- person search -
obwieszczenie/wyszukaj-po-podmiocie- announcements -
postepowanie/szczegoly- proceeding details -
rdr-api/wyszukiwarka/doradcy- restructuring advisors (different backend)
4. No DOM scraping
After JWT capture, all data comes from direct HTTP calls. The browser is closed. This makes the actor fast (~1s per query after session setup) and resilient to UI changes.
5. The encryption keys
The KRS board members portal (separate project) uses AES-128-CBC encryption with a static key TopSecretApiKey1 and a 512-character steganographic token that embeds the KRS number at specific array positions. Yes, the key is literally hardcoded in the frontend JavaScript.
Built with Apify. Questions? Open an issue on the actor page.
Top comments (0)