# jobs.ch Switzerland Job Listings Scraper (`automation-lab/jobs-ch-listings-scraper`) Actor

Search jobs.ch and export complete Swiss vacancy records with employers, locations, workload, descriptions, posting dates, and application URLs.

- **URL**: https://apify.com/automation-lab/jobs-ch-listings-scraper.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 2 total users, 1 monthly users, 50.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.96 / 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

## jobs.ch Switzerland Job Listings Scraper

Search jobs.ch and export complete Swiss vacancy records for recruiting feeds, hiring research, and labor-market analysis. The Actor accepts occupation keywords, a Swiss location, pre-filtered jobs.ch search URLs, and individual vacancy URLs.

Each result includes the job title, employer, structured location, workload range, employment classifications, posting context, full description, canonical jobs.ch URL, and direct application destination when the employer publishes one.

### What can this jobs scraper do?

- Search current jobs.ch vacancies by one or more keywords.
- Apply one location to generated searches.
- Preserve filters from supplied jobs.ch search URLs.
- Process individual public vacancy detail URLs.
- Follow result pagination up to a configurable safety limit.
- Export full text and HTML descriptions.
- Deduplicate vacancies by their stable jobs.ch UUID.
- Produce JSON, CSV, Excel, XML, RSS, or another Apify dataset export.
- Run on an Apify Schedule to refresh a recruiting feed.

The Actor reads server-rendered public jobs.ch pages over HTTP. It does not require a jobs.ch account, browser, or automatic residential proxy fallback.

### Who is this Actor for?

**Recruiting teams** can build focused candidate-market and vacancy feeds for roles they hire repeatedly.

**Job aggregators** can normalize current Swiss listings into a downstream search index or spreadsheet.

**Labor-market analysts** can compare employers, locations, workload percentages, and posting dates over repeated dataset snapshots.

**Data engineers** can send scheduled run results to a warehouse, webhook, Make, Zapier, Google Sheets, or another Apify Actor.

**Job seekers and career services** can collect a bounded set of relevant vacancies for review without copying pages manually.

### What data does it extract?

| Field | Meaning |
| --- | --- |
| `jobId` | Stable jobs.ch vacancy UUID |
| `title` | Advertised job title |
| `company`, `companyId`, `companyUrl` | Employer identity and public jobs.ch profile |
| `location` | Primary display location |
| `locations` | Structured address, canton, country, and coordinates when published |
| `workloadMin`, `workloadMax` | Advertised workload percentage range |
| `employmentTypeIds` | jobs.ch employment-type classifications |
| `employmentPositionIds` | jobs.ch employment-level classifications |
| `languageSkills` | Requested languages and source proficiency levels |
| `skills` | Skill labels when jobs.ch publishes them |
| `descriptionText` | Normalized plain-text vacancy description |
| `descriptionHtml` | Original description HTML |
| `publicationDate` | Current publication timestamp |
| `initialPublicationDate` | Initial posting timestamp, useful for repost analysis |
| `isActive` | Whether the source marks the job as active |
| `applicationUrl` | Direct application destination when available |
| `applicationMethod` | Source application-method classification |
| `canonicalUrl` | Canonical public jobs.ch detail URL |
| `searchContext` | Search URL that discovered the job |
| `scrapedAt` | Timestamp when this Actor produced the row |

Source fields can be `null` when an employer does not publish them. Employment IDs are preserved as source values rather than given speculative labels.

### How to scrape jobs.ch vacancies

1. Open the Actor input page.
2. Enter one or more values in **Search terms**, such as `software engineer` or `data analyst`.
3. Optionally enter a location such as `Zurich`, `Bern`, or `Basel`.
4. Set **Maximum jobs** to the number of complete records you need.
5. Click **Start**.
6. Open the **Dataset** tab to review or export the results.

For an existing jobs.ch filter combination, paste its search URL into **jobs.ch search or vacancy URLs**. For one known vacancy, paste its detail URL instead.

### Input parameters

| Input | Type | Default | Description |
| --- | --- | --- | --- |
| `searchTerms` | string array | `software engineer` | Occupations, titles, or keywords searched on jobs.ch |
| `location` | string | optional | City, canton, or region applied to generated searches |
| `startUrls` | request list | optional | Public jobs.ch search or vacancy detail URLs |
| `maxItems` | integer | `50` | Maximum complete vacancy rows across all inputs |
| `maxPages` | integer | `20` | Maximum result pages fetched per search seed |
| `proxyConfiguration` | object | direct | Optional Apify Proxy settings |

Provide at least one search term or start URL. Only public `jobs.ch` vacancy search and detail URLs are accepted. Unsupported domains fail with a clear non-zero error.

#### Example input

```json
{
  "searchTerms": ["data analyst"],
  "location": "Zurich",
  "maxItems": 10,
  "maxPages": 2
}
```

