# Boligsiden Property Listings Scraper (`automation-lab/boligsiden-property-listings-scraper`) Actor

Export public Boligsiden property listings by municipality and property type, with asking prices, monthly expenses, days on market, coordinates and broker provenance.

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

## Pricing

from $1.44 / 1,000 item processeds

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

## Boligsiden Property Listings Scraper

Export **Boligsiden property listings** as structured snapshots for Danish housing inventory analysis. Filter by municipality and property type, then download asking prices, monthly expenses, market duration, coordinates and broker provenance.

Built for property analysts, brokers and proptech teams comparing current advertised inventory. Each dataset row represents one source case ID, not a sale transaction or a private owner.

### Why use this scraper?

Get a focused, typed listing dataset without maintaining source API pagination. The Actor deduplicates case IDs within a run, checks that returned records match your filters, and excludes private-owner details, historical registrations and image collections. No login or proxy configuration is required.

### Who is it for?

Property analysts can compare municipality-level asking prices; brokers can inspect current competing inventory; data teams can ingest listing snapshots keyed by case ID. This is a snapshot exporter, not an automated valuation model.

### Getting started

1. Enter a source municipality slug such as `koebenhavn` or `nyborg`, or leave it unset for nationwide results.
2. Choose a property type, or leave it unset for all types.
3. Set `maxItems` to your desired record ceiling.
4. Run the Actor and open the Listings dataset.
5. Export JSON, CSV or Excel from Apify Console.

Example:

```json
{"municipality":"koebenhavn","propertyType":"condo","maxItems":100,"maxPages":100}
```

### Inputs

| Field | Meaning |
|---|---|
| `municipality` | Exact lowercase source slug, e.g. `koebenhavn`, `aarhus`, `nyborg`. Optional; omit for all municipalities. |
| `propertyType` | Exact source category. Optional; omit for all types. Combined with municipality using AND. |
| `maxItems` | Maximum unique records per run; default 100, valid range 1–10000. Zero is invalid. |
| `maxPages` | Global API page cap; default 100, valid range 1–200. |

Property types: `villa`, `condo`, `terraced house`, `holiday house`, `cooperative`, `farm`, `hobby farm`, `full year plot`, `holiday plot`.

Property types match the exact source record classification, not substring search. Boligsiden search groups overlap (for example, villa searches can return farms). The Actor excludes records of other types from the dataset. In particular, the apartment category omits villa-apartment records. Page limits include pages containing excluded records. Municipality uses an exact source slug, not a translated city name. A syntactically valid but unknown municipality may return no records. Unknown input fields and malformed limits fail validation rather than being ignored.

### Boligsiden property listings output

| Field | Meaning |
|---|---|
| `caseId` | Stable source listing case identifier; useful for joining snapshots. |
| `address` | Public listing address assembled from source components. |
| `municipality` | Source municipality slug. |
| `propertyType` | Source property category. |
| `askingPriceDkk` | Advertised asking price, DKK. |
| `pricePerSquareMeterDkk` | Source asking price per square meter, DKK. |
| `monthlyExpenseDkk` | Source monthly expense; not total financing or ownership cost. |
| `daysOnMarket` | Source current market-duration value in days. |
| `livingArea` | Housing area in square meters. |
| `latitude`, `longitude` | Source geographic coordinates. |
| `broker` | Public estate agency name. |
| `brokerUrl` | Listing link supplied by the broker/source. |
| `sourceUrl` | Public Boligsiden address-page link. |
| `scrapedAt` | UTC snapshot timestamp. |

Missing optional source values are `null`, not inferred. Advertised asking prices are not completed sale prices. The default dataset contains only listings; run counters are stored in `SUMMARY` in the default key-value store.

### Output example

Illustrative formatting using fields returned by the source; address and identifiers are anonymized:

```json
{
  "caseId":"00000000-0000-4000-8000-000000000001",
  "address":"Examplevej 9 5853 Ørbæk",
  "municipality":"nyborg",
  "propertyType":"villa",
  "askingPriceDkk":1450000,
  "pricePerSquareMeterDkk":12787,
  "monthlyExpenseDkk":1523,
  "daysOnMarket":25,
  "livingArea":111,
  "latitude":55.26268,
  "longitude":10.676296,
  "broker":"Example agency",
  "brokerUrl":"https://example.com/listing",
  "sourceUrl":"https://www.boligsiden.dk/adresse/example",
  "scrapedAt":"2026-10-25T00:00:00.000Z"
}
```

