# FINN.no Listings Scraper (`automation-lab/finn-no-multi-category-listings`) Actor

Search public FINN.no property, jobs, mobility, and Torget listings and export normalized records with category-specific market fields.

- **URL**: https://apify.com/automation-lab/finn-no-multi-category-listings.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** Real estate, Jobs, E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 item extracteds

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?

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

## FINN.no Listings Scraper

Search public **FINN.no listings** across property, jobs, mobility, and Torget in one run.

The Actor exports normalized listing IDs, titles, URLs, locations, prices, timestamps, seller or employer context, images, and category-specific public fields.

Use it for recurring Norwegian market monitoring, one-time analysis, spreadsheet exports, and data-pipeline inputs.

No login, browser, or proxy is required for the supported public search pages.

### What does FINN.no Listings Scraper do?

The Actor accepts either:

- a keyword plus one or more FINN.no categories; or
- FINN.no search URLs containing filters you selected on the website.

It requests public search-result pages, follows numbered pagination, removes duplicate ads, normalizes each category into one stable dataset contract, and stops at your limit.

A cross-category run divides its result allowance across the selected searches so one busy category does not crowd out every other category.

The default dataset remains easy to load into Google Sheets, Excel, BigQuery, PostgreSQL, a BI tool, or another Actor.

### Which FINN.no categories are supported?

| Input category | Public FINN.no surface | Useful category fields |
| --- | --- | --- |
| `property` | Homes, lettings, new-build, leisure, and plot search URLs | asking price, total price, area, bedrooms, property type, ownership type, agent, viewing |
| `jobs` | Job search | job title, employer, location, publication time, deadline, number of positions, coordinates |
| `mobility` | Car and other mobility search URLs | make, model, year, mileage, fuel, transmission, registration number, dealer type |
| `torget` | Torget / recommerce search | price, location, seller labels, brand, trade type, shipping signal |

The built-in mobility search starts with cars.

Supply a FINN.no mobility search URL to use another supported mobility subcategory or detailed website filters.

### Who is it for?

#### Market analysts

Build recurring snapshots of asking prices, listing counts, locations, and inventory mix in Norway.

#### Recruiters and labor-market teams

Track public vacancies by keyword and retain stable ad IDs for change detection.

#### Vehicle dealers and pricing teams

Export current vehicle inventory with normalized price, make, model, year, mileage, and seller classification.

#### Recommerce teams

Monitor public Torget inventory, price points, brands, and shipping availability.

#### Data engineers

Feed one consistent cross-category schema into scheduled ETL and warehouse jobs.

### Why use a normalized multi-category dataset?

FINN.no categories expose different fields, but monitoring workflows usually need the same core identity and provenance columns.

Every row includes:

- `listingId`;
- `category`;
- `title`;
- canonical `url`;
- `location`;
- `priceNok` and `priceCurrency` when available;
- `sellerOrEmployer` when available;
- `sourceSearchUrl`;
- `scrapedAt`.

Category-only columns remain `null` outside their category.

That makes appends and comparisons predictable without pretending that a job has a vehicle mileage or that a Torget item has bedrooms.

### Input parameters

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `categories` | array | all four | Categories used when `startUrls` is empty |
| `query` | string | empty | Keyword applied to each selected category |
| `startUrls` | array | empty | Public FINN.no search URLs; takes precedence over category and query |
| `maxItems` | integer | `20` | Maximum unique rows across the run, from 1 to 10,000 |
| `maxPagesPerSearch` | integer | `20` | Per-search pagination safety limit, from 1 to 200 |
| `requestDelayMillis` | integer | `150` | Delay between search pages, from 0 to 10,000 ms |

Only HTTPS URLs on `www.finn.no` or `finn.no` are accepted.

A supplied URL must be a supported property, jobs, mobility, or Torget search page.

Detail-page and arbitrary external URLs are rejected rather than interpreted ambiguously.

### Get started

1. Open the Actor input page.
2. Select one or more categories.
3. Enter a keyword such as `Oslo`, `sykepleier`, `Tesla`, or `sofa`.
4. Keep `maxItems` small for your first run.
5. Click **Start**.
6. Open **Dataset** to inspect the normalized rows.
7. Export JSON, CSV, Excel, XML, or RSS from the dataset controls.
8. Schedule the same input when you need recurring snapshots.

For exact website filters, configure a search on FINN.no, copy its search URL, and paste it into `startUrls`.

### Example inputs

Search public nursing vacancies:

```json
{
  "categories": ["jobs"],
  "query": "sykepleier",
  "maxItems": 10,
  "maxPagesPerSearch": 2
}
```

Extract a filtered car search:

```json
{
  "startUrls": [
    { "url": "https://www.finn.no/mobility/search/car?sort=PUBLISHED_DESC" }
  ],
  "maxItems": 25
}
```

Create an evenly represented Oslo snapshot:

```json
{
  "categories": ["property", "jobs", "mobility", "torget"],
  "query": "Oslo",
  "maxItems": 100
}
```

