# local.ch Scraper (`santamaria-automations/local-ch-scraper`) Actor

Scrape business listings from local.ch, the Swiss business directory. Returns name, address, phone, email, website, categories, rating, opening hours and coordinates. No login required. Pay-per-result.

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

## Pricing

Pay per event

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

## local.ch Scraper - Swiss Business Directory

Scrape business listings from [local.ch](https://www.local.ch), the Swiss business directory (Swisscom Directories). Search by keyword and city and get phone numbers, emails, websites, addresses, categories, ratings and opening hours.

### What data can you extract?

For each business, the scraper extracts:

- **Business name**, subtitles and all categories
- **Phone, mobile and fax numbers** (international format)
- **Email address** and **website URL**
- **Social media profile links**
- **Full address** with street, postal code, city and canton
- **GPS coordinates** (latitude/longitude)
- **Opening hours** and **description** (detail mode)
- **Rating and number of ratings**
- **Logo image URL**
- **Detail mode extras**: description headline, online booking link, spoken languages, location features, payment methods, all attribute groups, rating distribution per star and the `*` no-advertising marker

Phone, email, website and social links are available from the search results alone, so they are included even when detail pages are switched off.

### Use with AI Agents (MCP)

Connect this actor to any MCP-compatible AI client: Claude Desktop, Claude.ai, Cursor, VS Code, LangChain, LlamaIndex, or custom agents.

**Claude Desktop / Cursor config:**

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

Example prompt: "Use `local-ch-scraper` to find dentists in Zürich and return a table with name, phone, email and website."

Clients that support dynamic tool discovery (Claude.ai, VS Code) will receive the full input schema automatically via `add-actor`.

### How to use

```json
{
  "searchTerms": ["Zahnarzt", "Bäckerei"],
  "location": "Zürich",
  "maxResults": 40,
  "includeDetails": true,
  "language": "de"
}
```

An empty input `{}` also works and runs the defaults (Zahnarzt in Zürich, 20 results).

### Input parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `searchTerms` | array | `["Zahnarzt"]` | Business types or names. Each term is searched separately |
| `location` | string | `Zürich` | Swiss city, region or postal code |
| `maxResults` | integer | `20` | Maximum unique businesses per search term (1-10,000, site limit 180 per search) |
| `includeDetails` | boolean | `true` | Fetch detail pages for opening hours, description and subtitles |
| `language` | string | `de` | Site language: `de`, `fr`, `it` or `en` |

A business that matches several search terms is returned once.

**Limit:** local.ch serves at most 9 result pages (180 businesses) per search, so one search term and location returns up to 180 businesses even when `maxResults` is higher. Split large jobs into several search terms or more specific locations.

### Output example

```json
{
  "id": "2XCS66MZnVWZKva3NVsOag",
  "name": "Cierny Zahnärzte",
  "description": "Die Praxis Cierny wird mittlerweile in der 3. Generation der Familie Cierny geführt ...",
  "subtitles": ["eidg. dipl. Arzt / eidg. dipl. Zahnarzt"],
  "category": "Zahnarzt",
  "categories": ["Zahnarzt", "Implantologie", "Dentalhygiene"],
  "phone": "+41444318242",
  "phones": ["+41444318242"],
  "mobile": null,
  "fax": null,
  "email": "praxis@cierny-zaz.ch",
  "website": "http://www.cierny-zaz.ch",
  "social_links": ["https://www.facebook.com/Cierny-Zahn%C3%A4rzte-584954974923275"],
  "address": "Segnesstrasse 9, 8048 Zürich",
  "street": "Segnesstrasse 9",
  "postal_code": "8048",
  "city": "Zürich",
  "canton": "ZH",
  "country": "Switzerland",
  "country_code": "CH",
  "latitude": 47.38837,
  "longitude": 8.485818,
  "opening_hours": "Mo 08:00-12:00,13:00-17:00; Tu 07:00-15:00; We 08:00-12:00,13:00-17:00",
  "rating": 4.8,
  "review_count": 1231,
  "logo_url": "https://bin.staticlocal.ch/localplace-logo/5d/example.jpg",
  "description_title": "Wir sind zahnfreundlich. Ihr Zahnarzt in Zürich",
  "refuse_advertising": true,
  "refuse_advertising_contacts": ["+41444318242"],
  "booking_url": "https://oa.zawin.ch/k1144_01",
  "booking_provider": "custom",
  "languages": ["Deutsch", "Englisch", "Französisch"],
  "location_features": ["Im Zentrum", "In Bahnhofsnähe", "Parkmöglichkeit"],
  "payment_methods": ["Barzahlung", "Bezahlung mit EC-Karte", "TWINT", "Visa"],
  "attributes": {
    "Sprachen": ["Deutsch", "Englisch", "Französisch"],
    "Lage": ["Im Zentrum", "In Bahnhofsnähe", "Parkmöglichkeit"],
    "Zahlungskonditionen": ["Barzahlung", "Bezahlung mit EC-Karte", "TWINT", "Visa"]
  },
  "rating_distribution": {"1": 0, "2": 0, "3": 0, "4": 0, "5": 2},
  "entry_type": "BUSINESS",
  "search_query": "Zahnarzt",
  "search_location": "Zürich",
  "source_url": "https://www.local.ch/de/d/zuerich/8048/zahnarzt/cierny-zahnaerzte-2XCS66MZnVWZKva3NVsOag",
  "source_platform": "local.ch",
  "scraped_at": "2026-10-18T12:00:00Z"
}
```

Fields that local.ch does not publish for an entry are `null` (or an empty list).

#### Output fields

| Field | Type | Description |
|-------|------|-------------|
| `id` | string | local.ch entry id |
| `name` | string | Business name |
| `description` | string / null | Business description (detail mode) |
| `subtitles` | array | Subtitle lines under the name (titles, specialities) |
| `category` | string / null | Main category |
| `categories` | array | All categories |
| `phone` | string / null | Main phone number (international format) |
| `phones` | array | All phone numbers |
| `mobile` | string / null | Mobile number |
| `fax` | string / null | Fax number |
| `email` | string / null | Contact email |
| `website` | string / null | Website URL |
| `social_links` | array | Social media profile URLs |
| `address` | string / null | Full address |
| `street` | string / null | Street and number |
| `postal_code` | string / null | Postal code |
| `city` | string / null | City |
| `canton` | string / null | Canton code |
| `country` | string | Country name |
| `country_code` | string | ISO country code |
| `latitude` | number / null | Latitude |
| `longitude` | number / null | Longitude |
| `opening_hours` | string / null | Weekly opening hours (detail mode) |
| `rating` | number / null | Average rating |
| `review_count` | integer / null | Number of ratings |
| `logo_url` | string / null | Logo image URL |
| `entry_type` | string | Entry type (BUSINESS or PERSON) |
| `search_query` | string | Search term that produced the row |
| `search_location` | string | Location of the search |
| `source_url` | string | local.ch detail page URL |
| `source_platform` | string | Always local.ch |
| `scraped_at` | string | Scrape timestamp (UTC) |
| `description_title` | string / null | Headline of the business description (detail mode) |
| `refuse_advertising` | boolean / null | Detail mode. true when the entry is marked with \* on local.ch (the owner does not want advertising calls); null without detail data |
| `refuse_advertising_contacts` | array | Detail mode. Contact values (phone, fax, ...) that carry the \* marker |
| `booking_url` | string / null | Online booking link (detail mode, if the entry has an external booking channel) |
| `booking_provider` | string / null | Booking provider of the online booking channel (detail mode) |
| `languages` | array | Spoken languages (detail mode) |
| `location_features` | array | Location features such as 'Im Zentrum' or 'Parkmöglichkeit' (detail mode) |
| `payment_methods` | array | Accepted payment methods (detail mode) |
| `attributes` | object | Detail mode. All attribute groups as {group name: \[attribute names]}, e.g. treatments or practice type |
| `rating_distribution` | object / null | Detail mode. Number of ratings per star, e.g. {"5": 2, "4": 0}; null when not published |

#### Detail fields

These fields are filled when `includeDetails` is on and the detail page was loaded. On search-only rows the lists are empty, `attributes` is `{}` and the other fields are `null`.

| Field | Type | Description |
|-------|------|-------------|
| `description_title` | string / null | Headline of the business description |
| `refuse_advertising` | boolean / null | `true` when the entry is marked with \* on local.ch: the owner does not want advertising calls. `false` when no contact is marked |
| `refuse_advertising_contacts` | array | The contact values (phone, fax, ...) that carry the \* marker, `[]` if none |
| `booking_url` | string / null | Online booking link, if the entry has one |
| `booking_provider` | string / null | Booking provider of that link |
| `languages` | array | Spoken languages |
| `location_features` | array | Location features, e.g. "Im Zentrum", "Parkmöglichkeit" |
| `payment_methods` | array | Accepted payment methods |
| `attributes` | object | Every attribute group as `{group name: [attribute names]}`, e.g. treatments or practice type |
| `rating_distribution` | object / null | Number of ratings per star (`"1"` to `"5"`), when published |

### Pricing

Pay per result. You are charged one result event for every row saved to the dataset, plus a one-time start event per run.

| Event | Price | When |
|-------|-------|------|
| `directory-start` | $0.005 per run | Charged once, right before the first row is saved |
| `directory-serp-result` | $0.0015 per business | Business saved from search results only |
| `directory-detail-result` | $0.0025 per business | Business saved with full detail data |

Duplicates are never saved or charged twice, and a restarted run does not charge rows it already saved.

### Performance

- **Speed**: 20 businesses per search page; detail pages add a little time per business
- **Search only**: set `includeDetails` to `false` for the fastest and cheapest run

### Tips

- Use the language of your terms: German terms work best with `de`, French terms with `fr`
- Use a narrow location or several specific search terms to reach more businesses
- Pair the `website` field with the actors below to find more contacts per company

### Related Actors

**Swiss business data**

- [search.ch Scraper](https://apify.com/santamaria-automations/search-ch-scraper) -- Swiss business directory
- [Moneyhouse.ch Scraper](https://apify.com/santamaria-automations/moneyhouse-ch-scraper) -- Swiss company registry with management and financials
- [Zefix.ch Scraper](https://apify.com/santamaria-automations/zefix-ch-scraper) -- Official Swiss commercial register

**Enrich your leads**

- [Website Email & Phone Scraper](https://apify.com/santamaria-automations/website-email-scraper) -- Extract emails and phones from company websites
- [Website Contact Extractor](https://apify.com/santamaria-automations/website-contact-extractor) -- Extract team members and decision-makers

### Data protection

This actor returns only data that is publicly visible on the source pages. Results can include personal data, such as names, phone numbers, email addresses and addresses of private persons, sole traders or contact persons. Where the source labels an entry type, it is included so you can filter the results yourself.

You are responsible for using the data lawfully, including the GDPR/DSGVO, national data-protection laws and unfair-competition rules (e.g. UWG) for marketing and outreach.

# Actor input Schema

## `searchTerms` (type: `array`):

One or more business types or names to search for (e.g. Zahnarzt, Bäckerei, Treuhand). Each term is searched separately and maxResults applies per term. Defaults to Zahnarzt when empty.

## `location` (type: `string`):

Swiss city, region or postal code (e.g. Zürich, Bern, 8001). Defaults to Zürich when empty.

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

Maximum number of unique businesses to return for each search term. local.ch lists at most 180 businesses per search, so use several terms or locations for more.

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

Fetch each business detail page for opening hours, the full description and subtitles. Phone, email and website are already included without it. Slower and billed at the detail rate.

## `language` (type: `string`):

Language of the local.ch site, which controls the language of category names and descriptions.

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

Defaults to a residential Switzerland proxy, which local.ch needs when the actor runs on Apify. Turn it off only for runs from a Swiss residential connection.

## Actor input object example

```json
{
  "searchTerms": [
    "Zahnarzt"
  ],
  "location": "Zürich",
  "maxResults": 20,
  "includeDetails": true,
  "language": "de",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "CH"
  }
}
```

# Actor output Schema

## `businesses` (type: `string`):

Dataset containing scraped business data

# 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 = {
    "searchTerms": [
        "Zahnarzt"
    ],
    "location": "Zürich",
    "maxResults": 20,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "CH"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("santamaria-automations/local-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 = {
    "searchTerms": ["Zahnarzt"],
    "location": "Zürich",
    "maxResults": 20,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "CH",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("santamaria-automations/local-ch-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 '{
  "searchTerms": [
    "Zahnarzt"
  ],
  "location": "Zürich",
  "maxResults": 20,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "CH"
  }
}' |
apify call santamaria-automations/local-ch-scraper --silent --output-dataset

```

## MCP server setup

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