### Pagination and limits

The Actor fetches sequential API pages of up to 50 records with a constant page size and stops at the first of: `maxItems`, `maxPages`, source exhaustion or billing limit. Deduplication applies within the run only. Source order is used; listings can change while pagination is in progress, so this is not an atomic or exhaustive historical export.

A smaller page cap can produce fewer records than `maxItems`. `SUMMARY.pageCapReached` flags a reached page ceiling when the item ceiling was not met. Use the dataset as the authoritative saved output if a run was interrupted.

### How much does it cost to export Boligsiden property listings?

Pay per event: a one-time **$0.005 start fee**, plus **$0.002392 per item on BRONZE**. Item rates are FREE $0.0027508, SILVER $0.0018658, and GOLD/PLATINUM/DIAMOND $0.0014352. Tiers reflect aggregate qualifying monthly Store spend, not a private per-Actor volume discount.

BRONZE estimated run totals (start fee included):

| Accepted records | Estimated total USD |
|---|---|
| 10 | 0.02892 |
| 100 | 0.2442 |
| 1000 | 2.397 |

These are event-price estimates, not guaranteed invoices. Empty results incur the start fee but no listing event. Invalid inputs rejected before work incur no custom start charge. The `item` event represents one unique accepted listing row; images, retries and summary counters have no separate event charge.

Set Apify's maximum total charge to bound spending. A billing stop may yield fewer rows than requested. Prices refer to Actor event charges; consult Apify for your platform plan terms.

### Integrations

Export CSV to a spreadsheet for municipality-level asking-price comparisons. Send JSON to a database keyed by `caseId` plus snapshot date. Use Apify schedules for repeat snapshots and compare them in your own pipeline; the Actor does not itself maintain change history or issue alerts.

Connect completion webhooks to Make, Zapier or your own ingestion endpoint. Keep full exports in storage rather than injecting large datasets into an AI conversation.

### API usage

Replace the token placeholder with your Apify token kept outside source control:

```bash
curl -X POST 'https://api.apify.com/v2/acts/automation-lab~boligsiden-property-listings-scraper/run-sync-get-dataset-items' \
  -H 'Authorization: Bearer YOUR_APIFY_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"municipality":"nyborg","propertyType":"villa","maxItems":10}'
```

JavaScript:

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/boligsiden-property-listings-scraper')
  .call({ municipality: 'nyborg', propertyType: 'villa', maxItems: 10 });
const { items } = await client.dataset(run.defaultDatasetId).listItems({ limit: 10 });
console.log(items);
```

Python:

```python
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/boligsiden-property-listings-scraper').call(
    run_input={'municipality': 'nyborg', 'propertyType': 'villa', 'maxItems': 10})
print(client.dataset(run['defaultDatasetId']).list_items(limit=10).items)
```

### MCP and AI workflows

Use the Actor-specific endpoint:

```bash
claude mcp add --transport http apify \
  'https://mcp.apify.com?tools=automation-lab/boligsiden-property-listings-scraper'