### Output example

This anonymized example uses the real output shape produced by the current mobility parser:

```json
{
  "listingId": "123456789",
  "category": "mobility",
  "title": "Example electric vehicle",
  "url": "https://www.finn.no/mobility/item/123456789",
  "location": "Oslo",
  "priceNok": 349000,
  "priceCurrency": "NOK",
  "publishedAt": "2025-01-15T12:00:00.000Z",
  "sellerOrEmployer": "Example dealer AS",
  "imageUrls": [],
  "labels": [],
  "latitude": 59.91,
  "longitude": 10.75,
  "propertyType": null,
  "ownershipType": null,
  "areaSqm": null,
  "bedrooms": null,
  "totalPriceNok": null,
  "monthlyCostNok": null,
  "viewing": null,
  "jobTitle": null,
  "applicationDeadline": null,
  "positions": null,
  "make": "Example make",
  "model": "Example model",
  "year": 2022,
  "mileageKm": 45000,
  "fuel": "Elektrisitet",
  "transmission": "Automat",
  "registrationNumber": "AB12345",
  "sellerType": "Forhandler",
  "brand": null,
  "tradeType": null,
  "shippingAvailable": null,
  "sourceSearchUrl": "https://www.finn.no/mobility/search/car",
  "scrapedAt": "2025-01-15T12:00:01.000Z"
}
```

Null values mean FINN.no did not expose that field for the listing or the field belongs to another category.

### How much does it cost to extract FINN.no listings?

The Actor uses pay-per-event pricing:

- **Actor start:** $0.00005 once per run.
- **Listing:** one event for each unique row saved.
- Filtered, duplicate, malformed, and failed records are not charged as listings.

The per-listing price depends on your Apify pricing tier.

| Tier | Price per listing |
| --- | ---: |
| FREE | $0.00230 |
| BRONZE | $0.00200 |
| SILVER | $0.00156 |
| GOLD | $0.00120 |
| PLATINUM | $0.00120 |
| DIAMOND | $0.00120 |

At the FREE tier, 10 saved listings cost about **$0.02305**, and 100 cost about **$0.23005**, including the start event.

At BRONZE, 10 saved listings cost about **$0.02005**, and 100 cost about **$0.20005**.

The input limit is the clearest way to control spend.

### Recurring monitoring workflow

The Actor outputs a stable `category` plus `listingId` key and a fresh `scrapedAt` timestamp.

A practical scheduled workflow is:

1. Run the same category, query, or filtered URL daily.
2. Append the dataset to durable storage.
3. Join rows on `category` and `listingId`.
4. Flag IDs that appear for the first time.
5. Compare price, title, labels, location, or category fields with the prior snapshot.
6. Notify your team through a webhook, Slack automation, email service, or another Actor.

The Actor returns snapshots; it does not itself retain history, compute diffs, or send alerts.

### Export and integration patterns

#### Spreadsheet review

Download CSV or Excel from the Dataset tab and filter by category, location, price, employer, or vehicle attributes.

#### Warehouse ingestion

Use the dataset API with `clean=true` and map nullable category fields into a wide table or category-specific views.

#### Webhooks

Attach an Apify run webhook to trigger downstream processing after a successful scheduled run.

#### Actor-to-Actor workflows

Call this Actor from a monitoring, enrichment, or notification Actor and pass the dataset ID to the next step.

### Run with the Apify API using cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~finn-no-multi-category-listings/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "categories": ["jobs"],
    "query": "sykepleier",
    "maxItems": 10
  }'
```

Never commit your Apify token to source control.

### Run with JavaScript

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });

const run = await client.actor('automation-lab/finn-no-multi-category-listings').call({
  categories: ['property', 'jobs', 'mobility', 'torget'],
  query: 'Oslo',
  maxItems: 20,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

### Run with Python

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("automation-lab/finn-no-multi-category-listings").call(
    run_input={
        "categories": ["mobility"],
        "query": "Tesla",
        "maxItems": 25,
    }
)
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items)
```

### Use through MCP

#### Claude Code setup

Add the Actor to Claude Code:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/finn-no-multi-category-listings"
```

#### Claude Desktop setup

Claude Desktop can use this MCP configuration.

#### Cursor setup

Cursor accepts the same remote MCP server JSON.

#### VS Code setup

VS Code MCP extensions can use the same server URL and configuration:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/finn-no-multi-category-listings"
    }
  }
}
```

Example prompts:

- “Search FINN.no jobs for sykepleier and return 25 vacancies.”
- “Export the newest 20 cars from this FINN.no search URL.”
- “Create a 40-row Oslo snapshot across property, jobs, mobility, and Torget.”

### Reliability and polite use

The implementation requests server-rendered search pages and embedded public application state.

It does not load listing images, execute a browser, or fetch every detail page.

Transient network errors, rate limits, and temporary server errors receive up to two bounded retries.