#### Use a filtered jobs.ch URL

```json
{
  "startUrls": [
    { "url": "https://www.jobs.ch/en/vacancies/?term=project%20manager&location=Bern" }
  ],
  "maxItems": 20,
  "maxPages": 3
}
```

### Output example

This shortened example reflects the current output shape; values vary with live vacancies.

```json
{
  "jobId": "11111111-2222-4333-8444-555555555555",
  "title": "Software Engineer",
  "company": "Example Technology AG",
  "location": "Zurich",
  "locations": [
    { "postalCode": "8000", "city": "Zurich", "cantonCode": "ZH", "countryCode": "CH" }
  ],
  "workloadMin": 80,
  "workloadMax": 100,
  "employmentTypeIds": ["5"],
  "languageSkills": [{ "language": "de", "level": 3 }],
  "descriptionText": "Join our engineering team...",
  "publicationDate": "2026-08-20T09:00:00+02:00",
  "initialPublicationDate": "2026-08-15T09:00:00+02:00",
  "isActive": true,
  "applicationUrl": "https://careers.example.org/jobs/engineering-role/apply",
  "applicationMethod": "APPLICATION_METHOD.EXTERNAL",
  "canonicalUrl": "https://www.jobs.ch/en/vacancies/detail/11111111-2222-4333-8444-555555555555/",
  "scrapedAt": "2026-08-31T12:00:00.000Z"
}
```

### How much does it cost to scrape jobs.ch job listings?

The Actor uses pay-per-event pricing:

- **Actor start:** $0.005 once per run.
- **Job listing:** the active tier price for each complete vacancy saved. The BRONZE reference price is $0.0016 per job, with lower per-item rates on higher Apify tiers.

At the BRONZE reference rate, calculate a run as the $0.005 start event plus the number of complete jobs multiplied by $0.0016. For example, use `0.005 + (10 × 0.0016)` for 10 jobs, `0.005 + (100 × 0.0016)` for 100 jobs, or `0.005 + (1000 × 0.0016)` for 1,000 jobs.

Only complete saved vacancies produce job-listing events. Duplicate, rejected, or failed records do not. Apify plan tier and platform rounding can affect the final amount shown in Console.

### Build a recurring Swiss recruiting feed

Create an Apify Task with a stable keyword/location combination or a pre-filtered jobs.ch URL. Attach a Schedule, then consume each run's default dataset through the API or an integration.

A common workflow is:

1. Run daily with a bounded `maxItems`.
2. Save `jobId`, `publicationDate`, and `initialPublicationDate` in your database.
3. Upsert by `jobId`.
4. Flag IDs first seen in the newest run as new vacancies.
5. Compare changed posting fields without treating an updated timestamp as a new identity.

The Actor does not maintain a hidden cross-run history or send alerts itself. Your dataset consumer owns comparison, retention, and notification logic.

### Export to spreadsheets and data pipelines

From the Dataset tab, choose JSON, CSV, Excel, XML, or RSS. For automation, use the dataset API URL returned with the run.

Useful integrations include:

- Google Sheets for recruiter review queues.
- Make or Zapier for workflow routing.
- Webhooks for triggering an ETL job after a successful run.
- Snowflake, BigQuery, PostgreSQL, or object storage for longitudinal analysis.
- Another Actor for deduplication, enrichment, or delivery.

Keep the stable `jobId` as your primary source key. Use `scrapedAt` as collection metadata, not as the vacancy publication date.

### Run through the Apify API

Replace `YOUR_APIFY_TOKEN` with your token.

#### cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~jobs-ch-listings-scraper/runs?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"searchTerms":["software engineer"],"location":"Zurich","maxItems":10}'
```

#### JavaScript

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/jobs-ch-listings-scraper').call({
  searchTerms: ['software engineer'],
  location: 'Zurich',
  maxItems: 10,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("automation-lab/jobs-ch-listings-scraper").call(run_input={
    "searchTerms": ["software engineer"],
    "location": "Zurich",
    "maxItems": 10,
})
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items)
```

### Use with MCP and AI assistants

#### Claude Code

