# Bolagsplatsen Scraper – Swedish Businesses for Sale (`outlandish_mainsail/bolagsplatsen-scraper`) Actor

Scrape business-for-sale listings (företag till salu) from Bolagsplatsen.se, the Swedish marketplace for buying and selling companies. Get asking price, turnover, profit status, employees, industry, location, broker and more as clean JSON/CSV/Excel.

- **URL**: https://apify.com/outlandish_mainsail/bolagsplatsen-scraper.md
- **Developed by:** [Andreas v](https://apify.com/outlandish_mainsail) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.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.

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

## Bolagsplatsen Scraper – Swedish businesses for sale (företag till salu)

Scrape **business-for-sale listings from [Bolagsplatsen.se](https://www.bolagsplatsen.se)**, a Swedish marketplace for buying and selling companies (*företag till salu*, *bolag till salu*, *verksamhet till salu*). The Actor returns one structured record per listing: asking price (*prisidé*), turnover (*omsättning*), profit/loss status (*resultat*), number of employees, year founded, industry, municipality and county (*län*), broker, images and more. You can export the results as JSON, CSV, Excel or via API.

- ✅ Finds **every listing** through the site's public sitemap, with no search pagination
- ✅ **Clean numeric fields**: "1,995 Mkr" becomes `1995000` and "0-2 Mkr" becomes `min 0 / max 2000000`
- ✅ **Filters**: category/industry, location, keyword, asking-price and turnover ranges, broker vs. owner
- ✅ **Only new listings** mode for scheduled monitoring (daily alerts on new companies for sale)
- ✅ Optionally includes **sold listings** (*sålda bolag*) with sale date, which is useful for valuation benchmarks
- ✅ Polite and lightweight: plain HTTP (no browser), no proxy needed, respects `robots.txt`

### What data can you get?

| Field | Example | Notes |
|---|---|---|
| `url`, `listingId` | `…/foretag-till-salu/…-91593`, `"91593"` | |
| `status`, `soldDate` | `"active"` / `"sold"`, `"2026-05-16"` | Sold listings are skipped unless `includeSold` is on |
| `title`, `description` | | Full description text with line breaks (active listings) |
| `category`, `industries` | `"Mat & dryck"`, `["Restaurang", "Mat & dryck"]` | Main category (breadcrumb) + all industry tags |
| `locationRaw`, `district`, `municipality`, `county`, `region`, `locations` | `"Södertälje - Stockholm"` → municipality `Södertälje`, county `Stockholm` | `region` for broad areas like "Norrland" or "Sverige"; all places when several are listed |
| `askingPriceRaw`, `askingPrice`, `askingPriceMin`, `askingPriceMax`, `askingPriceIsRange` | `"1,2 Mkr"` → `1200000` | SEK; ranges ("2-5 Mkr") give min/max |
| `turnoverRaw`, `turnover`, `turnoverMin`, `turnoverMax`, `turnoverIsRange` | `"8,3 Mkr"` → `8300000` | Yearly turnover, SEK |
| `resultRaw`, `profitStatus` | `"Vinst"` → `"profit"` | The site shows profit/loss status, not an amount |
| `employeesRaw`, `employees`, `employeesMin`, `employeesMax` | `"2-10 st"` → 2 / 10 | |
| `yearFounded` | `2005` | |
| `premisesAreaSqm`, `rentPerMonthSek` | `100`, `35688` | When shown (retail, restaurants, etc.) |
| `movable`, `ownsProperty`, `generationTransfer`, `liquorLicense` | `true` | Yes/no facts shown on some listings |
| `seatsIndoor`, `seatsOutdoor`, `openingHours`, `otherFacts` | | Restaurant/retail specifics; any other facts end up in `otherFacts` |
| `advertiserType` | `"broker"` / `"owner"` | Business broker (*mäklare*) or the owner (*ägaren själv*) |
| `brokerName`, `brokerCompany`, `brokerProfileUrl` | `"Bra Objekt"` | Only for broker listings; publicly shown business contact info only |
| `images` | `["https://www.bolagsplatsen.se/media/objects/images/…webp"]` | Main listing image |
| `isPopular` | `false` | "Mycket populär annons" badge |
| `lastModified` | `"2026-09-28"` | Last-modified date from the site's sitemap (for sold listings, this is the sale date) |
| `scrapedAt` | ISO timestamp | |

### How to use it

1. Click **Try for free** / **Start**.
2. Set **Max listings** (0 = all) and any filters.
3. Run it, then download the dataset as JSON/CSV/Excel or read it through the API.

#### Input example

```json
{
  "maxItems": 200,
  "categories": ["Restaurang", "Café"],
  "locations": ["Stockholm", "Uppsala"],
  "maxAskingPrice": 3000000,
  "includeListingsWithoutPrice": false,
  "modifiedSince": "30 days"
}
```

Monitor new listings every day (use with an Apify **Schedule**):

```json
{ "maxItems": 0, "skipPreviouslyScraped": true, "modifiedSince": "7 days" }
```

Scrape specific listings:

```json
{ "startUrls": [{ "url": "https://www.bolagsplatsen.se/foretag-till-salu/pub-bar-pa-centralt-lage-sodertalje-85643" }] }
```

#### Input options

| Option | Default | Description |
|---|---|---|
| `maxItems` | 100 | Max listings to save (after filters). Newest first. 0 = no limit |
| `startUrls` | – | Specific listing URLs. If empty, all listings come from the sitemap |
| `includeSold` | false | Also return sold listings |
| `modifiedSince` | – | `YYYY-MM-DD` or relative (`7 days`, `2 weeks`, `1 month`), based on the sitemap last-modified date. Filtered **before** fetching, so it is fast and cheap |
| `skipPreviouslyScraped` | false | Skip listings saved in earlier runs (stored in the named key-value store `bolagsplatsen-scraper-seen`, key `seenListingsKey`) |
| `categories`, `locations`, `keywords` | – | Case-insensitive "contains" filters (any of the words) |
| `minAskingPrice`, `maxAskingPrice`, `minTurnover`, `maxTurnover` | – | SEK. Ranges match if they overlap |
| `includeListingsWithoutPrice` | true | When a price/turnover filter is set, keep listings that don't show that value |
| `advertiserType` | any | `any`, `broker` or `owner` |
| `proxyConfiguration` | off | Not needed; available if you ever get blocked |
| `maxConcurrency`, `maxRequestsPerMinute`, `delayBetweenRequestsSecs` | 3, 60, 1 | Politeness settings |

Filters other than `modifiedSince`/`skipPreviouslyScraped` are applied **after** each page is downloaded, because the site's filtered search pages are disallowed in `robots.txt`. You pay only for listings that are saved. A very narrow filter just takes longer to run.

#### Output example

A real record, shortened (description truncated, broker name replaced):

```json
{
  "url": "https://www.bolagsplatsen.se/foretag-till-salu/grill-sallad-glass-i-centrala-malmo-91253",
  "listingId": "91253",
  "listingType": "Företag till salu",
  "status": "active",
  "soldDate": null,
  "title": "Grill & Sallad & Glass i centrala Malmö",
  "description": "Grill & Sallad är en restaurangverksamhet på i centrala Malmö. Verksamheten har ett lättillgängligt snabbmatskoncept med ett varierat utbud av grillrätter, hamburgare, kebab, kyckling, rullar och sallader.\n\nMenyn omfatta…",
  "category": "Mat & dryck",
  "industries": [
    "Gatukök & snabbmat",
    "Mat & dryck"
  ],
  "locationRaw": "Malmö - Skåne",
  "municipality": "Malmö",
  "county": "Skåne",
  "district": null,
  "region": null,
  "locations": [
    {
      "raw": "Malmö - Skåne",
      "district": null,
      "municipality": "Malmö",
      "county": "Skåne",
      "region": null
    }
  ],
  "askingPriceRaw": "3,5 Mkr",
  "askingPrice": 3500000,
  "askingPriceMin": 3500000,
  "askingPriceMax": 3500000,
  "askingPriceIsRange": false,
  "turnoverRaw": "4 Mkr",
  "turnover": 4000000,
  "turnoverMin": 4000000,
  "turnoverMax": 4000000,
  "turnoverIsRange": false,
  "resultRaw": "Vinst",
  "profitStatus": "profit",
  "currency": "SEK",
  "employeesRaw": null,
  "employees": null,
  "employeesMin": null,
  "employeesMax": null,
  "yearFounded": 2009,
  "premisesAreaSqm": 78,
  "rentPerMonthSek": 16000,
  "movable": null,
  "ownsProperty": null,
  "generationTransfer": null,
  "liquorLicense": null,
  "seatsIndoor": 12,
  "seatsOutdoor": 50,
  "openingHours": "10-22",
  "otherFacts": null,
  "advertiserType": "broker",
  "brokerName": "<broker name as shown on the listing>",
  "brokerCompany": "Advilia Företagsmäklare",
  "brokerProfileUrl": "https://www.bolagsplatsen.se/foretagsmaklare/advilia-foretagsmaklare-988/annonser",
  "images": [
    "https://www.bolagsplatsen.se/media/objects/images/WhatsApp_Image_20260901_at_082838_4_1788246250-800x500.webp"
  ],
  "isPopular": false,
  "lastModified": "2026-09-25",
  "scrapedAt": "2026-09-28T20:11:58.862Z"
}
```

#### Data coverage (measured)

Full run on 28 Sep 2026: the sitemap listed **2,803 URLs (2,795 unique)**. 2,791 pages were saved (**1,947 active**, 844 sold) and 4 were removed/redirected, with 0 failed requests. Share of **active** listings where each field has a value:

| Field | Filled | Field | Filled |
|---|---|---|---|
| title, url, listingId, category, lastModified | 100% | description | ~100% (1 of 1,947 empty on the site) |
| images | ~100% | advertiserType | 100% |
| location (raw) | 96% | county (län) | 83% |
| municipality | 50% | turnover (raw, exact or range) | 80% |
| profit status (Vinst/Förlust) | 65% | asking price (raw, exact or range) | 42% |
| premises area | 47% | rent per month | 41% |
| employees | 36% | year founded | 36% |
| broker name / company (broker listings only) | 54% of all active (87% of broker listings) | | |

Empty values mean the seller didn't publish that information. The Actor doesn't guess.

### Use cases

- **Acquisition entrepreneurs & search funds**: get alerts on new Swedish SMEs for sale that match your criteria.
- **M\&A advisors and business brokers**: market mapping, competitor broker activity, pricing benchmarks.
- **Valuation & research**: turnover vs. asking price by industry and region. Sold listings give extra data points.
- **Lenders, accountants, franchise and real-estate professionals**: lead lists of businesses changing hands.
- **Data teams**: feed a CRM, Google Sheets, Airtable or a BI dashboard through Apify integrations.

### Pricing

This Actor uses **pay per event** pricing. You pay only for listings that are saved to your dataset:

| Event | Price |
|---|---|
| Listing saved to the dataset | **$3.00 per 1,000 listings** ($0.003 each; lower on higher Apify plans) |
| Actor start | $0.00005 (Apify default) |

Platform usage is included. A run over all ~1,950 active listings costs about **$6**. A daily "only new listings" run usually returns a few dozen listings and costs cents. You can cap spending per run with the **Max cost per run** setting.

### How it works and limitations

- Listings are found through `https://www.bolagsplatsen.se/sitemap_ads.xml` and each listing page is fetched as static HTML. The Actor does **not** call the site's `/api/` endpoints and does **not** use paginated or filtered search URLs (both are disallowed in `robots.txt`).
- The site shows **rounded** figures (e.g. "1 Mkr"). The Actor reports them exactly as displayed and does not guess exact values from the free-text description.
- Many sellers don't publish an asking price, turnover or employees, so those fields are empty for those listings. See the fill rates above.
- Active listings don't show a publication date. `lastModified` comes from the sitemap.
- Phone numbers and the full image gallery are loaded through the site's `/api/`, so they are not collected. The main image is included.
- The sitemap also contains old sold listings and a few removed ones. Removed listings (redirects/404) are skipped and not charged.

### Legal & GDPR

This Actor collects publicly available listing information. Bolagsplatsen's `robots.txt` allows crawling of listing pages. The Actor follows its disallow rules, identifies itself with a descriptive User-Agent and rate-limits itself (1 request/second by default).

**Personal data:** listings placed by brokers show the broker's name and company, which is business contact information published by the broker. Owner-placed listings don't show a name, and the Actor doesn't try to find one. Names of natural persons are still personal data under the GDPR. If you store or process them, you need a lawful basis (e.g. legitimate interest), must follow data-minimisation and purpose-limitation principles, and must honour data-subject rights. Don't use the data for unsolicited marketing where that is prohibited. You are responsible for how you use the data. If you don't need broker names, just drop the `brokerName` column. Listing texts and images may be protected by copyright, so don't republish them without permission.

This Actor is not affiliated with or endorsed by Bolagsplatsen.

### FAQ

**Do I need a proxy?** No. The site works with plain HTTP requests. Proxy settings are there only in case that changes.

**How long does a full run take?** At the default polite rate (1 request/second) a full run of all ~2,800 sitemap URLs took 47 minutes in our test. 100 listings take about 2 minutes. Runs with `modifiedSince` or `skipPreviouslyScraped` only fetch new or changed listings, so they are much faster.

**Can I get only new listings every day?** Yes: schedule the Actor with `skipPreviouslyScraped: true`.

**Something broke or a field is missing?** Open an issue on the Actor's Issues tab and include the listing URL.

# Actor input Schema

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

Maximum number of listings to save (after filters). Listings are processed newest first (by sitemap last-modified date). Set 0 for no limit (all ~2,800 listings in the sitemap).

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

Scrape only these listing pages, e.g. https://www.bolagsplatsen.se/foretag-till-salu/some-company-91593. Leave empty to discover all listings from the sitemap. Search/category URLs are not supported.

## `includeSold` (type: `boolean`):

The sitemap also contains listings that are already sold (marked 'SÅLD'). Sold pages still show turnover, price range, employees, location and the sale date, which is useful for valuation benchmarks. Off by default.

## `modifiedSince` (type: `string`):

Only fetch listings whose sitemap last-modified date is on or after this date. Use YYYY-MM-DD or a relative value such as '7 days', '2 weeks' or '1 month'. The site does not show a publication date on active listings, so this uses the sitemap <lastmod> date (for sold listings it equals the sale date).

## `skipPreviouslyScraped` (type: `boolean`):

Remember listing IDs across runs (in a named key-value store 'bolagsplatsen-scraper-seen' in your account) and skip listings already scraped in an earlier run. Ideal for scheduled monitoring of new businesses for sale.

## `seenListingsKey` (type: `string`):

Name of the record that stores already-scraped listing IDs. Use a different key per saved task/filter set if you monitor several searches separately. Only listings that were actually saved (matched your filters) are remembered.

## `categories` (type: `array`):

Keep only listings whose category or industry tags contain any of these words (case-insensitive), e.g. 'Restaurang', 'E-handel', 'Bygg', 'Mat & dryck', 'Tjänsteföretag'.

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

Keep only listings whose location (district, municipality, county/län or region) contains any of these words, e.g. 'Stockholm', 'Skåne', 'Göteborg', 'Norrland'.

## `keywords` (type: `array`):

Keep only listings whose title or description contains any of these words (case-insensitive).

## `minAskingPrice` (type: `integer`):

Minimum asking price (prisidé) in SEK. Price ranges such as '2-5 Mkr' match if they overlap the requested range.

## `maxAskingPrice` (type: `integer`):

Maximum asking price (prisidé) in SEK.

## `minTurnover` (type: `integer`):

Minimum yearly turnover (omsättning) in SEK.

## `maxTurnover` (type: `integer`):

Maximum yearly turnover (omsättning) in SEK.

## `includeListingsWithoutPrice` (type: `boolean`):

Many sellers do not publish an asking price or turnover. When a price/turnover filter is set, keep (true) or drop (false) listings that do not show that value.

## `advertiserType` (type: `string`):

Filter by who placed the ad.

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

Not required: the site works without proxies. Enable Apify Proxy only if you run into blocking.

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

Maximum parallel requests. Keep this low to be polite to the website.

## `maxRequestsPerMinute` (type: `integer`):

Rate limit for page requests.

## `delayBetweenRequestsSecs` (type: `integer`):

Minimum delay between two requests to bolagsplatsen.se. Default 1 second (about 60 pages per minute at most).

## `userAgent` (type: `string`):

User-Agent header sent with each request.

## Actor input object example

```json
{
  "maxItems": 50,
  "includeSold": false,
  "skipPreviouslyScraped": false,
  "seenListingsKey": "SEEN_IDS",
  "includeListingsWithoutPrice": true,
  "advertiserType": "any",
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "maxConcurrency": 3,
  "maxRequestsPerMinute": 60,
  "delayBetweenRequestsSecs": 1,
  "userAgent": "Mozilla/5.0 (compatible; BolagsplatsenScraper/1.0; Apify Actor; polite crawler, respects robots.txt)"
}
```

# Actor output Schema

## `listings` (type: `string`):

All scraped business-for-sale listings in the default dataset.

# 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": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("outlandish_mainsail/bolagsplatsen-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": 50 }

# Run the Actor and wait for it to finish
run = client.actor("outlandish_mainsail/bolagsplatsen-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": 50
}' |
apify call outlandish_mainsail/bolagsplatsen-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,outlandish_mainsail/bolagsplatsen-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/4EY1Qdp1AD7DhyYnL/builds/cgzDau0t6QyiKpdcU/openapi.json