Stable client errors are not retried blindly.

The default delay reduces request pressure between pages.

Lower concurrency and smaller scheduled runs are safer than a single very large burst.

### Limits and failure behavior

- FINN.no can change its public HTML or embedded application data.
- Search-result fields can be less complete than a listing detail page.
- The Actor does not reveal logged-in, private, hidden, or paid contact information.
- It does not bypass access controls or CAPTCHA challenges.
- `maxItems` applies across the whole run.
- Multi-search runs split the allowance approximately evenly across searches.
- Results depend on inventory available when the run starts.
- A recognizable no-result search completes with zero rows.
- An unsupported URL, malformed limit, HTTP failure, or unrecognizable page shape fails the run with a diagnostic log.

### Troubleshooting

#### Why did my run return fewer rows than `maxItems`?

The source may have fewer matching ads, a selected category may be sparse, the per-search page limit may have been reached, or duplicates may have been removed.

#### Why are some fields null?

The field may not apply to that category or FINN.no may not expose it on the public search card.

#### Why was my URL rejected?

Use a public FINN.no search URL, not a detail ad, account page, short link, or external website.

#### How do I preserve detailed filters?

Set filters on FINN.no and copy the resulting search URL into `startUrls`.

#### What should I do after a source-format failure?

Inspect the run log and retry later only if FINN.no had a temporary outage. Repeated “unrecognizable search data” errors usually require an Actor update rather than repeated runs.

### Responsible and legal use

This Actor extracts information displayed on public FINN.no search pages.

You are responsible for complying with FINN.no terms, robots guidance, applicable privacy and database laws, and your contractual obligations.

Collect only what you need.

Avoid using listing data for harassment, discrimination, unwanted contact, or decisions that require human review.

Do not attempt to access private accounts, hidden contact details, or restricted pages through this Actor.

When processing personal data, establish a lawful basis, apply retention limits, honor data-subject rights, and secure your exports.

This documentation is operational guidance, not legal advice.

### Related Automation Lab Actors

This Actor is intentionally standalone in the current public portfolio because the earlier category-specific FINN.no Actors are not public Store products.

Choose it when one normalized run across several FINN.no categories is the main requirement.

Future public category-specific Actors may be linked here only after their Store availability is verified.

### FAQ

#### Does it require a FINN.no account?

No. It supports public search pages only.

#### Does it require an Apify proxy?

No. The current implementation uses direct HTTP requests and exposes no automatic proxy fallback.

#### Can I search several categories at once?

Yes. Select any combination of property, jobs, mobility, and Torget.

#### Can I use exact FINN.no filters?

Yes. Pass one or more filtered search URLs in `startUrls`.

#### Does it scrape full descriptions and phone numbers?

No. This release intentionally uses search-page data and public seller or employer context. It does not fan out to every detail page.

#### Can I detect new or changed listings?

Yes, downstream. Schedule repeat runs and compare rows by `category` plus `listingId`. The Actor does not store history itself.

#### Are duplicate listings charged twice?

No. Duplicate category-and-ID pairs are discarded within the run before the listing event is charged.

# Actor input Schema

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

FINN.no sections to search when Start URLs are not provided.

## `query` (type: `string`):

Optional keyword applied to every selected category, for example Oslo, sykepleier, Tesla, or sofa.

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

Optional FINN.no property, jobs, mobility, or Torget search URLs. Existing URL filters are preserved. When set, these URLs replace Categories and Search keyword.

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

Maximum number of unique listing records saved across all searches.

## `maxPagesPerSearch` (type: `integer`):

Safety limit for pagination of each category or supplied search URL.

## `requestDelayMillis` (type: `integer`):

Polite delay between FINN.no search-page requests.

## Actor input object example

```json
{
  "categories": [
    "property",
    "jobs",
    "mobility",
    "torget"
  ],
  "query": "Oslo",
  "startUrls": [],
  "maxItems": 20,
  "maxPagesPerSearch": 20,
  "requestDelayMillis": 150
}
```

# Actor output Schema

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

Default dataset view with normalized listing identity, price, location, seller or employer, source URL, and timestamps.

## `categoryFields` (type: `string`):

Dataset view highlighting property, job, mobility, and Torget-specific public attributes.

# 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 = {
    "categories": [
        "property",
        "jobs",
        "mobility",
        "torget"
    ],
    "query": "Oslo"
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/finn-no-multi-category-listings").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 = {
    "categories": [
        "property",
        "jobs",
        "mobility",
        "torget",
    ],
    "query": "Oslo",
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/finn-no-multi-category-listings").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 '{
  "categories": [
    "property",
    "jobs",
    "mobility",
    "torget"
  ],
  "query": "Oslo"
}' |
apify call automation-lab/finn-no-multi-category-listings --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/finn-no-multi-category-listings"
        }
    }
}

```

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/b1ccTTIaUT25DQ59r/builds/HMW2g7fsEwFh3jRNF/openapi.json
