DEV Community

Cover image for EKW Scraper: Polish Land Registry Data via API
Peter
Peter

Posted on • Originally published at apify.com

EKW Scraper: Polish Land Registry Data via API

TL;DR

  • Poland's EKW (Elektroniczne Ksiegi Wieczyste) is the official land and mortgage register - 25 million property entries across 352 court departments
  • There's no API. The portal is a server-rendered JSP application protected by a WAF that blocks all datacenter IPs
  • I built an Apify actor that navigates the portal using residential proxies and returns structured JSON for $0.065 per property
  • Official court extracts cost 20 PLN (~$5) each. This actor gives you the same data for 10-50x less

Why EKW Has No API

Poland's Elektroniczne Ksiegi Wieczyste is the digital version of the land and mortgage registry maintained by the Ministry of Justice. It covers 25 million property entries managed across 352 court departments (wydzialy ksiag wieczystych) throughout the country. Every apartment, house, plot of land, and commercial building in Poland has a corresponding entry in this registry.

If you work in real estate, banking, or legal services in Poland, you check Ksiegi Wieczyste constantly. Who owns this property? Are there any mortgages? Easements? Restrictions?

The portal at ekw.ms.gov.pl lets you look up one property at a time using a KW number (e.g., WA4M/00012345/6). No bulk access, no export, no API. The system was built as a server-side JSP application - there are no XHR calls, no JSON endpoints, no hidden REST APIs. Every interaction requires filling HTML forms, clicking buttons, and parsing rendered HTML tables.

An official court extract (odpis) costs 20 PLN and requires a formal request. For due diligence on dozens or hundreds of properties, this doesn't scale.

EKW Technical Challenges: WAF, Proxies, Check Digits

WAF Protection Blocks Datacenter IPs

Unlike other Polish government registries (KRS, CRBR, KNF), the EKW portal has a Web Application Firewall that blocks all datacenter IP addresses. Standard proxies, VPNs, and cloud servers all get blocked immediately. AWS, GCP, Azure, Hetzner - all blocked.

The actor requires Polish residential proxies to function. This is the only actor in the entire Polish registry suite with this requirement, which is why the per-query cost is slightly higher than other actors in the series.

KW Number Check Digit Algorithm

KW numbers follow the format XXXX/XXXXXXXX/X where the last character is a check digit validated server-side. If you submit an invalid check digit, the portal rejects the request entirely.

The algorithm uses a character-to-number mapping (A=10, B=11, C=12... X=30, Y=31, Z=32) and a repeating weight pattern [1,3,7,1,3,7,1,3,7,1,3,7]. Each character in the court code and number is converted to its numeric value, multiplied by the corresponding weight, and the results are summed. The check digit is the sum modulo 10.

The actor auto-calculates the check digit if you provide just the court code and number, so you don't need to compute it yourself.

Session and Form Navigation

Because the portal is a traditional JSP application with server-side session state, the actor must maintain cookies and follow the exact form submission sequence the portal expects. Skipping steps or submitting forms out of order results in session errors.

EKW Land Registry Scraper: How to Use

JavaScript (Node.js)

import { ApifyClient } from 'apify-client';

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

const run = await client.actor('minute_contest/ekw-ksiegi-wieczyste-scraper').call({
    kwNumbers: ['WA4M/00012345/6', 'KR1P/00067890/3'],
    viewType: 'aktualna',   // 'aktualna' = current, 'zupelna' = full history
    sections: ['I-O', 'II', 'III', 'IV'],  // which sections to extract
    proxyConfiguration: { useApifyProxy: true, apifyProxyGroups: ['RESIDENTIAL'] }
});

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

for (const property of items) {
    console.log(`KW: ${property.kwNumber} | ${property.propertyType}`);
    console.log(`Court: ${property.courtName}`);
    // Section II - Ownership
    for (const entry of property.dzialII?.entries || []) {
        console.log(`  Owner: ${entry.value}`);
    }
    // Section IV - Mortgages
    for (const entry of property.dzialIV?.entries || []) {
        console.log(`  Mortgage: ${entry.value}`);
    }
}
Enter fullscreen mode Exit fullscreen mode

Python

