# Tori.fi Scraper - Finnish Classifieds Marketplace (`santamaria-automations/tori-fi-scraper`) Actor

Scrape classified listings from Tori.fi, a Finnish online marketplace. Extract title, price, location, brand, condition, attributes and images. Optional detail pages add the full description, category path and all images. Pay per result.

- **URL**: https://apify.com/santamaria-automations/tori-fi-scraper.md
- **Developed by:** [NanoScrape](https://apify.com/santamaria-automations) (community)
- **Categories:** E-commerce, Lead generation
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 a classified listings

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

## Tori.fi Scraper

Scrape classified listings from [Tori.fi](https://www.tori.fi), the Finnish online classifieds marketplace. Paste any Tori.fi search URL with your filters applied and get one row per listing: title, price, location, brand, seller, image and posting age. Turn on details to add the condition, item attributes, full description, category path, all images and the last edit time.

### What you can do with it

- Track prices for a product, brand or category across Finland.
- Monitor new listings in a city or region by saving a filtered search URL and running the actor on a schedule.
- Build a price database for used goods (phones, bikes, furniture and more).
- Collect seller names of businesses that advertise on Tori.fi.

### Input

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `searchUrls` | array of strings | the example search in the prefill | Tori.fi search URLs. Each URL is paginated automatically. A single URL as a string also works. If empty or missing, the example search from the prefill is used. |
| `maxResults` | integer | 10 | Maximum listings in total across all URLs (0 = unlimited, max 10000). |
| `maxResultsPerQuery` | integer | 100 | Maximum listings per search URL (0 = unlimited, max 10000). |
| `includeDetails` | boolean | false | Open every listing page for the full description, category path, all images and the last edit time. |

An empty input `{}` runs the example search and returns up to 10 listings. Numbers given as text (`"50"`) are accepted.

#### Example: one search with details

```json
{
  "searchUrls": ["https://www.tori.fi/recommerce/forsale/search?q=iphone"],
  "maxResults": 50,
  "includeDetails": true
}
```

#### Example: several searches in one run

```json
{
  "searchUrls": [
    "https://www.tori.fi/recommerce/forsale/search?q=iphone",
    "https://www.tori.fi/recommerce/forsale/search?category=0.93&condition=1&location=0.100013&price_to=150"
  ],
  "maxResults": 200,
  "maxResultsPerQuery": 100
}
```

#### Example: quick scan without details

```json
{
  "searchUrls": ["https://www.tori.fi/recommerce/forsale/search?q=polkupyora"],
  "maxResults": 40
}
```

#### Supported URL formats

| URL | Example |
|-----|---------|
| Keyword search | `https://www.tori.fi/recommerce/forsale/search?q=iphone` |
| Search with filters (category, location, condition, price) | `https://www.tori.fi/recommerce/forsale/search?category=0.93&condition=1&location=0.100013&price_to=150` |

Set the filters on tori.fi, then copy the address bar. A `page` parameter in the URL is ignored, paging starts at page 1.

### Output

Each row is one listing. Fields marked "details" are empty unless `includeDetails` is on.

| Field | Type | Description |
|-------|------|-------------|
| `id` | string | Tori listing ID |
| `title` | string | Listing title |
| `description` | string or null | Listing text (details) |
| `price` | number or null | Price |
| `currency` | string or null | Currency code, `EUR` |
| `location` | string or null | Area and city as shown on the card, e.g. `Karjasilta, Oulu` |
| `category` | string or null | Category path, e.g. `Elektroniikka ja kodinkoneet > Puhelimet ja tarvikkeet > Matkapuhelimet` (details) |
| `brand` | string or null | Brand, when the listing has one |
| `condition` | string or null | Condition from the listing's Kunto chip: `New`, `Like new`, `Good`, `Fair`, `Poor` (details) |
| `condition_text` | string or null | The Kunto label exactly as the site shows it, e.g. `Kuin uusi`, `Hyvä` (details) |
| `image_url` | string or null | Main image URL |
| `image_urls` | array | Image URLs: one without details, all listing images with details |
| `seller` | string or null | Seller name when the card shows one. Private sellers usually show no name |
| `posted_at` | string or null | Approximate ISO timestamp computed from the age on the card |
| `posted_at_text` | string or null | Age exactly as the card shows it: `15 min`, `3 t` (hours), `2 pv` (days), `1 vk` (weeks), `1 kk` (months) |
| `edited_at` | string or null | Last edit time of the listing, ISO (details) |
| `attributes` | object or null | Item chips as label to value, e.g. `Kunto`, `Merkki`, `Muisti`, `Malli`. The labels depend on the category (details) |
| `source_url` | string | Link to the listing on Tori.fi |
| `search_query` | string or null | The search URL that produced the row |
| `scraped_at` | string | ISO timestamp of the scrape |

`posted_at` is rounded to the unit the site shows, so "3 t" means about three hours ago. Use `edited_at` with details when you need an exact time.

#### Sample row

```json
{
  "id": "49057887",
  "title": "APPLE IPHONE 14 128GB ( VALKONEN )",
  "description": "Moikka! olisi minulle jäänyt alle viikko sitten täysin ylimääräseksi tämä kyseinen iphone 14 ...",
  "price": 300,
  "currency": "EUR",
  "location": "Viitasaari Keskus, Viitasaari",
  "category": "Elektroniikka ja kodinkoneet > Puhelimet ja tarvikkeet > Matkapuhelimet",
  "brand": "Apple",
  "condition": "Good",
  "condition_text": "Hyvä",
  "image_url": "https://img.tori.net/dynamic/1280w/item/49057887/3e88d0bd-ece8-4f45-8473-a0d9731bc25d",
  "image_urls": ["https://img.tori.net/dynamic/default/item/49057887/3e88d0bd-ece8-4f45-8473-a0d9731bc25d"],
  "seller": null,
  "posted_at": "2026-10-26T03:44:31.294Z",
  "posted_at_text": "3 t",
  "edited_at": "2026-10-26T03:26:04.217Z",
  "attributes": {"Kunto": "Hyvä", "Merkki": "Apple", "Muisti": "128 GB", "Malli": "iPhone 14/Mini/Plus/Pro/Max"},
  "source_url": "https://www.tori.fi/recommerce/forsale/item/49057887",
  "search_query": "https://www.tori.fi/recommerce/forsale/search?q=iphone",
  "scraped_at": "2026-10-26T06:44:31.294Z"
}
```

### Pricing

Pay per event. You pay a small start fee per run plus one event per listing. A listing is charged once, at the basic or the detail rate.

| Event | Cost | Triggered by |
|-------|------|--------------|
| Run start | $0.001 per run | Each run |
| `directory-serp-result` | $0.002 per listing | Each listing delivered without details |
| `directory-detail-result` | $0.005 per listing | Each listing delivered with `includeDetails` on (instead of the basic event) |

**100 listings = $0.201** without details, **$0.501** with details.

The actor stops delivering when your run's maximum total charge is reached and ends with the status "Stopped at the max charge limit. N listings delivered." You are never charged for listings that were not delivered.

### Use with AI agents (MCP)

Connect this actor to any MCP-compatible AI client. Add the Apify MCP server to your client configuration:

```json
{
  "mcpServers": {
    "apify": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://mcp.apify.com/?tools=santamaria-automations/tori-fi-scraper",
        "--header",
        "Authorization: Bearer <YOUR_API_TOKEN>"
      ]
    }
  }
}
```

You can also copy the setup from the [MCP tab of this actor](https://console.apify.com/actors/tqZCdbYvYkVlYHZlT/info/api/mcp?build=latest) in the Apify Console.

Example prompt: "Use `tori-fi-scraper` to list the 30 newest used iPhone 13 listings in Helsinki under 300 EUR and show them as a table."

### FAQ

**How do I get a search URL?**
Open [tori.fi](https://www.tori.fi), search and set your filters, then copy the URL from the address bar.

**Why is `seller` empty on many rows?**
The listing card only shows a name for business sellers. Private sellers show none.

**Why are `description` and `category` empty?**
They come from the listing page. Turn on `includeDetails`.

**How do I get only new listings?**
Run the actor on a schedule with the same search URL and compare the `id` values with your previous run. `posted_at` helps to spot fresh listings.

**How do I stop at a budget?**
Set the maximum total charge for the run in the Apify Console. The actor stops at that limit and reports how many listings it delivered.

**What does an empty result mean?**
If your search has no hits the run succeeds with zero rows and the status "No listings found for this search."

### Related actors

**European classifieds**

- [Finn.no Scraper (Norway)](https://apify.com/santamaria-automations/finn-no-scraper)
- [Blocket.se Scraper (Sweden)](https://apify.com/santamaria-automations/blocket-se-scraper)
- [DBA.dk Scraper (Denmark)](https://apify.com/santamaria-automations/dba-dk-scraper)
- [Marktplaats.nl Scraper (Netherlands)](https://apify.com/santamaria-automations/marktplaats-nl-scraper)
- [Kleinanzeigen.de Scraper (Germany)](https://apify.com/santamaria-automations/kleinanzeigen-de-scraper)

**Enrich your data**

- [Website Email & Phone Scraper](https://apify.com/santamaria-automations/website-email-scraper)
- [Website Contact Extractor](https://apify.com/santamaria-automations/website-contact-extractor)

### Support

Something missing or not working as expected? [Open an issue](https://console.apify.com/actors/tqZCdbYvYkVlYHZlT/issues) and we will fix it.

# Actor input Schema

## `searchUrls` (type: `array`):

Paste one or more Tori.fi search URLs. Go to tori.fi, set your filters (category, location, price, condition, etc.), then copy the URL. Each URL is paginated automatically. Leave empty to run the example search from the prefill.

## `maxResults` (type: `integer`):

Maximum number of listings to scrape in total (0 = unlimited) / Kerattavien ilmoitusten enimmaismaara (0 = rajoittamaton)

## `maxResultsPerQuery` (type: `integer`):

Maximum results per search URL (0 = unlimited) / Tulosten enimmaismaara yksittaista URL:a kohti (0 = rajoittamaton)

## `includeDetails` (type: `boolean`):

Fetch each listing page for the full description, category path, all images and the last edit time. Charged at the detail rate instead of the basic rate. / Hae yksityiskohtasivut kuvausta, kategoriapolkua ja kuvia varten.

## Actor input object example

```json
{
  "searchUrls": [
    "https://www.tori.fi/recommerce/forsale/search?category=0.93&condition=1&location=0.100013&price_to=150"
  ],
  "maxResults": 10,
  "maxResultsPerQuery": 100,
  "includeDetails": false
}
```

# Actor output Schema

## `classifiedListings` (type: `string`):

Dataset containing scraped classified listings

# 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 = {
    "searchUrls": [
        "https://www.tori.fi/recommerce/forsale/search?category=0.93&condition=1&location=0.100013&price_to=150"
    ],
    "maxResults": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("santamaria-automations/tori-fi-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 = {
    "searchUrls": ["https://www.tori.fi/recommerce/forsale/search?category=0.93&condition=1&location=0.100013&price_to=150"],
    "maxResults": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("santamaria-automations/tori-fi-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 '{
  "searchUrls": [
    "https://www.tori.fi/recommerce/forsale/search?category=0.93&condition=1&location=0.100013&price_to=150"
  ],
  "maxResults": 10
}' |
apify call santamaria-automations/tori-fi-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,santamaria-automations/tori-fi-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/tqZCdbYvYkVlYHZlT/builds/koJfyvDL89df6EbcG/openapi.json
