DEV Community

Cover image for KRS Scraper: Get Polish Company Board Members
Peter
Peter

Posted on • Originally published at apify.com

KRS Scraper: Get Polish Company Board Members

TL;DR

  • Poland's KRS (Krajowy Rejestr Sadowy) is the official register of all companies, foundations, and associations
  • The official JSON API deliberately anonymizes personal data - names become L******, PESEL numbers become 5**********
  • But the PDF extract from the same government portal contains full, non-anonymized data
  • I built a KRS board members scraper on Apify that reverse-engineered the portal's AES encryption and token steganography to download PDFs programmatically - giving you structured JSON with full board member names for $0.04 per company

What Is KRS and Why Board Member Data Matters

KRS - Krajowy Rejestr Sadowy (National Court Register) - is Poland's central corporate registry maintained by the Ministry of Justice. Every Polish limited liability company (sp. z o.o.), joint-stock company (S.A.), foundation, and association must be registered here. The registry holds data on over 800,000 entities, including their legal form, registered address, share capital, and - crucially - the names of board members, supervisory board members, and shareholders.

Board member data is the backbone of B2B prospecting, compliance screening, and corporate due diligence in Poland. If you are evaluating a potential supplier, checking who controls a company for KYC/AML purposes, or building a list of decision-makers for sales outreach, you need the names of the people who actually run these companies. The problem is that the most convenient way to access KRS data - the official API - deliberately hides exactly this information.

Why the KRS API Anonymizes Names

If you query the official KRS API at api-krs.ms.gov.pl, you get company data back - but with all personal information censored:

