# Hemnet Property Scraper (`mantisus/hemnet-property-scraper`) Actor

Scrape hemnet.se across all three sections: homes for sale, slutpriser (sold prices), and kommande (upcoming). Get price, area, rooms, monthly fee, floor, energy class, BRF details, and broker contacts. Paste a Hemnet URL or reuse one filter set across many locations. Export to CSV, JSON or Excel.

- **URL**: https://apify.com/mantisus/hemnet-property-scraper.md
- **Developed by:** [Maksym Bohomolov](https://apify.com/mantisus) (community)
- **Categories:** Real estate, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.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/platform/actors/running/actors-in-store#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

## Hemnet Property Scraper

Bulk-scrape property listings from [hemnet.se](https://www.hemnet.se), Sweden's largest housing marketplace: homes for sale, completed sales with final prices (slutpriser), and upcoming listings (kommande) announced before they go live. Feed it a Hemnet search URL, a link to a single property, or a list of Swedish place names with a filter template. You get complete listing data, down to broker contacts, amenities and floor plans.

**Search URLs and links to individual properties both work, in the same field.** Paste `hemnet.se/bostader?…` for a search, or `hemnet.se/bostad/…-21730633` for one specific home. Each link becomes one row, with no search to page through.

No headless browser. It runs on direct HTTP requests to Hemnet's own mobile API, so runs finish fast and cost a fraction of a browser-based scraper.

### Common use cases

- **Market monitoring**: daily delta of new listings in selected municipalities using the `onlyNew` flag
- **Sold-price comps**: completed sales with final price, and the gap from the asking price in both kronor and percent
- **Investment research**: price per m², monthly fee, running costs, construction year and energy class across several areas in one run
- **Broker lead generation**: name, email, phone and agency for every listing, filtered by area and property type
- **New-build tracking**: the Kommande section plus `isDevelopment` and `isNewConstruction` to catch projects before they reach the open market
- **Distressed assets**: Hemnet's own foreclosure filter is read from the for-sale URL, and `isForeclosure` marks Kronofogden (enforcement authority) sales in the output
- **Aggregator feeds**: bulk extraction for portals combining Hemnet with other sources

### How to use it

Three input formats are supported, and they can be combined in one run.

#### Option 1: paste search URLs from Hemnet

Build the search on hemnet.se with whatever filters you like: price, rooms, living area, property type, balcony, elevator, keywords, ground floor, expanded area. Then paste the address bar into `urls`. The filters and the section are both read from the URL: `/bostader` for sale, `/salda/bostader` for sold prices, `/kommande/bostader` for upcoming.

```json
{
  "urls": [
    "https://www.hemnet.se/bostader?location_ids%5B%5D=17744&item_types%5B%5D=bostadsratt&price_max=5000000",
    "https://www.hemnet.se/salda/bostader?location_ids%5B%5D=18031&sold_age=6m"
  ],
  "searchLimit": 50,
  "onlyNew": false,
  "proxySettings": { "useApifyProxy": false }
}
```

Some filters can't be applied. Hemnet's sold-price search supports fewer of them than the for-sale one, for instance. The run log then says exactly which filter was dropped. Nothing is ignored silently.

#### Option 2: paste links to individual properties

Drop them into the same `urls` field, mixed with searches if you like. A home for sale, an upcoming one and a completed sale all work, and each is fetched on its own with no search around it.

```json
{
  "urls": [
    "https://www.hemnet.se/bostad/lagenhet-2rum-centrum-halmstads-kommun-vattugatan-2a-21730633",
    "https://www.hemnet.se/salda/lagenhet-2rum-norrmalm-skovde-kommun-kansligatan-7b-1311609520551872240"
  ],
  "proxySettings": { "useApifyProxy": false }
}
```

#### Option 3: apply one filter template across many locations

When you need the same filters across many places, pass one example URL as `sampleUrl` and the location list separately. Its filters and its section are applied to every location; its own location is ignored. Without a `sampleUrl`, locations are searched for sale.

```json
{
  "locations": ["Stockholms län", "Göteborg", "Nacka kommun"],
  "sampleUrl": "https://www.hemnet.se/bostader?rooms_min=3&price_max=6000000&item_types%5B%5D=bostadsratt",
  "searchLimit": 100,
  "onlyNew": false,
  "proxySettings": { "useApifyProxy": false }
}
```

Locations are checked before anything is scraped, and named in the log (`17744 is Stockholms län (COUNTY)`). This matters because Hemnet itself ignores a location it doesn't recognise and answers with the whole country instead. So an old URL whose area has since been redrawn would quietly return all of Sweden. The actor refuses that search rather than billing you for it.

#### Other parameters

- `searchLimit`: maximum results per search, applied to each URL and each location separately. Set to 0 for all results. Ignored when `onlyNew` is enabled.
- `onlyNew`: returns only what has appeared since the previous run. New listings, or newly registered sales when the search is a `/salda` section. The first run with this flag does a full scan and records its position. Later runs stop as soon as they reach it. Works for all three sections, and the filters in your URL still apply, so a sold-date window is a good way to keep that first scan small. `searchLimit` is the one exception: it's ignored here, because a cap would move the position past listings it never returned.
- `stateStore`: key-value store holding the `onlyNew` position, one per search. Leave empty to use the auto-created `hemnet-tech-store`. Pick a separate store per monitoring task to keep their positions isolated. To reset monitoring and force a full scan, point it at a different store.
- `proxySettings`: standard Apify proxy configuration.

#### What Only New Listings guarantees

Within a run you're never charged twice for the same row.

Across runs a repeat is possible. A run cut short keeps its previous position, so the next one may re-deliver a few rows you already have. Nothing is skipped. Match on `id` if your pipeline needs to be certain.

Sales registered late by the broker can be missed. Run Sold without `onlyNew` when you need every sale.

#### Hemnet's 2 500-result window

**Hemnet serves at most 2 500 results per search.** It's the site's own limit, not the actor's.

Split a wide search into narrower ones and pass several URLs: by municipality, by price band, or by property type. The log says how many results a search left behind, so you know which one to split.

### Output

Data is saved to an Apify dataset with seven preconfigured views: **Overview**, **For Sale**, **Upcoming**, **Sold Prices**, **Location Data**, **Brokers** and **Full Data**. Each view picks the columns that section fills. **Overview**, **For Sale**, **Upcoming** and **Sold Prices** lead with a photo, so the table reads as a gallery rather than a grid of ids. Download in JSON, CSV, XML, Excel, HTML, RSS or JSONL.

Every value arrives ready to use rather than ready to display. Prices are whole kronor as integers, not `"2 195 000 kr"`. Areas and room counts are numbers, not `"70 m²"` and `"2,5 rum"`. Timestamps are ISO-8601 UTC. The dataset drops into a spreadsheet, a database or a model without a parsing step.

#### Sample output: property for sale

```json
{
  "id": "21730633",
  "searchMode": "for_sale",
  "url": "https://www.hemnet.se/bostad/lagenhet-2rum-centrum-halmstads-kommun-vattugatan-2a-21730633",
  "streetAddress": "Vattugatan 2A",
  "area": "Centrum",
  "districts": ["Mickedala", "Halmstad"],
  "municipality": "Halmstads kommun",
  "county": "Hallands län",
  "latitude": 56.6801949,
  "longitude": 12.8557949,
  "housingForm": "Lägenhet",
  "tenure": "Bostadsrätt",
  "askingPriceSek": 2195000,
  "priceOnRequest": false,
  "feeSek": 4272,
  "runningCostsSek": 3314,
  "squareMeterPriceSek": 31357,
  "numberOfRooms": 2,
  "livingAreaSqm": 70,
  "floorNumber": 2,
  "floorsInBuilding": 5,
  "constructionYear": 1989,
  "energyClass": "D",
  "publishedAt": "2026-08-02T13:17:11.123000+00:00",
  "daysOnHemnet": 0,
  "timesViewed": 0,
  "biddingStarted": false,
  "closestWaterDistanceMeters": 350,
  "coastlineDistanceMeters": 2100,
  "labels": ["Tis 11 aug kl 17:00", "Premium", "Balkong", "Hiss"],
  "adTier": "PREMIUM",
  "hasVideo": false,
  "has3dTour": false,
  "amenities": { "Balkong": true, "Hiss": true },
  "openHouses": [
    {
      "start": "2026-08-11T15:00:00+00:00",
      "end": "2026-08-11T15:30:00+00:00",
      "description": "Tidsbokad visning. Boka själv via hemsidan!",
      "isOnlyDate": false
    }
  ],
  "housingCooperative": "BRF Poppeln 16",
  "housingCooperativeRegistrationNumber": "7164082468",
  "housingCooperativeRegisteredYear": 1986,
  "mainImageUrl": "https://bilder.hemnet.se/images/itemgallery_L/6a/e4/6ae43aa3….jpg",
  "imageCount": 32,
  "imageUrls": ["https://bilder.hemnet.se/images/itemgallery_L/…", "…31 more"],
  "floorPlanImageUrls": ["https://bilder.hemnet.se/images/itemgallery_L/…"],
  "brokerName": "Claudia R…",
  "brokerEmail": "claudia.ryden@…",
  "brokerPhone": "0735-18****",
  "brokerAgencyName": "MOHV Halmstad",
  "brokerAgencyCity": "Halmstad",
  "brokerAgencyUrl": "http://www.mohv.se/"
}
```

#### Sample output: completed sale

```json
{
  "id": "1311609520551872240",
  "searchMode": "sold",
  "url": "https://www.hemnet.se/salda/lagenhet-2rum-norrmalm-skovde-kommun-kansligatan-7b-1311609520551872240",
  "listingId": "21755181",
  "streetAddress": "Kansligatan 7B",
  "districts": ["Norrmalm", "Skövde"],
  "municipality": "Skövde kommun",
  "county": "Västra Götalands län",
  "latitude": 58.39475,
  "longitude": 13.85011,
  "housingForm": "Lägenhet",
  "tenure": "Bostadsrätt",
  "askingPriceSek": 1250000,
  "finalPriceSek": 1200000,
  "priceChangeSek": -50000,
  "priceChangePercent": -4.0,
  "squareMeterPriceSek": 25532,
  "feeSek": 3218,
  "numberOfRooms": 2,
  "livingAreaSqm": 47,
  "constructionYear": 1943,
  "soldAt": "2026-08-02T10:00:00+00:00",
  "labels": ["Balkong"],
  "amenities": { "Balkong": true, "Hiss": false },
  "housingCooperative": "BRF Kansligården",
  "housingCooperativeRegistrationNumber": "7666000224",
  "housingCooperativeRegisteredYear": 1943,
  "mainImageUrl": "https://bilder.hemnet.se/images/itemgallery_L/1c/7b/1c7b7040….jpg",
  "imageCount": 21,
  "imageUrls": ["https://bilder.hemnet.se/images/itemgallery_L/…", "…20 more"]
}
```

#### Sample output: upcoming listing

```json
{
  "id": "21760410",
  "searchMode": "upcoming",
  "url": "https://www.hemnet.se/bostad/lagenhet-2rum-bromma-blackeberg-stockholms-kommun-holbergsgatan-90-21760410",
  "streetAddress": "Holbergsgatan 90",
  "area": "Bromma - Blackeberg",
  "districts": ["Blackeberg", "Bromma", "Bromma, västra", "Stockholm"],
  "municipality": "Stockholms kommun",
  "county": "Stockholms län",
  "latitude": 59.34639,
  "longitude": 17.8809948,
  "housingForm": "Lägenhet",
  "tenure": "Bostadsrätt",
  "askingPriceSek": 2295000,
  "feeSek": 4158,
  "runningCostsSek": 6000,
  "squareMeterPriceSek": 46837,
  "numberOfRooms": 2,
  "livingAreaSqm": 49,
  "floorNumber": 6,
  "floorsInBuilding": 9,
  "constructionYear": 2006,
  "energyClass": "E",
  "isUpcoming": true,
  "markedAsNew": true,
  "description": "Välkommen till denna smakfulla och välplanerade tvårummare på våning 6…"
}
```

Every row carries `searchMode`: `"for_sale"`, `"upcoming"` or `"sold"`. Group on it to split a mixed run back into its sections.

The full field list, with a description and a unit for every field, is in the dataset schema on the Storage tab after each run.

### Is it legal to scrape Hemnet?

This scraper accesses only data that is publicly visible on hemnet.se without authentication. It doesn't sign in. It doesn't bypass paywalls. It doesn't try to reach anything Hemnet keeps behind one.

Scraping publicly available data is generally legal. See Apify's overview: [Is web scraping legal? Yes, if you know the rules](https://blog.apify.com/is-web-scraping-legal/). You're responsible for your own use complying with Hemnet's Terms of Service and applicable law.

Three fields are personal data under the GDPR, even though Hemnet publishes them: `brokerName`, `brokerEmail` and `brokerPhone`. Storing or processing them needs a lawful basis. If you don't need them, download any view except **Brokers** and **Full Data**, or drop the columns with the `omit` parameter.

Agency-level contacts are organisational data, not personal data: `brokerAgencyName`, `brokerAgencyCity`, `brokerAgencyPhone`, `brokerAgencyEmail` and `brokerAgencyUrl` describe a business, not an individual.

### Bugs and feedback

If something doesn't work or a field is missing, the [Issues tab](https://console.apify.com/actors/L3X7P2r8o1htoxiUo/issues) is the fastest way to reach me.

# Actor input Schema

## `urls` (type: `array`):

Hemnet search pages with their filters already applied, and links to individual properties. A search URL also decides the section: /bostader for sale, /salda/bostader for sold prices, /kommande/bostader for upcoming. A link to one property is scraped on its own, with no search around it.

## `locations` (type: `array`):

Swedish place names to search. Combine with a Sample URL below to apply filters and to choose the section.

## `sampleUrl` (type: `string`):

A Hemnet URL whose filters and section are applied to every Location search. Its own location is ignored. Without it, Locations are searched for sale.

## `searchLimit` (type: `integer`):

Maximum number of results per search. Applied to each URL and each location separately. Set to 0 for no cap from the actor. Hemnet itself serves at most 2500 results per search, so 0 gets you 2500 at most: split a wider search into narrower ones and pass several URLs. Ignored when Only New Listings is enabled.

## `proxySettings` (type: `object`):

Select proxies to be used by your scraper.

## `onlyNew` (type: `boolean`):

Only retrieve what has appeared since the last run: new listings, or newly registered sales when the search is a /salda section. The first run with this flag does a full scan. Result Limit is ignored here, so that no new listing is missed. The last-seen position per search is stored in a key-value store (see State Store below), which persists across runs.

## `stateStore` (type: `string`):

Key-value store holding the Only New Listings position. Leave empty to use a store named "hemnet-tech-store", created automatically. Pick a separate store per monitoring task to keep their positions isolated. To reset monitoring and force a full scan, point it at a different store.

## Actor input object example

```json
{
  "urls": [
    "https://www.hemnet.se/bostader?location_ids%5B%5D=17744&item_types%5B%5D=bostadsratt&price_max=5000000",
    "https://www.hemnet.se/salda/bostader?location_ids%5B%5D=18031",
    "https://www.hemnet.se/kommande/bostader?location_ids%5B%5D=17744",
    "https://www.hemnet.se/bostad/lagenhet-3rum-vastra-orminge-nacka-kommun-betsovagen-10-21761515",
    "https://www.hemnet.se/salda/lagenhet-2rum-norrmalm-skovde-kommun-kansligatan-7b-1311609520551872240"
  ],
  "locations": [
    "Stockholms län",
    "Göteborg",
    "Nacka kommun"
  ],
  "sampleUrl": "https://www.hemnet.se/bostader?rooms_min=3&price_max=6000000&item_types%5B%5D=bostadsratt",
  "searchLimit": 50,
  "proxySettings": {
    "useApifyProxy": false
  },
  "onlyNew": false
}
```

# Actor output Schema

## `overview` (type: `string`):

Photo, address, price, size and dates. The columns every section fills, for a first look at what a run returned.

## `forSale` (type: `string`):

Listings on the market, with fees, floor, energy class, days on Hemnet, view count, bidding and open houses. The view to work from when pricing or shortlisting.

## `upcoming` (type: `string`):

Listings announced before they go live, so the columns stop at what Kommande states. Use it to reach a seller ahead of the open market.

## `sold` (type: `string`):

Completed sales with the final price (slutpris), the sale date, and the gap from the asking price in kronor and percent. The view for comps.

## `location` (type: `string`):

Address, districts, municipality, county, coordinates and distance to water. For mapping, or for joining a run onto your own geography.

## `broker` (type: `string`):

Every listing's broker and agency: name, email, phone, city and website. The view to download for lead generation.

## `full` (type: `string`):

Every field, in the order the row is built. The one to take when you plan to transform the data yourself.

# 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 = {
    "urls": [
        "https://www.hemnet.se/bostader?location_ids%5B%5D=17744&rooms_min=3"
    ],
    "proxySettings": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("mantisus/hemnet-property-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 = {
    "urls": ["https://www.hemnet.se/bostader?location_ids%5B%5D=17744&rooms_min=3"],
    "proxySettings": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("mantisus/hemnet-property-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 '{
  "urls": [
    "https://www.hemnet.se/bostader?location_ids%5B%5D=17744&rooms_min=3"
  ],
  "proxySettings": {
    "useApifyProxy": false
  }
}' |
apify call mantisus/hemnet-property-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mantisus/hemnet-property-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/L3X7P2r8o1htoxiUo/builds/wkwxLjTVwW8vXtfE3/openapi.json