```

Claude Desktop, Cursor, and VS Code configuration (for clients supporting remote HTTP MCP):

```json
{"mcpServers":{"apify":{"url":"https://mcp.apify.com?tools=automation-lab/boligsiden-property-listings-scraper"}}}
```

Authenticate through your client's supported Apify authorization flow. Discover actual tool names and input schemas using `tools/list`. Actor selection also exposes run/data helpers; it does not expose exactly one tool or grant permission to execute it.

Example prompt: “Export 20 Nyborg villas, then compare advertised prices and monthly expenses. Do not infer sale prices.”

For an authorized call, start once and retain the run and dataset IDs. A call can return while work is still running. Follow the same run with bounded 2/4/8-second backoff capped at 10 seconds, with a 120-second overall deadline. Set client timeout above the chosen server wait plus transport margin. Recover the known run after a timeout rather than restarting it. At the deadline, report pending and its ID; terminal failures mean any rows are partial.

After success, read dataset pages of 20 rows with explicit offsets and needed fields, at most 100 source rows and 64 KiB of serialized UTF-8 content admitted to model context. Enforce that byte budget host-side before model injection, including oversized rows; disclose omissions and the continuation offset. If the client cannot intercept response sizes, do not claim a hard byte guarantee. Full exports stay outside model context. Discovery/cache behavior depends on the client; no universal token-savings claim is made.

### FAQ and troubleshooting

Network timeouts, HTTP 429 and temporary server errors get at most three attempts with backoff. Permanent HTTP errors, unexpected response shapes and filter mismatches fail the run. No automatic paid proxy or browser fallback is enabled.

**Why did I get zero rows?** Check the exact municipality slug and category. A valid empty source response is not an error. An HTML challenge or malformed response is an error, not a successful empty export.

**Why fewer rows than requested?** The source may be exhausted, the page cap or billing ceiling may have been reached, or listings may have changed during pagination. Check run status and `SUMMARY`.

**Can I paste search URLs?** No. This Actor accepts only the documented structured filters, not arbitrary URLs, case IDs or historical-sale queries.

### Legality and responsible use

This Actor reads public listing information. It is not affiliated with Boligsiden and does not grant rights to republish source content. Review applicable source terms, data protection requirements and your intended use. Do not use public property addresses to infer private owners or sensitive personal attributes.

No private-owner records, sold-price history, listing images, valuations, rental-specific contract data, alerts or guaranteed full-market history are included. Availability and field coverage can change upstream.

### Diagnostics and privacy

Failed operations send sanitized diagnostic input, exceptions and Actor/build/run IDs to our private GlitchTip service for repair. Secret fields and URL query strings are removed; reports are retained for 30 days. Dataset rows are not sent as diagnostic payloads. The Actor uses no runtime AI provider. Apify retains datasets, inputs and logs under your account's storage policy; delete them through Apify's storage controls when no longer needed. No persistent private cache or session store is maintained.

For help, open an issue on this Actor's Apify Store page with the run link and non-sensitive input. Never post tokens or private credentials.

### Related workflows

This is a standalone Denmark listing dataset. Other national property portals have different schemas and coverage; do not treat them as additional Boligsiden inventory. Use your own downstream joins for cross-country analysis.

# Changelog

This Actor's version history is a separate document: https://apify.com/automation-lab/boligsiden-property-listings-scraper/changelog.md

# Actor input Schema

## `municipality` (type: `string`):

Exact lowercase Boligsiden municipality slug, e.g. koebenhavn, aarhus or nyborg. Omit for all municipalities; Danish letters use source transliteration.

## `propertyType` (type: `string`):

Exact source record classification. Search groups can include other types (e.g. farms in villa searches); these are excluded locally before saving or charging. condo excludes villa apartment. Omit for all types. Combined with municipality using AND. Page limits count every fetched page, including excluded records.

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

Global maximum unique listing records saved per run, after deduplication. Default 100, range 1–10000; zero is invalid. Collection may stop sooner at source exhaustion, maxPages or the billing limit.

## `maxPages` (type: `integer`):

Global page cap, 1–200, default 100. Pages contain up to 50 records. A page cap can yield fewer than maxItems. No unlimited mode.

## Actor input object example

```json
{
  "municipality": "koebenhavn",
  "propertyType": "condo",
  "maxItems": 20,
  "maxPages": 20
}
```

# Actor output Schema

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

One row per unique source case ID.

## `summary` (type: `string`):

Requests, pages, bytes, retries, unique candidates and actually saved listing count.

# 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 = {
    "municipality": "koebenhavn",
    "propertyType": "condo",
    "maxItems": 20,
    "maxPages": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/boligsiden-property-listings-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 = {
    "municipality": "koebenhavn",
    "propertyType": "condo",
    "maxItems": 20,
    "maxPages": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/boligsiden-property-listings-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 '{
  "municipality": "koebenhavn",
  "propertyType": "condo",
  "maxItems": 20,
  "maxPages": 20
}' |
apify call automation-lab/boligsiden-property-listings-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/boligsiden-property-listings-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/hxs7PlODXJgTg0NWA/builds/emvFRDena3E9piFwT/openapi.json
