# Tutti.ch Scraper — Swiss Classifieds (`studio-amba/tutti-ch-scraper`) Actor

Scrape tutti.ch, Switzerland's largest classifieds marketplace. Extract listings with prices, city, canton, seller name, images, and posting dates from keyword searches or categories. Filter by canton for regional market research. No login required.

- **URL**: https://apify.com/studio-amba/tutti-ch-scraper.md
- **Developed by:** [Studio Amba](https://apify.com/studio-amba) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 result scrapeds

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/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

## Tutti.ch Scraper — Swiss Classifieds

Extract classified listings and prices from [tutti.ch](https://www.tutti.ch), Switzerland's largest classifieds marketplace. Search by keyword, browse categories, filter by canton, and get structured data with prices in CHF, location, seller name, images, and posting dates. No login or cookies required.

### Why use this scraper?

Tutti.ch is Switzerland's go-to classifieds platform (part of SMG Swiss Marketplace Group, alongside ricardo.ch and homegate.ch). Millions of second-hand items, vehicles, furniture, and electronics are listed by private sellers across all 26 cantons. This scraper turns those listings into clean JSON you can use for:

- **Price research** — see what second-hand goods actually cost on the Swiss market
- **Resale arbitrage** — spot underpriced items by region or category
- **Regional market analysis** — compare supply and prices per canton
- **Lead generation** — find sellers of specific product types
- **Deal alerts** — run on a schedule and diff new listings for your saved searches

### Features

- Keyword search with automatic pagination (~30 listings per page)
- Category browsing (cars, bicycles, furniture, real estate, and more)
- Canton filter — only get listings from the region you care about
- Prices, city, canton, seller name, thumbnail, and exact posting timestamp
- Clean, flat JSON output — no HTML parsing needed on your side
- Handles Cloudflare protection via Bright Data Web Unlocker

### How to scrape Tutti.ch data

1. Get a [Bright Data](https://brightdata.com) API key with a Web Unlocker zone named `web_unlocker1` (tutti.ch sits behind a Cloudflare managed challenge, so a plain proxy is not enough)
2. Enter a **Search Query** (e.g. `velo`, `iphone`, `sofa`)
3. Optionally set a **Canton** (e.g. `ZH`, `bern`) and/or a **Category** (e.g. `bicycles`, `cars`)
4. Set **Max Results** to the number of listings you need
5. Paste your Bright Data API key into the **Bright Data API Key** field
6. Run the actor and download your data as JSON, CSV, or Excel

The scraper reads the server-rendered search results, so each page fetch returns ~30 fully-structured listings including the seller name and exact posting time.

#### Canton filtering

The `canton` input accepts any of these forms:

- Two-letter code: `ZH`, `BE`, `VD`, `TI`
- Tutti URL slug: `zuerich`, `bern`, `waadt`, `tessin`
- Canton name: `Zürich`, `Bern`

Filtering happens on the listing location, so the actor keeps paginating until it has collected `maxResults` listings from that canton (or runs out of results).

#### Categories

The `category` input takes tutti.ch's internal category keys, for example: `cars`, `bicycles`, `furniture`, `realEstate`, `electronics`, `motorcycles`. Combine it with a search query or use it alone to browse the whole category.

### Input

| Field | Type | Description |
|-------|------|-------------|
| `searchQuery` | string | Keyword to search for. Default: `velo`. |
| `category` | string | Optional category key (e.g. `bicycles`, `cars`). |
| `canton` | string | Optional canton filter (code, slug, or name). |
| `maxResults` | integer | Maximum listings to return. Default: 100. |
| `brightDataApiKey` | string (secret) | Your Bright Data Web Unlocker API key. Required. |
| `proxyConfiguration` | object | Kept for compatibility — not used (Bright Data handles anti-bot). |

#### Example input

```json
{
    "searchQuery": "velo",
    "canton": "ZH",
    "maxResults": 20
}
```

### Output

| Field | Type | Description |
|-------|------|-------------|
| `id` | string | Unique tutti.ch listing ID |
| `title` | string | Listing title |
| `price` | number | Asking price in CHF (0 for free items, null for "price on request") |
| `currency` | string | Always `CHF` |
| `priceType` | string | `negotiable` — classifieds prices are asking prices |
| `city` | string | Listing location (city or municipality) |
| `canton` | string | Two-letter canton code (e.g. `ZH`, `LU`) |
| `sellerName` | string | Seller display name (alias) |
| `imageUrl` | string | Primary listing image (thumbnail) |
| `listingDate` | string | ISO 8601 timestamp when the listing was posted |
| `category` | string | Category key, e.g. `bicycles` |
| `url` | string | Full listing URL |
| `scrapedAt` | string | ISO 8601 scrape timestamp |

#### Example output

```json
{
    "id": "81868234",
    "title": "Kindervelo",
    "price": 20,
    "currency": "CHF",
    "priceType": "negotiable",
    "city": "Schüpfheim",
    "canton": "LU",
    "sellerName": "patrickwo8235",
    "imageUrl": "https://c.tutti.ch/thumbnail/7686127849.jpg",
    "listingDate": "2026-07-11T21:32:45+02:00",
    "category": "bicycles",
    "url": "https://www.tutti.ch/de/vi/luzern/sport-outdoor/velos/kindervelo/81868234",
    "scrapedAt": "2026-07-11T19:42:33.714Z"
}
```

### Cost estimate

Each page fetch returns ~30 listings, so a 100-listing run needs about 4 requests and a 1,000-listing run about 34. Compute usage is minimal (no browser). Expect thousands of listings per dollar of platform credits, plus your Bright Data usage (roughly $0.0015 per page fetch — about $0.05 per 1,000 listings).

### Limitations

- Condition, brand, and full descriptions are not shown on tutti.ch search pages; scraping them would require one extra request per listing. Not included in this version.
- Tutti.ch has no auctions — `priceType` is always `negotiable` and `auctionEndDate` stays empty (the field exists for schema parity with our Ricardo.ch scraper).
- Canton filtering happens after fetching, so heavily filtered runs fetch more pages than unfiltered ones.

### Related scrapers

- **[Ricardo.ch Scraper](https://apify.com/studio-amba/ricardo-ch-scraper)** — Swiss marketplace auctions and buy-now listings from ricardo.ch, Tutti's sibling platform (same SMG group).
- **[Homegate Scraper](https://apify.com/studio-amba/homegate-scraper)** — Swiss real estate listings from homegate.ch.
- **[Comparis Scraper](https://apify.com/studio-amba/comparis-scraper)** — Swiss insurance and telecom comparison data.
- **[Brack Scraper](https://apify.com/studio-amba/brack-scraper)** — Swiss electronics and IT products from brack.ch.
- **[AutoScout24 Scraper](https://apify.com/studio-amba/autoscout24-scraper)** — European used car listings.
- Browse the [Apify Store e-commerce category](https://apify.com/store/categories/ecommerce) for more marketplace scrapers from Studio AMBA.

### FAQ

**Do I need a tutti.ch account?**
No. The scraper only reads public search pages. No login, no cookies.

**Why do I need a Bright Data key?**
Tutti.ch is protected by a Cloudflare managed challenge that blocks datacenter IPs and headless browsers. Bright Data's Web Unlocker solves this server-side. A free trial account is enough to test.

**Is this legal?**
The scraper only collects publicly visible listing data, the same information any visitor sees. You are responsible for how you use the data — check local regulations for your use case.

**How fresh is the data?**
Search results are sorted by posting date, and each listing includes its exact posting timestamp, so scheduled runs can pick up new listings within minutes of posting.

# Actor input Schema

## `searchQuery` (type: `string`):

Keyword to search for (e.g. 'velo', 'iphone', 'sofa'). Results are paginated automatically (~30 listings per page).

## `category` (type: `string`):

Optional tutti.ch category key to browse instead of / in addition to the keyword, e.g. 'cars', 'bicycles', 'realEstate', 'furniture'. Leave empty to search all categories.

## `canton` (type: `string`):

Optional canton filter. Accepts the two-letter code ('ZH', 'BE', 'VD'), the tutti URL slug ('zuerich', 'bern', 'waadt'), or the canton name. Only listings located in that canton are returned.

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

Maximum number of listings to return.

## `brightDataApiKey` (type: `string`):

Your Bright Data API key for the Web Unlocker zone. Required to bypass tutti.ch's Cloudflare protection. Get one at https://brightdata.com

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

Kept for compatibility — requests are routed through Bright Data Web Unlocker, so Apify proxies are not used.

## Actor input object example

```json
{
  "searchQuery": "velo",
  "maxResults": 20,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# 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 = {
    "searchQuery": "velo",
    "maxResults": 20,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("studio-amba/tutti-ch-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 = {
    "searchQuery": "velo",
    "maxResults": 20,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("studio-amba/tutti-ch-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchQuery": "velo",
  "maxResults": 20,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call studio-amba/tutti-ch-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=studio-amba/tutti-ch-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/S4zKEXurZ95GL2nv9/builds/GbXxqoUcSePHNbns3/openapi.json
