# Boligsiden Scraper - Danish Real Estate Listings & Sold Prices (`studio-amba/boligsiden-scraper`) Actor

Scrape property listings and historical sold prices from Boligsiden.dk, Denmark's #2 real estate portal. Extract prices, locations, sizes, rooms, energy labels, and realtor details for houses, apartments, and holiday homes across Danish municipalities. No login needed.

- **URL**: https://apify.com/studio-amba/boligsiden-scraper.md
- **Developed by:** [Studio Amba](https://apify.com/studio-amba) (community)
- **Categories:** Real estate
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 result 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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Boligsiden Scraper

Extract property listings and historical sold prices from [Boligsiden.dk](https://www.boligsiden.dk), Denmark's #2 real estate portal. This actor pulls prices, sizes, rooms, energy labels, monthly costs, images, and realtor details for houses, apartments, and holiday homes across Danish municipalities — plus Boligsiden's registered sold-price history (solgte), not just current asking prices.

### Why use this actor?

Boligsiden aggregates listings from essentially every Danish realtor and publishes the country's official land-registry sold-price history (Tinglysningen) alongside current listings. That combination — live asking prices plus verified historical sale amounts down to street level — makes it useful for price-trend analysis, comparable-sale research, and market-entry due diligence in a way a for-sale-only feed can't match.

Use cases include market analysis by kommune, price-per-square-meter benchmarking, relocation and mortgage-appraisal research, academic housing studies, and competitor tracking for proptech and real estate agency tools.

### How to scrape Boligsiden data

1. Go to the actor's page on the Apify Store
2. Choose a kommune (Copenhagen, Aarhus, Odense, Aalborg, Esbjerg, Kolding, or all of Denmark) or paste a Boligsiden.dk search URL
3. Pick search type: for-sale listings (tilsalg) or sold prices (solgte)
4. Click "Start" to run the scraper
5. Download results as JSON, CSV, or Excel when complete

The actor fetches pages through Bright Data's Web Unlocker to get past Boligsiden.dk's Cloudflare managed challenge, then extracts data from the site's own server-rendered React payload for maximum reliability — no fragile CSS scraping for the fields that matter most.

### Input

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `searchType` | String | No | `forSale` (tilsalg, default) or `sold` (solgte, historical sold prices) |
| `kommune` | String | No | `denmark` (whole country, default — for-sale only), `koebenhavn`, `aarhus`, `odense`, `aalborg`, `esbjerg`, or `kolding` |
| `customKommuneSlug` | String | No | Advanced: any of Denmark's other 92 kommune URL slugs. Overrides `kommune`. |
| `startUrls` | Array | No | Boligsiden.dk search result pages, e.g. `https://www.boligsiden.dk/kommune/aarhus/tilsalg`. Overrides `kommune`/`searchType`. |
| `maxResults` | Integer | No | Maximum listings to return. For-sale pages return 50 listings each, sold-price pages return 20 each; the actor paginates automatically. |
| `brightDataApiKey` | String | No | Your own Bright Data API key for the Web Unlocker zone. Leave empty to use the built-in unlocking service. |

### Output

Each result contains:

| Field | Type | Example |
|-------|------|---------|
| `title` | String | `"George Marshalls Vej 29, København SV"` |
| `price` | Number | `12750000` |
| `currency` | String | `"DKK"` |
| `listingType` | String | `"forSale"` or `"sold"` |
| `propertyType` | String | `"villa"`, `"condo"`, `"terraced_house"`, `"holiday_house"` |
| `address` | String | `"George Marshalls Vej 29"` |
| `city` | String | `"København SV"` |
| `postalCode` | String | `"2450"` |
| `municipality` | String | `"København"` |
| `province` | String | `"Region Hovedstaden"` |
| `latitude` / `longitude` | Number | `55.6395` / `12.5296` |
| `rooms` | Number | `5` (for-sale listings only — sold-price records don't carry this field) |
| `surface` | Number | `142` (housing/living area, m²) |
| `landSurface` | Number | `650` (lot area, m² — for-sale listings only) |
| `buildYear` | Number | `1998` (for-sale listings only) |
| `epcScore` | String | `"C"` (Danish energy label, for-sale listings only) |
| `pricePerSqm` | Number | `31250` |
| `monthlyExpense` | Number | `2450` (DKK/month — for-sale listings only) |
| `priceChangePercentage` | Number | `-2.5` (for-sale listings only) |
| `soldDate` | String | `"2026-08-25"` (sold listings only) |
| `imageUrl` | String | Primary listing photo URL (for-sale listings only) |
| `imageUrls` | Array | All listing photo URLs (for-sale listings only) |
| `agencyName` | String | `"Nybolig"` (for-sale listings only) |
| `boligsidenId` | String | `"f9bbe9b4-d0e1-44dc-97c1-fc6d3e520530"` |
| `url` | String | Full Boligsiden.dk address page URL |
| `scrapedAt` | String | ISO 8601 timestamp |

### Example output

```json
{
    "title": "George Marshalls Vej 29, København SV",
    "price": 12750000,
    "currency": "DKK",
    "listingType": "forSale",
    "propertyType": "villa",
    "address": "George Marshalls Vej 29",
    "city": "København SV",
    "postalCode": "2450",
    "municipality": "København",
    "rooms": 6,
    "surface": 210,
    "landSurface": 480,
    "buildYear": 1936,
    "epcScore": "D",
    "pricePerSqm": 60714,
    "monthlyExpense": 3100,
    "priceChangePercentage": 0,
    "imageUrl": "https://images.boligsiden.dk/images/case/.../600x400/....webp",
    "agencyName": "Home",
    "boligsidenId": "6a1b2c3d-....",
    "url": "https://www.boligsiden.dk/adresse/george-marshalls-vej-29-2450-koebenhavn-sv",
    "scrapedAt": "2026-08-28T15:10:00.000Z"
}
```

### Cost estimate

This actor fetches every page through Bright Data's Web Unlocker (one request per search page, not a full browser session). Approximate costs:

- **~50 for-sale results (1 search page)**: $0.01-0.03 in platform credits
- **~500 for-sale results (10 search pages)**: $0.10-0.30 in platform credits
- **~20 sold-price results (1 search page)**: $0.01-0.03 in platform credits (sold pages return fewer results per request, so cost per result is proportionally higher)

Actual usage cost only settles once the run reports SUCCEEDED — reading the dataset from a still-running run will undercount what you'll actually be charged.

### Tips for best results

- **Start small** — test with `maxResults: 20` before running large scrapes.
- **Sold-price search always needs a specific kommune.** Boligsiden.dk has no working nationwide sold-price listing page — only `/kommune/{slug}/solgte` returns real data. If you set `searchType: sold` with `kommune: denmark`, the actor logs a warning and falls back to Copenhagen automatically.
- **`customKommuneSlug` unlocks any of Denmark's 98 kommuner** — the curated `kommune` list covers the six largest markets; for anywhere else, browse `boligsiden.dk/kommune/{slug}/tilsalg` and copy the slug from the URL.
- **For-sale listings carry far more fields than sold listings** — rooms, energy label, build year, images, and realtor are only present on the current-listing record, not on historical sale registrations.

### Limitations

- Boligsiden.dk fronts every page with a genuine Cloudflare managed challenge. This actor routes requests through Bright Data's Web Unlocker to get past it.
- Free-text (`?q=`) and zip-code (`?zipCodes=`) query parameters don't filter results server-side on this site — only the `/kommune/{slug}/...` URL path reliably scopes a search, so this actor uses that path pattern rather than query-string filters.
- **`rooms`, `landSurface`, `buildYear`, `epcScore`, `imageUrl(s)`, and `agencyName` are always null/absent on sold listings.** These live on the original for-sale listing record, not on the land-registry sale registration Boligsiden shows for historical sales — this is a real data-model difference on the site, not a scraping gap.
- **Sold prices reflect the most recent verified land-registry sale registration for an address.** Some addresses carry additional non-arm's-length registration records (e.g. family transfers, foreclosures) alongside ordinary sales — the actor prefers the most recent ordinary-sale registration and skips registration types the site itself flags as non-standard, to avoid reporting a non-market placeholder amount as the sale price.
- Price data on for-sale listings reflects Boligsiden's own asking price, not a guaranteed final sale price.
- The actor scrapes the public website. No login or authentication is used.

### Related scrapers

- [Booli.se Scraper](https://apify.com/itsnotyouitsme/booli-scraper) — Sweden's #2 real estate portal
- [Imobiliare.ro Scraper](https://apify.com/itsnotyouitsme/imobiliare-scraper) — Romanian real estate listings
- [Immoweb Scraper](https://apify.com/itsnotyouitsme/immoweb-scraper) — Belgium's #1 real estate portal
- [Idealista Scraper](https://apify.com/itsnotyouitsme/idealista-scraper) — Spanish real estate portal
- [Zimmo Scraper](https://apify.com/itsnotyouitsme/zimmo-scraper) — Belgian real estate listings
- [Aruodas Scraper](https://apify.com/itsnotyouitsme/aruodas-scraper) — Lithuanian real estate listings

### Need this data on a schedule, or a custom version?

We run this scraper as a managed service for businesses: scheduled runs,
deduplication, delta detection, and delivery to your inbox, Google Sheets,
or API — maintenance included. We can also build a custom version with your
exact fields and filters, or combine multiple sources into one feed.

See [studioamba.dev/services](https://studioamba.dev/services/) or email
<hello@studioamba.dev> for a free data sample.
We maintain 700+ European web scrapers and answer within one business day.

# Actor input Schema

## `searchType` (type: `string`):

For-sale listings (tilsalg) or historical sold prices (solgte). Note: nationwide sold-price search isn't available on boligsiden.dk — sold searches always require a specific Kommune below.

## `kommune` (type: `string`):

Danish municipality to search. 'Denmark (whole country)' only works for For Sale search — sold-price search needs a specific kommune. Use 'Custom Kommune Slug' below for any of Denmark's other 92 municipalities.

## `customKommuneSlug` (type: `string`):

Advanced: any Danish kommune's URL slug not in the list above (e.g. 'randers', 'vejle', 'horsens'). Find it by browsing boligsiden.dk/kommune/{slug}/tilsalg. Overrides the Kommune field when set.

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

Boligsiden.dk search result pages, e.g. https://www.boligsiden.dk/kommune/aarhus/tilsalg or https://www.boligsiden.dk/tilsalg. Overrides Kommune/Search Type when provided.

## `maxResults` (type: `integer`):

Maximum number of listings to scrape. Search pages return 50 listings each; the actor paginates automatically until this limit is reached.

## `brightDataApiKey` (type: `string`):

Optional: your own Bright Data API key for the Web Unlocker zone. Leave empty to use the built-in unlocking service.

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

Legacy field, kept for backwards compatibility. Fetching now goes through the built-in Bright Data Web Unlocker, so this is ignored.

## Actor input object example

```json
{
  "searchType": "forSale",
  "kommune": "denmark",
  "startUrls": [
    {
      "url": "https://www.boligsiden.dk/kommune/koebenhavn/tilsalg"
    }
  ],
  "maxResults": 50,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# 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 = {
    "startUrls": [
        {
            "url": "https://www.boligsiden.dk/kommune/koebenhavn/tilsalg"
        }
    ],
    "maxResults": 50,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("studio-amba/boligsiden-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 = {
    "startUrls": [{ "url": "https://www.boligsiden.dk/kommune/koebenhavn/tilsalg" }],
    "maxResults": 50,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("studio-amba/boligsiden-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 '{
  "startUrls": [
    {
      "url": "https://www.boligsiden.dk/kommune/koebenhavn/tilsalg"
    }
  ],
  "maxResults": 50,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call studio-amba/boligsiden-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,studio-amba/boligsiden-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/vCLCqYtyIggiphbr9/builds/Asi2xFesvKTTlPD2J/openapi.json
