# jobs.ch Scraper (`deriverge/jobs-ch-scraper`) Actor

Swiss job ads from jobs.ch with the workload in percent, contract type, canton and the full description. Search in German, French or English by keyword and town, filter by workload or contract, or paste a jobs.ch search link.

- **URL**: https://apify.com/deriverge/jobs-ch-scraper.md
- **Developed by:** [deriverge s.r.o.](https://apify.com/deriverge) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.25 / 1,000 jobs

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?

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

## jobs.ch Scraper

jobs.ch is a Swiss job board that listed 41,730 jobs on 11 October 2026, written in German, French or English. This actor returns one row per job: title, employer and whether jobs.ch files it as an SME, a large company or a recruitment agency, the workload such as `80-100%`, contract type, position level, the workplace with postcode and canton, required languages, benefits, the apply link and the full description. The form starts with the keyword `python`: click **Start** for 20 jobs. jobs.ch shows at most 2,000 jobs per search. It costs $0.50 per 1,000 jobs on the Free plan ($0.25 on Business), and the $5 of monthly credit in Apify's Free plan covers about 10,000 jobs.

### Fields in each job

| Field | Description |
|---|---|
| `title`, `company` | Job title as the employer wrote it and the employer's name, empty when the ad hides it. |
| `companyType` | With details on: `SME`, `Large company` or `Recruitment agency`, as jobs.ch classifies the advertiser. |
| `workload`, `workloadMin`, `workloadMax` | Share of full time as text, e.g. `80-100%`, and its two ends as numbers, `80` and `100`. |
| `employmentTypes` | Contract types such as `Permanent`, `Temporary`, `Freelance`, `Internship`, `Apprenticeship` or `Supplementary income`. |
| `positionLevel` | `Employee`, `With responsibilities` or `Executive`. |
| `place` | Workplace as the result list shows it, e.g. `Zürich` or `Bern, Biel, Zürich, Luzern` for a job in several towns. |
| `street`, `postcode`, `city`, `canton` | With details on: the workplace address and the two-letter canton code, such as `ZH`, `BE` or `GE`. |
| `latitude`, `longitude` | With details on: coordinates of the workplace. |
| `occupation` | With details on: the occupation jobs.ch assigns, e.g. `Web Administrator` or `Nurse`. |
| `languages`, `skills`, `benefits` | With details on: languages the employer asks for, skill tags, and benefits such as `Flexible working hours` or `Training`. |
| `easyApply`, `applyUrl` | `true` when the job takes a quick application on jobs.ch. For the other jobs, `applyUrl` (with details on) usually points to the employer's application page. |
| `publishedAt`, `firstPublishedAt`, `expiresAt` | Latest and first publication time, and with details on the end date jobs.ch holds for the ad. |
| `preview`, `description`, `descriptionHtml` | With details on: the title with the opening words of the ad, about 140 characters, then the full ad as plain text and as HTML. |
| `detailsIncluded` | `false` when the job's details were not read, so only the search result fields are filled. |
| `url`, `companyUrl` | The job and the company profile on jobs.ch, in the language set by **Link language**. |

### How to scrape jobs.ch

1. Put one or more **Keywords** in German, French or English, such as `Pflege`, `Buchhaltung`, `comptable` or `data engineer`. Each keyword runs as its own search.
2. Add **Locations** as towns or postcodes (`Zürich`, `Lausanne`, `Basel`, `8001`). Every keyword is searched in every location, and an empty field means all of Switzerland. jobs.ch decides how far around a town it looks: its search pages have no radius setting, so **Radius (km)** has no effect.
3. Narrow the search on jobs.ch's side with **Contract types**, **Position levels**, **Minimum workload (%)**, **Maximum workload (%)** and **Sort by**. A job matches the minimum when its range reaches it: with 80, a job offered at `30-100%` stays in. The maximum keeps jobs that can be done at that share or less, so 60 also returns `60-100%`.
4. A search made on jobs.ch can be pasted into **Search links from jobs.ch (optional)**, e.g. `https://www.jobs.ch/en/vacancies/?term=python&location=Bern`. The term, location, contract, position, region, category, workload and sort in the link are kept.
5. Keep **Include full details** on for the address, canton, coordinates, company type, occupation, languages, skills, benefits, expiry date, apply link and description. Each job is then one more page and the actor reads one page a second, so plan about 18 minutes for 1,000 jobs; without details 1,000 jobs take about a minute.
6. Click **Start** and watch the rows arrive in **Output**, then export them as CSV, Excel or JSON or read them through the API.

As API input, permanent nursing jobs around Bern at 80% or more:

```json
{
  "keywords": ["Pflege"],
  "locations": ["Bern"],
  "minWorkload": 80,
  "employmentTypes": ["5"],
  "maxItems": 300
}
```

In the API, contract types are numbers: `5` is permanent, `1` temporary, `2` freelance, `3` internship, `6` apprenticeship and `4` supplementary income. Position levels are `3` for employee, `2` for a position with responsibilities and `1` for executive.

### Example output

One job from a run on 11 October 2026, with `description` and `descriptionHtml` cut after the opening paragraph.

```json
{
  "key": "jobsch:5f9964f1-86e3-4bb6-9f34-2cac4b1cdbb4",
  "jobId": "5f9964f1-86e3-4bb6-9f34-2cac4b1cdbb4",
  "title": "Data Engineer - Analytics Platform",
  "url": "https://www.jobs.ch/en/vacancies/detail/5f9964f1-86e3-4bb6-9f34-2cac4b1cdbb4/",
  "company": "Medgate AG",
  "companyUrl": "https://www.jobs.ch/en/companies/a9dc0f67-54f9-4bee-8087-6a8f11a75640-medgate-ag/",
  "companyLogo": "https://media.jobs.ch/media/6ccc96c7-1980-48db-865c-2f4525c59597",
  "companyType": "Large company",
  "place": "Allschwil",
  "street": "Lachenstrasse 1",
  "postcode": "4123",
  "city": "Allschwil",
  "canton": "BL",
  "latitude": 47.56249,
  "longitude": 7.55503,
  "workloadMin": 80,
  "workloadMax": 100,
  "workload": "80-100%",
  "employmentTypes": [
    "Permanent"
  ],
  "positionLevel": "With responsibilities",
  "occupation": "Data Architect",
  "languages": [
    "English"
  ],
  "skills": [],
  "benefits": [],
  "easyApply": false,
  "publishedAt": "2026-09-29T10:47:53+02:00",
  "firstPublishedAt": "2026-09-29T08:47:53+00:00",
  "expiresAt": "2026-10-30",
  "applyUrl": "https://app.jobportal.abaservices.ch/job-advertisement/aaa92995-6005-40c1-a11f-593620a026c2/b4b51611-f764-3179-4f38-71bbba18ce52",
  "preview": "Data Engineer - Analytics Platform Working place: Allschwil and Home- Office For 26 years, Medgate has been bringing doctors directly to ...",
  "description": "Working place: Allschwil and Home- Office\nFor 26 years, Medgate has been bringing doctors directly to patients 24/7 thanks to telemedicine. At Medgate Switzerland, more than 300 motivated employees - including over 130 permanently employed doctors - are actively shaping the medicine of tomorrow with the help of future-oriented digital health solutions. ...",
  "descriptionHtml": "<p> </p> <p> <strong>Working place: Allschwil and Home- Office</strong> </p> <p>For 26 years, Medgate has been bringing doctors directly to patients 24/7 thanks to telemedicine. At Medgate Switzerland, more than 300 motivated employees - including over 130 permanently employed doctors - are actively shaping the medicine of tomorrow with the help of future-oriented digital health solutions.</p> ...",
  "detailsIncluded": true,
  "searchQuery": "\"python\"",
  "scrapedAt": "2026-10-11T15:17:59.148Z"
}
```

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

| | Free plan | Starter | Scale | Business |
|---|---|---|---|---|
| 1,000 jobs | $0.50 | $0.40 | $0.325 | $0.25 |

You pay only for the events in the table. There is no start fee, and compute time and proxies are included.

Collecting 5,000 Swiss jobs with every detail costs $2.50 on the Free plan and $1.25 on Business. A job found by two of your searches is charged once, and jobs removed by **Exclude keywords** or already delivered in new-only mode are not charged.

### Limits

- jobs.ch stops a search at 2,000 jobs, 20 per page. On 11 October 2026 `Pflege` matched 2,789 jobs and `comptable` 1,093; the run log warns in such cases, and more keywords or locations split the search into reachable parts.
- The actor reads only the search result pages and job pages that jobs.ch's robots.txt allows, one page a second. An October 2026 run of 500 jobs with details took 9 minutes. If jobs.ch refuses a request or answers with a bot check, the actor sends no further requests and ends with the jobs collected until then.
- Address fields depend on the ad. In that run of 500 jobs, 468 had a postcode, 420 a canton, 320 coordinates and 353 a street. Without details these fields and the company type stay empty.
- Few employers fill in skills or benefits: 32 and 39 of those 500 jobs had them.
- `expiresAt` varies a lot: for 339 of the 500 jobs it fell within 45 days of the first publication, for 118 more than 300 days later. It is empty when jobs.ch holds a placeholder date years ahead (20 jobs).

### FAQ

#### Is it legal to scrape jobs.ch?

jobs.ch shows these job ads to anyone. The actor reads only the pages that jobs.ch's robots.txt allows for crawlers, one page a second, and does not collect the contact persons jobs.ch lists with an ad. Descriptions are the employers' own texts and can still name a contact: 8 of the 500 jobs in an October 2026 run contained an email address. You are responsible for how you use them.

#### Can I search the French-speaking part of Switzerland?

Yes. Use French keywords such as `comptable`, which matched 1,093 jobs on 11 October 2026, or towns such as `Lausanne` or `Genève`, and set **Link language** to French to get links in French. The job texts stay in the language the employer wrote them in.

### Related scrapers

- [XING Jobs Scraper](https://apify.com/deriverge/xing-jobs-scraper)
- [karriere.at Jobs Scraper](https://apify.com/deriverge/karriere-at-jobs-scraper)
- [Arbeitsagentur Jobs Scraper](https://apify.com/deriverge/arbeitsagentur-jobs-scraper)
- [HelloWork Jobs Scraper](https://apify.com/deriverge/hellowork-jobs-scraper)
- [Career Site Jobs Search](https://apify.com/deriverge/career-site-jobs-search)

### Support

This actor is built and maintained by deriverge s.r.o., a software company based in the Czech Republic. If a run fails or a field you need is missing, please open an issue in the **Issues** tab or write to us at info@deriverge.com. We respond in English and Czech. Runs can be scheduled in Apify Console or started from the **API** tab, which has examples for Python, JavaScript and cURL and works with Make, Zapier, n8n and the Apify MCP server. If the actor saves you time, a short review helps other people find it.

# Changelog

This Actor's version history is a separate document: https://apify.com/deriverge/jobs-ch-scraper/changelog.md

# Actor input Schema

## `keywords` (type: `array`):

What to search for, as in the jobs.ch search box: a job title, skill or company, in German, French or English, for example "Pflege", "Buchhaltung", "comptable" or "data engineer". Each keyword is its own search. Leave empty for all jobs.

## `locations` (type: `array`):

Swiss cities, towns or postcodes, for example Zürich, Bern, Geneva, Basel, Lausanne or 8001. Each location is its own search. Leave empty for all of Switzerland.

## `radiusKm` (type: `integer`):

jobs.ch search pages have no radius filter, so this value is not applied. jobs.ch decides which nearby places a town search includes.

## `employmentTypes` (type: `array`):

Only jobs with these contract types. Leave empty for all.

## `positionLevels` (type: `array`):

Only jobs at these levels. Leave empty for all.

## `minWorkload` (type: `integer`):

Only jobs offering at least this workload, for example 80 for 80 to 100% jobs.

## `maxWorkload` (type: `integer`):

Only jobs you can do at this workload or less, for example 60 for part-time jobs.

## `sortBy` (type: `string`):

Order of the results, as on jobs.ch.

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

Language of the job and company links. Job texts stay in the language the employer wrote them in.

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

jobs.ch search addresses with your filters, for example https://www.jobs.ch/en/vacancies/?term=python\&location=Bern. Each link is its own search.

## `excludeKeywords` (type: `array`):

Drop jobs whose title or company contains any of these.

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

Reads each job page for the full description (text and HTML), street, postcode, city, canton and coordinates, company type, occupation, languages, skills, benefits, expiry date and apply link. The price is the same.

## `newOnly` (type: `boolean`):

Keeps a snapshot per watch name (or per saved task) and returns only jobs that were not there before. Schedule it daily for a job alert.

## `watchName` (type: `string`):

Name of the snapshot used by the new-only mode, for example "pflege-zurich". Runs from a saved task get one automatically.

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

Hard cap on returned jobs. jobs.ch shows up to 2,000 jobs per search. The actor reads one page a second, so 1,000 jobs with full details take about 18 minutes (without details about a minute).

## Actor input object example

```json
{
  "keywords": [
    "python"
  ],
  "sortBy": "relevance",
  "language": "en",
  "includeDetails": true,
  "newOnly": false,
  "maxItems": 20
}
```

# Actor output Schema

## `jobs` (type: `string`):

One row per job with company, address, canton, workload, contract, languages, benefits and full description.

## `summary` (type: `string`):

Jobs matching and returned per search, details read, dropped rows and warnings.

# 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 = {
    "keywords": [
        "python"
    ],
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("deriverge/jobs-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 = {
    "keywords": ["python"],
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("deriverge/jobs-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 '{
  "keywords": [
    "python"
  ],
  "maxItems": 20
}' |
apify call deriverge/jobs-ch-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,deriverge/jobs-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/Npk4uGBwxHXpxhYe2/builds/jnl2tycM0RT2XZ8mZ/openapi.json