from apify_client import ApifyClient

client = ApifyClient("YOUR_API_TOKEN")

run = client.actor("minute_contest/ekw-ksiegi-wieczyste-scraper").call(
    run_input={
        "kwNumbers": ["WA4M/00012345/6"],
        "viewType": "aktualna",
        "sections": ["I-O", "II", "III", "IV"],
        "proxyConfiguration": {
            "useApifyProxy": True,
            "apifyProxyGroups": ["RESIDENTIAL"]
        }
    }
)

items = client.dataset(run["defaultDatasetId"]).list_items().items
for prop in items:
    print(f"KW: {prop['kwNumber']} | {prop.get('propertyType')}")
    for entry in prop.get("dzialII", {}).get("entries", []):
        print(f"  Owner: {entry['value']}")
Enter fullscreen mode Exit fullscreen mode

EKW Property Data: What You Get

The registry is divided into 5 sections, each containing different categories of property information:

Section Polish Name Content
Dzial I-O Oznaczenie nieruchomosci Property designation - area, location, cadastral number, intended use
Dzial I-Sp Prawa zwiazane Associated rights - e.g., parking spaces, utility easements tied to the property
Dzial II Wlasnosc Ownership - who owns the property, ownership shares, acquisition basis
Dzial III Prawa i ograniczenia Rights, restrictions, easements, encumbrances, enforcement claims
Dzial IV Hipoteki Mortgages - amounts, creditors, currencies, interest rates

Each section returns structured label-value pairs parsed from the portal's HTML tables. You can select which sections to extract - if you only need ownership data, request just Section II to speed up the scrape.

The viewType parameter controls whether you get the current state (aktualna) or the full history (zupelna) including all past owners, cancelled mortgages, and historical changes.

Real-World Use Case: Portfolio Due Diligence

A private equity fund is evaluating a portfolio of 200 commercial properties in Warsaw for potential acquisition. Before making an offer, they need to verify:

  1. Current ownership (Section II) - confirm the seller actually owns each property
  2. Existing mortgages (Section IV) - identify liens that must be cleared at closing
  3. Restrictions and easements (Section III) - flag properties with enforcement claims or usage restrictions
  4. Property designation (Section I-O) - verify the area and intended use match the seller's representations

Using the official court process, 200 extracts at 20 PLN each would cost 4,000 PLN (~$1,000) and take days of manual requests. With this actor, the same data costs ~$13 and completes in under an hour.

Who Needs EKW Land Registry Data

  • Banks and mortgage lenders - verify property ownership and existing liens before loan approval
  • Real estate developers - bulk check properties for encumbrances and restrictions before acquisition
  • Law firms - due diligence on real estate transactions, title verification
  • Debt collectors - identify debtor assets by searching known property numbers
  • Property investors - screen acquisition targets for hidden liabilities and competing claims

EKW Scraper Pricing vs Official Extracts

Method Cost per property
Official court extract (odpis) 20 PLN (~$5)
Manual portal lookup Free (one at a time, no export)
This actor ~$0.065 (~0.27 PLN)

Note: Requires Apify plan with residential proxy access (not available on free tier). The residential proxy requirement is specific to EKW due to its WAF protection.

FAQ

Can I search EKW by address instead of KW number?

No. The EKW portal only accepts KW numbers (e.g., WA4M/00012345/6). There is no address-based search. If you don't know the KW number, you can sometimes find it through the local geodetic office (starostwo powiatowe) or through the geoportal.gov.pl cadastral map.

What is the difference between "aktualna" and "zupelna" view types?

The aktualna (current) view shows only the active state - current owners, active mortgages, and existing restrictions. The zupelna (full) view includes the complete history - all previous owners, cancelled mortgages, and historical changes. The full view takes longer to scrape and produces more data.

How fast can the EKW scraper process properties?

Due to WAF protection and residential proxy requirements, the actor processes approximately 10-20 properties per minute. This is slower than other actors in the suite but still orders of magnitude faster than manual lookups. For large batches (1000+ properties), expect run times of 1-2 hours.

Try it: apify.com/minute_contest/ekw-ksiegi-wieczyste-scraper


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

Top comments (0)