Add the Actor to Claude Code through Apify's MCP server:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/jobs-ch-listings-scraper"
```

#### Claude Desktop, Cursor, and VS Code

Claude Desktop, Cursor, and VS Code can use equivalent MCP JSON:

```json
{
  "mcpServers": {
    "apify": {
      "type": "http",
      "url": "https://mcp.apify.com?tools=automation-lab/jobs-ch-listings-scraper"
    }
  }
}
```

Example prompts:

- “Find 20 current data analyst jobs in Zurich and summarize the advertised workload ranges.”
- “Export project manager vacancies from this jobs.ch search URL and group them by employer.”
- “Run my saved Swiss software engineering feed and return IDs that are absent from yesterday's table.”

An assistant can run the Actor, but it needs your Apify authorization and remains subject to your chosen limits and charges.

### Reliability, limits, and failure behavior

- jobs.ch is a live third-party source; fields and availability can change.
- Search relevance and location interpretation are controlled by jobs.ch.
- Some employers do not publish an external application URL, structured skills, or a complete address.
- The Actor uses direct HTTP by default and does not download vacancy images or videos.
- Transient network, HTTP 429, and upstream 5xx failures receive up to three bounded attempts.
- A missing expected page-state object or mismatched detail ID fails loudly instead of returning a misleading empty success.
- `maxPages` bounds discovery work; `maxItems` bounds the number of saved records.
- Multiple seeds are deduplicated by vacancy UUID.

If a run unexpectedly returns no jobs, open the source search URL in a browser and confirm the same filters currently have results.

### Responsible use and legality

Use this Actor only for lawful purposes and public data you are entitled to process. Respect jobs.ch terms, applicable copyright and database rights, privacy rules, and the rights of employers and applicants.

Do not use vacancy data for discriminatory decisions, spam, impersonation, or intrusive profiling. Avoid collecting more data than your workflow needs, set reasonable schedules, and define retention and deletion rules for exported datasets.

This Actor is an independent automation tool and is not affiliated with or endorsed by jobs.ch or JobCloud.

### FAQ and troubleshooting

#### Why did my URL fail immediately?

The Actor accepts only public jobs.ch vacancy search and detail URLs. Company, account, application-form, and third-party URLs are intentionally rejected. Use a search URL under `/vacancies/` or a public vacancy detail URL.

#### Why is `applicationUrl` null?

The employer may not publish a direct external destination in the public detail record. `canonicalUrl` still points to the jobs.ch vacancy page.

#### Why are there fewer rows than `maxItems`?

`maxItems` is a ceiling, not a promise. The search can exhaust its available pages, duplicate IDs can appear across seeds, or a source record can be skipped if it lacks a valid title.

#### Can it monitor changes automatically?

Schedule the Actor and compare stable IDs in your own destination. The Actor exports fresh records but does not keep hidden historical state or send alerts.

#### Does it need a Swiss proxy?

Not currently. The required public server-rendered data works through direct HTTP. You can supply an Apify proxy configuration if your environment requires one; there is no automatic residential fallback.

### Related automation-lab Actors

- [Jobup.ch Swiss Job Listings Scraper](https://apify.com/automation-lab/jobup-ch-swiss-job-listings-scraper) for French-speaking Swiss vacancy searches.
- [Company Career Page Jobs Scraper](https://apify.com/automation-lab/company-career-page-jobs-scraper) for public employer career pages outside a single job board.
- [Dataset Dedup](https://apify.com/automation-lab/dataset-dedup) for removing duplicate records in downstream dataset workflows.

### Support

If jobs.ch changes its public page shape or a valid vacancy URL fails, open an issue from the Actor page. Include a redacted input, run ID, expected behavior, and the first relevant error line. Do not include Apify tokens or private applicant information.

# Actor input Schema

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

Job titles, occupations, or keywords to search on jobs.ch.

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

Optional Swiss city, canton, or region applied to every generated search.

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

Optional public jobs.ch search URLs (including their filters) or individual vacancy detail URLs.

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

Maximum number of complete vacancy records saved across all inputs.

## `maxPages` (type: `integer`):

Safety limit for jobs.ch result pages visited for each search URL or term.

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

Optional Apify Proxy settings. Direct requests are used by default because jobs.ch currently serves the required public pages without a proxy.

## Actor input object example

```json
{
  "searchTerms": [
    "software engineer"
  ],
  "location": "Zurich",
  "startUrls": [
    {
      "url": "https://www.jobs.ch/en/vacancies/?term=data%20analyst"
    }
  ],
  "maxItems": 20,
  "maxPages": 20,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

Dataset view with job, employer, location, workload, posting, and application fields.

# 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": [
        "software engineer"
    ],
    "location": "Zurich",
    "startUrls": [
        {
            "url": "https://www.jobs.ch/en/vacancies/?term=data%20analyst"
        }
    ],
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/jobs-ch-listings-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": ["software engineer"],
    "location": "Zurich",
    "startUrls": [{ "url": "https://www.jobs.ch/en/vacancies/?term=data%20analyst" }],
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/jobs-ch-listings-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": [
    "software engineer"
  ],
  "location": "Zurich",
  "startUrls": [
    {
      "url": "https://www.jobs.ch/en/vacancies/?term=data%20analyst"
    }
  ],
  "maxItems": 20
}' |
apify call automation-lab/jobs-ch-listings-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/jobs-ch-listings-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/8hZKgsBLxg6Z2GMrq/builds/xXK0cEFpRncDhfn56/openapi.json
