# Boliga Scraper – Danish Real Estate Listings & Sold Prices (`haketa/boliga-scraper`) Actor

Boliga scraper & API for Danish real estate: search homes for sale and sold prices and export address, price, rooms, m2, lot, type, sale type, energy, days on market and coordinates. Denmark property and sold-price market data — fast, no login.

- **URL**: https://apify.com/haketa/boliga-scraper.md
- **Developed by:** [Haketa](https://apify.com/haketa) (community)
- **Categories:** Real estate, Lead generation
- **Features:** Limited permissions
- **Stats:** 13 total users, 6 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## 🇩🇰 Boliga Scraper — Danish Real Estate Listings & Historical Sold Prices (Solgte Boliger)

[![Apify Actor](https://img.shields.io/badge/Apify-Actor-00D4B2?style=flat-square\&logo=apify)](https://apify.com/haketa/boliga-scraper)
[![Denmark Real Estate](https://img.shields.io/badge/Market-Denmark_Real_Estate-C60C30?style=flat-square)](#)
[![Pagination](https://img.shields.io/badge/Pagination-1%2C000%2B_Properties-2EA44F?style=flat-square)](#)
[![Export Formats](https://img.shields.io/badge/Export-JSON_%7C_CSV_%7C_Excel_%7C_API-0969DA?style=flat-square)](#)

Extract **active property listings (`til salg`)** *and* **historical sold prices (`solgte boliger / salgspriser`)** from [Boliga.dk](https://www.boliga.dk) — Denmark's largest and most comprehensive real estate marketplace. This **Boliga scraper** pulls clean, structured **Boliga data** as JSON, CSV, or Excel: prices in DKK, price per m², rooms, living area, lot size, build year, energy class, street addresses, municipality codes, GPS coordinates, sale dates, sale types and foreclosure flags. **No login, no coding.**

Export every result to **JSON, CSV, Excel, XML, or API** for **Danish property valuation, PropTech applications, mortgage underwriting, real-estate lead generation, and housing market analytics**.

> 🇩🇰 **Built for Danish real-estate analysts, PropTech builders, mortgage & valuation teams, investors, brokers and data journalists** — anyone who needs reliable Boliga.dk listing and sold-price data on demand. This **Boliga data extractor** covers BOTH homes currently for sale *and* the official nationwide sold-price history (`solgte boliger`) spanning decades.

***

### ⚡ At a glance

| Feature | Details |
|---|---|
| **Target Platform** | [Boliga.dk](https://www.boliga.dk) (`boliga.dk` — Denmark's leading real-estate portal) |
| **Data Modes** | **For Sale (`forSale`)** active listings (`til salg`) **OR** **Sold Prices (`sold`)** historical sales (`solgte boliger`) |
| **Geographic Coverage** | Nationwide across all of Denmark — 40+ popular cities/zip areas in dropdown + any custom Danish postal code (`1000`–`9990`) |
| **Property Types** | Villa, Rækkehus (Townhouse), Ejerlejlighed (Apartment), Fritidshus (Vacation Home), Andelsbolig (Cooperative), Landejendom, Helårsgrund, Fritidsgrund, Villalejlighed |
| **Pagination & Scale** | Automatic multi-page extraction (50 properties/page) — supports **1,000+ to 100,000+ properties per run** |
| **Unique Sold-Price Fields** | Final `price` (DKK), `soldDate` (`YYYY-MM-DD`), `salesType` (`Alm. Salg`, `Fam. Salg`, `Auktion`), `squareMeterPrice`, `priceChangePercent` |
| **Active Listing Fields** | Asking `price` (DKK), `daysForSale`, `isForeclosure`, `energyClass` (`A`–`G`), `priceChangePercent`, `latitude`/`longitude` |
| **Deduplication** | 100% deduplicated by canonical Boliga property `id` |
| **Export Formats** | Bulk export to **JSON, CSV, Excel, XML, RSS**, or programmatic access via **Apify REST API** |

***

### 🔎 Why scrape Boliga?

[Boliga](https://www.boliga.dk) is Denmark's premier property search engine and housing data repository. Danes use it daily to browse homes for sale and — crucially — to look up what properties **actually sold for**. Boliga aggregates the entire Danish housing market and combines live agency listings with official Danish land-registry transaction history, making it the single richest public source of Danish real-estate intelligence.

Collecting this data manually is slow and impractical. Boliga paginates results 50 properties at a time, and a regional or nationwide sold-price archive contains hundreds of thousands of transactions. Copy-pasting addresses, square-meter prices, and sale dates into a spreadsheet does not scale and cannot feed automated models. This **Boliga scraper** turns the entire platform into a clean, on-demand data pipeline — delivering valuation-grade comparables, market trends and property lead lists in seconds.

**Common reasons people scrape Boliga:**

- 💰 Build **automated valuation models (AVMs)** and comparative market analyses (CMA) backed by real historical sold prices (`solgte boliger`).
- 📊 Track **Danish housing-market trends** — monitor DKK/m² (`squareMeterPrice`), days-on-market (`daysForSale`), and price reductions (`priceChangePercent`) by postal code over time.
- 🏦 Feed **mortgage, risk and collateral valuation** systems with verified transaction history and sale-type classifications (`Alm. Salg` vs `Fam. Salg`).
- 🏘️ Benchmark active agency listings, monitor inventory in specific Copenhagen, Aarhus, Odense or Aalborg postal codes, and spot distressed or foreclosure properties (`isForeclosure`).
- 🤖 Power **PropTech apps, dashboards and AI valuation assistants** with structured Danish property feeds.
- 🎯 Generate **real-estate leads** from newly listed properties, long-standing listings (`daysForSale > 120`), and price-reduced homes.

***

### ✨ What this Boliga scraper does

- ✅ **Two data modes in one actor** — scrape homes currently **for sale (`forSale` / til salg)** or the complete archive of **sold prices (`sold` / solgte boliger)**. Switch modes with a single dropdown.
- ✅ **Sold prices (`solgte boliger`) with sale-type classification** — captures final sale price, exact **sale date (`soldDate`)**, **sale type (`salesType`)** (distinguishing open-market `Alm. Salg` from family transfers `Fam. Salg` and auctions `Auktion`), price per m², and price change percentage.
- ✅ **Unlocked 1,000+ automatic pagination** — walks page after page (50 records per page) and merges results into one clean dataset until your `maxItems` limit is reached or all matching pages are exhausted.
- ✅ **Comprehensive targeting & filtering** — select Danish cities/areas from a 41-location dropdown, enter any comma-separated Danish postal codes (`zipCodes`), and filter by property type, price range (DKK), rooms, living area (m²), build year, and sale year (`soldYearMin` / `soldYearMax`).
- ✅ **Multi-zip sold-price orchestration** — automatically seeds and paginates each postal code across sold-price searches and deduplicates all records into a single unified dataset.
- ✅ **Zero-config sample mode** — hitting **Start** with empty filters returns a fast, ready-to-inspect sample of Danish homes so you can verify the schema immediately.
- ✅ **Export anywhere** — download as **JSON, CSV, Excel, XML, or RSS**, or integrate directly via the **Apify API**.

***

### 🎯 Use cases

#### 1. Real Estate Investors & Acquisition Analysts

Compare asking `squareMeterPrice` on active `forSale` listings against recent `sold` transactions (`Alm. Salg`) in the exact same `zipCode` and `propertyType` to immediately spot undervalued apartments (`Ejerlejlighed`) and houses (`Villa`).

#### 2. PropTech Platforms & Automated Valuation Models (AVMs)

Ingest historical sold transactions (`soldYearMin: 2020`) with `size`, `lotSize`, `rooms`, `buildYear`, `municipalityCode`, and `latitude`/`longitude` to train spatial machine-learning valuation models for the Danish housing market.

#### 3. Banks, Mortgage Lenders & Appraisers

Distinguish arms-length market transactions (`salesType: "Alm. Salg"`) from non-market family transfers (`"Fam. Salg"`) and forced auctions (`"Auktion"`) when underwriting loans or auditing portfolio collateral values.

#### 4. Estate Agencies & Broker Lead Generation

Identify high-opportunity seller leads by filtering active listings with long market exposure (`daysForSale`), negative `priceChangePercent`, or foreclosure status (`isForeclosure: true`).

#### 5. Economic Research & Data Journalism

Analyze regional housing affordability, urban vs. rural price divergence, and energy-label (`energyClass`) price premiums across Copenhagen, Frederiksberg, Aarhus, Odense, and Aalborg.

***

### 📥 Input parameters

Configure a run from the Apify Console UI or pass a JSON payload via the API. All fields are optional.

| Field | Type | Default | Description |
|---|---|---|---|
| `listingType` | string | `"forSale"` | Choose `"forSale"` (homes currently for sale / *til salg*) or `"sold"` (historical sold prices / *solgte boliger*). |
| `zipAreas` | array | `[]` | Select one or more Danish cities/postal areas from the dropdown (e.g. `2100` Copenhagen Ø, `8000` Aarhus C). Empty = all of Denmark. |
| `zipCodes` | string | `""` | Additional Danish 4-digit postal codes, comma-separated (e.g. `"2100,2200,8000"`). Combined with `zipAreas`. |
| `propertyTypes` | array | `[]` | Dwelling type codes (`"1"`–`"9"`): `1` Villa, `2` Rækkehus, `3` Ejerlejlighed, `4` Fritidshus, `5` Andelsbolig, `6` Landejendom, `7` Helårsgrund, `8` Fritidsgrund, `9` Villalejlighed. |
| `priceMin` / `priceMax` | integer | — | Minimum and maximum price in Danish Kroner (DKK). |
| `roomsMin` / `roomsMax` | integer | — | Minimum and maximum number of rooms. |
| `sizeMin` / `sizeMax` | integer | — | Minimum and maximum living area in square meters (m²). |
| `buildYearMin` / `buildYearMax` | integer | — | Earliest and latest construction year (`buildYear`). |
| `soldYearMin` / `soldYearMax` | integer | — | Sale-year range (e.g. `2023` to `2026`) — **used for `sold` mode only**. |
| `startUrls` | array | `[]` | Optional custom Boliga search URLs. When provided, these take priority over the filter inputs above. |
| `maxItems` | integer | `0` | Maximum number of properties to collect (prefilled to `100` for fast trial runs; `0` = unlimited for filtered searches). |
| `proxyConfiguration` | object | DK Residential | Proxy settings. Pre-configured with **Danish (`DK`) residential proxies** for reliable, rate-limit-free extraction. |

#### Example 1 — 1,000+ Villas & Apartments for sale in Copenhagen

```json
{
  "listingType": "forSale",
  "zipAreas": ["1050", "2000", "2100", "2200", "2300", "2400"],
  "propertyTypes": ["1", "3"],
  "priceMax": 12000000,
  "maxItems": 1000,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"],
    "apifyProxyCountry": "DK"
  }
}
```

#### Example 2 — Historical sold prices (`solgte boliger`) in Aarhus & Odense 💰

```json
{
  "listingType": "sold",
  "zipAreas": ["8000", "8200", "5000"],
  "soldYearMin": 2023,
  "maxItems": 1500
}
```

#### Example 3 — Nationwide affordable homes under 3,000,000 DKK

```json
{
  "listingType": "forSale",
  "priceMax": 3000000,
  "roomsMin": 3,
  "maxItems": 2000
}
```

***

### 📤 Output & field reference

Each property is saved as a normalized JSON record in your dataset. Below are two real records — one active listing (`forSale`) and one historical sale (`sold`).

#### Sample JSON — Active listing (`forSale`)

```json
{
  "listingType": "forSale",
  "id": "2353911",
  "url": "https://www.boliga.dk/bolig/2353911",
  "propertyType": "Villa",
  "propertyTypeCode": 1,
  "address": "Ved Grænsen 26",
  "zipCode": "2000",
  "city": "Frederiksberg",
  "municipalityCode": 147,
  "price": 10750000,
  "squareMeterPrice": 107500,
  "priceChangePercent": -6,
  "rooms": 4,
  "size": 100,
  "lotSize": 399,
  "floor": null,
  "buildYear": 1915,
  "energyClass": "E",
  "basementSize": 35,
  "daysForSale": 95,
  "isForeclosure": false,
  "soldDate": null,
  "salesType": null,
  "latitude": 55.68379,
  "longitude": 12.49919,
  "guid": "A8655DA2-45D3-4CEC-BC8A-3690FB211EEB",
  "scrapedAt": "2026-11-01T23:29:34.898Z"
}
```

#### Sample JSON — Sold property (`sold`)

```json
{
  "listingType": "sold",
  "id": "1062099617",
  "url": "https://www.boliga.dk/bolig/1062099617",
  "propertyType": "Ejerlejlighed",
  "propertyTypeCode": 3,
  "address": "Jette Tikjøbs Plads 5, st.",
  "zipCode": "8000",
  "city": "Aarhus C",
  "municipalityCode": 751,
  "price": 1783200,
  "squareMeterPrice": 31284,
  "priceChangePercent": 0,
  "rooms": 2,
  "size": 57,
  "lotSize": null,
  "floor": null,
  "buildYear": 2017,
  "energyClass": null,
  "basementSize": null,
  "daysForSale": null,
  "isForeclosure": null,
  "soldDate": "2026-10-26",
  "salesType": "Fam. Salg",
  "latitude": 56.167065,
  "longitude": 10.2278,
  "guid": "CAC07A06-FC8C-4918-BDE9-58842CDB679D",
  "scrapedAt": "2026-11-01T23:30:45.936Z"
}
```

#### 🏷️ Identity & Location Fields

| Field | Type | Coverage | Description |
|---|---|---|---|
| `listingType` | string | 🟢 High (100%) | Dataset mode: `"forSale"` or `"sold"`. |
| `id` | string | 🟢 High (100%) | Canonical Boliga property/transaction ID (used for deduplication). |
| `url` | string | 🟢 High (100%) | Direct link to the property page on `boliga.dk`. |
| `address` | string | 🟢 High (100%) | Street name, house number, and floor/door suffix. |
| `zipCode` | string | 🟢 High (100%) | 4-digit Danish postal code (`postnummer`). |
| `city` | string | 🟢 High (100%) | City or postal district name (`by`). |
| `municipalityCode` | number | 🟢 High (~99%) | Official Danish municipality code (`kommunekode`, e.g. `101` Copenhagen, `751` Aarhus). |
| `latitude` / `longitude` | number | 🟢 High (~99%) | WGS84 GPS coordinates for mapping and spatial analysis. |
| `guid` | string | 🟢 High (~95%) | Internal property GUID when present. |
| `scrapedAt` | string | 🟢 High (100%) | ISO 8601 extraction timestamp. |

#### 💰 Pricing & Transaction Fields

| Field | Type | Coverage | Description |
|---|---|---|---|
| `price` | number | 🟢 High (100%) | Price in DKK — asking price (`kontantpris`) for `forSale`, final transaction price (`salgspris`) for `sold`. |
| `squareMeterPrice` | number | 🟢 High (~99%) | Price per living square meter in DKK (`DKK/m²`), rounded to integer. |
| `priceChangePercent` | number | 🟢 High (~98%) | Percentage price change (`%`) from initial listing price. |
| `daysForSale` | number | 🟡 Active Only | Number of days on the market (`liggetid`) — populated on `forSale` records (`null` on `sold`). |
| `isForeclosure` | boolean | 🟡 Active Only | `true` if the listing is a forced auction (`tvangsauktion`) — populated on `forSale` records. |
| `soldDate` | string | 🟡 Sold Only | Official transaction date in `YYYY-MM-DD` format — populated on `sold` records (`null` on `forSale`). |
| `salesType` | string | 🟡 Sold Only | Transaction category on `sold` records: `"Alm. Salg"` (regular market sale), `"Fam. Salg"` (family transfer), `"Auktion"`, or `"Andet"`. |

#### 📐 Property Classification & Specs

| Field | Type | Coverage | Description |
|---|---|---|---|
| `propertyType` | string | 🟢 High (100%) | Human-readable Danish dwelling type (`Villa`, `Ejerlejlighed`, `Rækkehus`, `Andelsbolig`, `Fritidshus`, etc.). |
| `propertyTypeCode` | number | 🟢 High (100%) | Numeric property type code (`1`–`10`). |
| `rooms` | number | 🟢 High (~98%) | Number of rooms (`værelser`). |
| `size` | number | 🟢 High (~99%) | Living area in square meters (`boligareal m²`). |
| `lotSize` | number | 🟡 Conditional (~70%) | Land plot area in m² (`grundareal`, populated primarily for houses, villas and plots). |
| `basementSize` | number | 🟡 Conditional (~40%) | Basement area in m² (`kælderareal`) when present. |
| `floor` | string | 🟡 Conditional (~35%) | Floor level for apartments (`etage`). |
| `buildYear` | number | 🟢 High (~98%) | Construction year (`opførelsesår`). |
| `energyClass` | string | 🟡 Conditional (~85% active) | Danish energy label (`energimærke`, `A`–`G`), populated mainly on active listings. |

***

### 🔁 Pagination & 1,000+ scaling

The **Boliga scraper** handles deep pagination automatically across both active and sold datasets:

1. **50-record high-speed page stepping** — fetches 50 properties per page and inspects the total page count (`totalPages`) to advance through page `1, 2, 3...` until `maxItems` is reached or all pages are collected.
2. **Multi-zip sold-price fan-out** — when scraping `sold` prices across multiple postal codes (`zipAreas` / `zipCodes`), the actor automatically creates a dedicated paginated stream per postal code and merges all records.
3. **Resilient deduplication** — tracks every seen property `id` (with an `address|price|soldDate` fallback) and tolerates overlapping pages without stopping early, ensuring you can cleanly extract **1,000+ to 50,000+ unique Danish properties** in a single run.

***

### ⚙️ How to use

1. Open **Boliga Scraper** in **Apify Console** and click **Try for free**.
2. Select **Listing type**: *For sale (til salg)* or *Sold prices (solgte boliger)*.
3. (Optional) Pick Danish cities/areas in **Areas / cities** (`zipAreas`) or type postal codes into **More zip codes** (`zipCodes`).
4. (Optional) Filter by **Property types**, **Price (DKK)**, **Rooms**, **Living area (m²)**, **Build year**, or **Sold year**.
5. Set **Max items (`maxItems`)** to your desired dataset size (e.g. `100`, `1000`, `5000`, or `0` for unlimited).
6. Click **Start** and export your results to **JSON, CSV, Excel, XML**, or consume them via the **Apify API**.

***

### 💡 Pro tips for best results

- **Filter out family sales (`Fam. Salg`) for valuations:** When building comparable-sales models from `sold` records, filter your exported dataset to `salesType === "Alm. Salg"` so below-market family transfers do not skew your DKK/m² averages.
- **Target specific postal codes for deep sold history:** Denmark's nationwide sold archive contains hundreds of thousands of records going back decades. Combine `zipAreas` / `zipCodes` with `soldYearMin` (e.g. `2022`) and `maxItems` to keep runs fast and focused.
- **Property type code quick reference:**
  - `1` = Villa (Detached house)
  - `2` = Rækkehus (Townhouse / terraced house)
  - `3` = Ejerlejlighed (Owner-occupied apartment / condo)
  - `4` = Fritidshus (Holiday / summer house)
  - `5` = Andelsbolig (Housing cooperative)
  - `6` = Landejendom (Country / agricultural property)
  - `7` = Helårsgrund (Year-round building plot)
  - `8` = Fritidsgrund (Holiday building plot)
  - `9` = Villalejlighed (Villa apartment)

***

### ⚠️ Honest limitations

- **Mode-specific fields:** `soldDate` and `salesType` exist only on historical `sold` records (`null` on active listings), whereas `daysForSale` and `isForeclosure` exist only on active `forSale` listings (`null` on sold records).
- **Energy class on historical sales:** `energyClass` is populated on almost all active `forSale` listings (~90%+), but is often `null` on historical `sold` records where the Danish property register does not include an energy certificate.
- **Empty-run safety cap:** If you start the actor with `maxItems: 0` and zero filters/URLs, it caps the preview run at `150` properties so an accidental blank run does not crawl the entire country. Set `maxItems` explicitly (e.g. `1000` or `5000`) or add any filter to scrape as many records as you want.

***

### ❓ FAQ

#### Does this scraper extract historical sold prices (`solgte boliger`)?

**Yes.** Set `listingType` to `"sold"` to extract historical Danish property sale prices, exact sale dates (`soldDate`), sale types (`salesType`), and square-meter prices (`squareMeterPrice`).

#### How many properties can I scrape in a single run, and how fast?

You can collect **1,000+ to 50,000+ properties** per run. Because the actor fetches 50 listings per page over lightweight HTTP requests, 1,000 properties typically complete in well under a minute.

#### Do I need a Boliga login or account?

No login or account is required. Pre-configured **Danish (`DK`) residential proxies** are used by default to ensure smooth, uninterrupted pagination.

#### What is the difference between `Alm. Salg`, `Fam. Salg`, and `Auktion`?

In `sold` mode, `salesType` tells you how the property was transferred:

- `Alm. Salg` — Regular open-market transaction.
- `Fam. Salg` — Transfer between family members (frequently priced up to 15% below public valuation).
- `Auktion` — Forced/foreclosure auction sale.

#### Can I scrape all of Denmark at once?

Yes — leave `zipAreas` and `zipCodes` empty, and set `maxItems` to your desired cap (e.g. `2000` or `10000`), or add a price/type/year filter.

#### Can I schedule recurring runs or connect to Google Sheets / BigQuery?

Yes. Use **Apify Schedules** to run the scraper daily or weekly, and connect the output to **Google Sheets, Make, Zapier, Slack, BigQuery, Snowflake, or webhooks** to track new listings (`forSale`) or newly recorded transactions (`sold`).

***

### 💻 API & programmatic integration

Run **Boliga Scraper** programmatically from Python, Node.js, or any HTTP client via the **Apify REST API**.

#### cURL example

```bash
curl -X POST "https://api.apify.com/v2/acts/haketa~boliga-scraper/runs?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "listingType": "sold",
    "zipAreas": ["2100", "2200", "8000"],
    "soldYearMin": 2023,
    "maxItems": 1000
  }'
```

#### Python (`apify-client`) example

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")

run_input = {
    "listingType": "forSale",
    "zipAreas": ["1050", "2000", "2100"],
    "propertyTypes": ["1", "3"],
    "maxItems": 1000,
}

run = client.actor("haketa/boliga-scraper").call(run_input=run_input)

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(f"{item.get('address')}, {item.get('zipCode')} {item.get('city')} — {item.get('price')} DKK ({item.get('squareMeterPrice')} DKK/m²)")
```

#### Node.js (`apify-client`) example

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });

const run = await client.actor('haketa/boliga-scraper').call({
    listingType: 'sold',
    zipAreas: ['8000'],
    soldYearMin: 2024,
    maxItems: 1000,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(`Fetched ${items.length} Danish property records from Boliga.`);
```

***

### 🔗 Related scrapers

Scraping real estate in other European and global markets? Pair the **Boliga scraper** with:

- [FazWaz Thailand Property Scraper](https://apify.com/haketa/fazwaz-thailand-property-scraper) — Thailand sale & rent listings, THB prices, rental yields and GPS.
- [ImmobilienScout24 Scraper](https://apify.com/haketa/immobilienscout24-scraper) — Germany's leading property portal.
- [Daft.ie Scraper](https://apify.com/haketa/daft-scraper) — Irish property listings for sale and rent.
- [Yad2 Scraper](https://apify.com/haketa/yad2-scraper) — Israel's #1 real estate and classifieds marketplace.
- [ZAP Imóveis Scraper](https://apify.com/haketa/zapimoveis-scraper) — Brazilian real estate listings.
- [Domain.com.au Scraper](https://apify.com/haketa/domain-com-au-scraper) — Australian property listings and sold prices.

***

### 📋 Changelog

- **v1.2 (2026-11)** — Enhanced 1,000+ multi-page pagination resilience across overlapping postal-code searches, added automatic conversion for standard `boliga.dk` web search URLs in `startUrls`, and expanded documentation.
- **v1.1 (2026-11)** — Added multi-zip range seeding (`zipcodeFrom`/`zipcodeTo`) for `sold` mode, normalized `salesType` and positive `id` resolution (`id`/`estateId`/`ouId`).
- **v1.0 (2026-06)** — Initial release supporting both active Danish listings (`forSale`) and historical sold prices (`sold`).

# Changelog

This Actor's version history is a separate document: https://apify.com/haketa/boliga-scraper/changelog.md

# Actor input Schema

## `listingType` (type: `string`):

Scrape homes currently FOR SALE (til salg), or historical SOLD prices (solgte boliger / salgspriser).

## `zipAreas` (type: `array`):

Pick one or more Danish areas to target. Leave empty to cover all of Denmark. Need a postal code that is not in the list? Use the "More zip codes" box below.

## `zipCodes` (type: `string`):

Optional. Add extra Danish postal codes not in the area list above, comma-separated — e.g. "2100,2200,8000". Combined with the areas you picked.

## `propertyTypes` (type: `array`):

Which dwelling types to include. Empty = all types.

## `priceMin` (type: `integer`):

Minimum price in Danish kroner.

## `priceMax` (type: `integer`):

Maximum price in Danish kroner.

## `roomsMin` (type: `integer`):

Minimum number of rooms.

## `roomsMax` (type: `integer`):

Maximum number of rooms.

## `sizeMin` (type: `integer`):

Minimum living area in square meters.

## `sizeMax` (type: `integer`):

Maximum living area in square meters.

## `buildYearMin` (type: `integer`):

Earliest construction year.

## `buildYearMax` (type: `integer`):

Latest construction year.

## `soldYearMin` (type: `integer`):

Only include homes sold in or after this year. Ignored for homes for sale.

## `soldYearMax` (type: `integer`):

Only include homes sold in or before this year. Ignored for homes for sale.

## `startUrls` (type: `array`):

Optional. Paste ready-made Boliga search URLs (for sale or sold search pages). When provided, these take priority and the filters above are ignored.

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

Stop after collecting this many properties (0 = no limit). Sold searches can return hundreds of thousands of records, so set a limit unless you really want everything.

## `proxyConfiguration` (type: `object`):

Recommended. Boliga rate-limits direct traffic; a Danish (DK) residential proxy gives the most reliable results.

## Actor input object example

```json
{
  "listingType": "forSale",
  "zipAreas": [],
  "zipCodes": "2100,8000",
  "propertyTypes": [],
  "startUrls": [],
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "DK"
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

Active listings and sold prices collected from Boliga.

# 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 = {
    "maxItems": 100,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "DK"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("haketa/boliga-scraper").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 = {
    "maxItems": 100,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "DK",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("haketa/boliga-scraper").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 '{
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "DK"
  }
}' |
apify call haketa/boliga-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,haketa/boliga-scraper"
        }
    }
}
```

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/WPig0v9cfw83QzQkY/builds/fpn4bgXflhi9YeGyj/openapi.json
