# Hemnet Properties Scraper (Sweden) (`scrapyx/hemnet-properties-scraper`) Actor

Scrapes for-sale property listings from Hemnet, Sweden's dominant real estate portal. Every row carries asking price, monthly fee, rooms, living and land area, price per square metre, street address, GPS coordinates, broker and agency, publication date and photos.

- **URL**: https://apify.com/scrapyx/hemnet-properties-scraper.md
- **Developed by:** [Ibnu Adzim](https://apify.com/scrapyx) (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.26 / 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 Properties Scraper (Sweden)

Scrapes for-sale property listings from **[Hemnet](https://www.hemnet.se)** —
Sweden's dominant real estate portal, where essentially every home for sale in
the country is listed.

Public data only. No login, no cookies, no browser.

### The one thing you need to know before using this

**A location id Hemnet cannot resolve returns the entire country.**

`location_ids[]=99999999` answers HTTP 200 with **46,590 listings** — every
home for sale in Sweden — not an error. And the query echo is no help: it
faithfully repeats the bogus id back at you.

This actor gates on Hemnet's own resolved `Location` entity, which is simply
absent when the id is unknown. If a location does not resolve it **stops before
paginating** and emits one `SEARCH_SUMMARY` with `locationApplied: false` and
zero property rows — rather than handing you 46,590 rows labelled as the place
you asked for.

You normally never see this, because you can just type a name.

### What you get

Three record types share one dataset, told apart by `recordType`.

#### `PROPERTY` — one row per listing

- **Money** — asking price, monthly fee (`avgift`), price per square metre
- **The property** — rooms, living and supplemental areas, land area, floor,
  housing form, new-build flag
- **Where** — street address, area description, and **GPS coordinates**
- **Who** — broker name, agency name, agency logo
- **When** — publication date, plus any scheduled showings
- **Media** — full image list and the listing's teaser description

Upstream's object is passed through verbatim. Its own `recordType` field
(`COMMON` / `PROJECT` / `PROJECT_UNIT`, marking new-build project units) is
preserved as **`listingRecordType`** so it cannot collide with the dataset's
own discriminator.

#### `SEARCH_SUMMARY` — one row per location

Hemnet's own match total, the viewable ceiling, which location id the name
resolved to and what Hemnet called it, pages fetched, requests spent, plus
`locationApplied` and `viewableCeilingReached`.

#### `ERROR` — one row per input that could not be processed

Every input maps to at least one row, so nothing disappears silently.

### Input

| Field | What it does |
| --- | --- |
| **Locations** | Swedish place names (`Stockholm`, `Nacka kommun`, `Göteborg`) or numeric Hemnet ids. Resolved through Hemnet's own public lookup; when a name matches several places the **broadest** wins (county over municipality over district), and the summary row says which was served. |
| **Property types** | Seven verified types. See the warning below. |
| **Minimum rooms** | Verified: Stockholms län 10,100 → 2,978 at `4`. |
| **Maximum asking price** | Verified: 10,100 → 3,633 at `2000000` SEK. |
| **Minimum living area** | Verified: 10,100 → 2,197 at `100` m². |
| **Max listings per location** | `0` for everything Hemnet will serve. |
| **Max concurrent requests / Minimum seconds between requests** | Throughput controls. Pages are ~1.6 MB each. |

#### ⚠️ Property types that look real but silently disable the filter

Hemnet does **not** reject an unrecognised property type — it returns every
listing in the region as though your filter had applied. Four of the words
that do this are perfectly ordinary Swedish:

| You might type | What Hemnet does |
| --- | --- |
| `lagenhet` ("apartment") | returns **everything** — use `bostadsratt` |
| `parhus`, `kedjehus`, `andelsboende` | return **everything** |

Only these seven actually filter, and anything else is refused before the run
starts: `bostadsratt`, `villa`, `radhus`, `fritidshus`, `tomt`, `gard`,
`other`.

### Known limits

- **2,500 rows per query, hard.** Hemnet reports the full match count but pages
  through at most 2,500 (it tells you so itself, as `totalViewable`). Stockholms
  län has ~10,100 listings and will only ever yield 2,500 in one query — split
  by municipality, property type, price or size to reach the rest.
  `viewableCeilingReached` flags this on every summary row.
- **For-sale only.** Sold history (`searchSales` reports 523,472 records for
  Stockholms län alone) and upcoming listings live on separate surfaces and are
  a genuinely different product. Not included here.
- **No detail pass, by design.** The listing card already carries every field a
  detail page repeats, so a detail fetch would spend one request per listing to
  re-collect what you already have.
- **50 rows per page is fixed** by Hemnet and is not tunable.

# Actor input Schema

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

One Swedish place per entry, each with its own SEARCH\_SUMMARY row — e.g. `Stockholm`, `Göteborg`, `Nacka kommun`, or a numeric Hemnet location id like `17744`.

Names are resolved through Hemnet's own public location lookup. When a name matches several places (searching `Stockholm` returns the county, the municipality and a dozen districts) the **broadest** is used — county over municipality over district — and the summary row always reports which one was actually served.

Because Hemnet pages through at most 2,500 rows per query, several narrow locations return far more data than one broad one.

## `itemTypes` (type: `array`):

Restrict to one or more property types. Leave empty for all.

Only these seven were verified to actually filter. Hemnet does **not** reject an unrecognised type — it returns every listing in the region as though the filter had applied. That trap catches real Swedish words: `lägenhet`, `parhus`, `kedjehus` and `andelsboende` all silently return the unfiltered total, so they are refused before the run starts.

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

Verified real: on Stockholms län this narrowed 10,100 listings to 2,978. Leave at 0 to skip.

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

Verified real: `2000000` narrowed Stockholms län from 10,100 to 3,633. Leave at 0 to skip.

## `livingAreaMin` (type: `integer`):

Verified real: `100` narrowed Stockholms län from 10,100 to 2,197. Leave at 0 to skip.

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

Stop after this many listings per location. Set to 0 for everything Hemnet will serve — which is capped at 2,500 rows per query however many matches it reports.

## `maxConcurrency` (type: `integer`):

Upper bound on requests in flight at once, across all locations.

## `minRequestInterval` (type: `integer`):

Paces how often requests START, without tying up a concurrency slot. Each search page is roughly 1.6 MB, so a small interval is polite by default.

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

Residential pinned to Sweden. Hemnet showed no bot challenge on the profiles this actor uses, but it is a Sweden-only portal, and a comparable local-market portal in this portfolio (Rumah123) turned out to score exit-IP geography rather than ASN reputation — rotating IPs without a country pin failed there 4 times out of 4.

## Actor input object example

```json
{
  "locations": [
    "Nacka kommun"
  ],
  "itemTypes": [],
  "roomsMin": 0,
  "priceMax": 0,
  "livingAreaMin": 0,
  "maxItems": 100,
  "maxConcurrency": 4,
  "minRequestInterval": 0,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "SE"
  }
}
```

# Actor output Schema

## `items` (type: `string`):

One row per scraped record. See the dataset's default view for field definitions.

# 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 = {
    "locations": [
        "Nacka kommun"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapyx/hemnet-properties-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 = { "locations": ["Nacka kommun"] }

# Run the Actor and wait for it to finish
run = client.actor("scrapyx/hemnet-properties-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 '{
  "locations": [
    "Nacka kommun"
  ]
}' |
apify call scrapyx/hemnet-properties-scraper --silent --output-dataset

```

## MCP server setup

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