{
  "nazwa": "EXAMPLE SP. Z O.O.",
  "reprezentacja": {
    "sklad": [
      { "nazwisko": "L******", "imiona": "A***" }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

This is useless if you need to know who actually runs a company. The government's position is that personal data in KRS is public - it is literally published in court gazettes (Monitor Sadowy i Gospodarczy). Anyone can walk into a court registry office and request a full extract for a few zlotys. But the API redacts names anyway, citing GDPR-era privacy concerns while simultaneously publishing the same data in PDF form on the very same portal.

This inconsistency creates a practical gap: developers building automated systems cannot get structured name data through the intended API path.

How the KRS Board Members Scraper Works

The same government portal at wyszukiwarka-krs.ms.gov.pl offers PDF extracts that contain full, non-anonymized data. Board members, supervisory board, shareholders - all with complete names.

The catch: downloading these PDFs requires replicating two security mechanisms the portal uses.

Security Layer 1: AES-128-CBC Encryption

The portal encrypts KRS numbers before sending them to the API using AES-128-CBC. The encryption key?

TopSecretApiKey1
Enter fullscreen mode Exit fullscreen mode

Yes, really. It is hardcoded in the Angular frontend JavaScript. The key is also used as the initialization vector (IV = key). This means anyone who inspects the frontend source can replicate the encryption - which is exactly what the scraper does.

Security Layer 2: Token Steganography

After encryption, the portal generates a 512-character token that hides the KRS number and a timestamp at specific array positions. The algorithm:

  1. Create a 512-character array filled with random alphanumeric characters
  2. Insert the encrypted KRS number at positions like [193, 8, 327, 501, 112, 74, 409, 226, 16, 306]
  3. Insert a checksum at positions [24, 46, 174, 345]
  4. Apply a circular right-shift to the entire array
  5. Join into a string and send as an API parameter

This is essentially an anti-automation measure - but since it is all implemented in client-side JavaScript, it can be reverse-engineered. The scraper replicates this entire token generation process, then parses the downloaded PDF into structured JSON fields.

KRS Board Member Data: What You Get

Field Example
companyName EXAMPLE SP. Z O.O.
krsNumber 0000019193
nip 5261006099
regon 010058960
legalForm SPOLKA Z OGRANICZONA ODPOWIEDZIALNOSCIA
address ul. Przykladowa 1, 00-001 Warszawa
boardMembers [{fullName, firstName, surname, role}]
supervisoryBoard [{fullName, firstName, surname, role}]
shareholders [{name, sharesValue, sharesCount}]
shareCapital 500000.00

You get two extract types: aktualny (current state only) and pelny (full history including past board members, address changes, and share capital modifications).

How to Use the KRS Board Members Scraper

JavaScript (Node.js)

import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_API_TOKEN' });

const run = await client.actor('minute_contest/krs-fullnames-scraper').call({
    krsNumbers: ['0000019193', '0000366824'],
    extractType: 'aktualny'  // 'aktualny' = current, 'pelny' = full history
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();

for (const company of items) {
    console.log(`${company.companyName} (KRS: ${company.krsNumber})`);
    console.log(`NIP: ${company.nip}, REGON: ${company.regon}`);
    for (const member of company.boardMembers) {
        console.log(`  ${member.role}: ${member.fullName}`);
    }
}
Enter fullscreen mode Exit fullscreen mode

Python

from apify_client import ApifyClient

client = ApifyClient("YOUR_API_TOKEN")

run = client.actor("minute_contest/krs-fullnames-scraper").call(
    run_input={
        "krsNumbers": ["0000019193", "0000366824"],
        "extractType": "aktualny"
    }
)

items = client.dataset(run["defaultDatasetId"]).list_items().items
for company in items:
    print(f"{company['companyName']} (KRS: {company['krsNumber']})")
    for member in company.get("boardMembers", []):
        print(f"  {member['role']}: {member['fullName']}")
Enter fullscreen mode Exit fullscreen mode

Real-World Use Case: B2B Sales Prospecting in Poland

Imagine you are an SDR at a SaaS company targeting mid-market Polish firms. You have a list of 500 KRS numbers from a trade association directory. You need to find the CEO or board president of each company for personalized outreach.

Without this scraper, your options are: (1) visit wyszukiwarka-krs.ms.gov.pl 500 times and manually download each PDF, (2) pay 200-500 EUR/month for a commercial data provider subscription, or (3) use the API and get L****** instead of names.

With the KRS board members scraper, you pass all 500 KRS numbers in a single API call. Within minutes, you get structured JSON with every board member's full name and role. Filter for PREZES ZARZADU (CEO) and you have your outreach list - complete with verified NIP and REGON identifiers you can cross-reference against other registries.

Total cost: about $20 for 500 companies.

Who Needs KRS Board Member Data

  • B2B sales teams - find decision-maker names before cold outreach
  • KYC/AML compliance - verify who controls a company
  • Due diligence - check board composition before investment or partnership
  • Credit risk - cross-reference board members across multiple companies
  • Legal firms - identify parties for litigation or contract negotiation

KRS Scraper Pricing Comparison

Provider Model Cost per 100 companies
Official KRS API Free $0 (but names are censored)
Transparent Data Subscription ~30 EUR
MGBI Subscription 200-500 EUR/month
This actor Pay-per-use ~$4

Free $5 Apify credits on signup = ~125 company lookups at no cost.

FAQ

Is KRS board member data legally public?

Yes. KRS is a public registry by law (Ustawa o Krajowym Rejestrze Sadowym). Anyone can request a full extract at any district court for a small fee. The data is also published in Monitor Sadowy i Gospodarczy. The API anonymization is the government's own policy choice, not a legal requirement - the PDF extracts from the same portal contain full names.

Can I look up companies by NIP instead of KRS number?

The KRS board members scraper accepts KRS numbers as input. If you only have a NIP (tax ID), you can first use the official KRS API search endpoint to find the corresponding KRS number, then pass it to this actor. Alternatively, the eKRS financial statements actor in this series accepts NIP directly.

How current is the data?

The scraper downloads the official extract from the Ministry of Justice portal in real time. The data reflects whatever is currently registered in KRS - typically updated within a few days of any court filing. This is the same data you would get by visiting the portal manually.

Try it: apify.com/minute_contest/krs-fullnames-scraper


This is part of the Polish Business Data APIs series covering programmatic access to Polish government registries.

Top comments (0)