# Swiss Jobs Scraper — jobs.ch & jobup.ch, Salary & Workload % (`yugenox/swiss-jobs-scraper`) Actor

Scrape Swiss job ads from jobs.ch and jobup.ch in one run: title, company, place with address and canton, workload %, contract type, position, benefits, dates and links. Optional full details: description, salary estimate, category, industry, work hours, apply link, employer profile. No login.

- **URL**: https://apify.com/yugenox/swiss-jobs-scraper.md
- **Developed by:** [Yugenox Corp](https://apify.com/yugenox) (community)
- **Categories:** Jobs, Lead generation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 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.

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

## Swiss Jobs Scraper: jobs.ch and jobup.ch with salary and workload %

Scrape **jobs.ch** and **jobup.ch**, Switzerland's biggest job boards, in one run. For every job you get the **title, company, place with postcode, street and canton, workload % (Pensum), contract type, position level, required languages, benefits, publication dates and the link**. Turn on **Include full details** to add the **full job description** (with the site's translation into English, German or French), the site's **salary estimate in CHF per year**, category, industry, work hours, expiry date, how to apply and the **application link**, plus the **employer profile** (website, size, rating). Search by keyword and place, filter by workload, contract type, position level and publication date, paste search or job URLs, and run it daily with **Only new jobs**. A job listed on both sites is saved once. No login, no cookies, no account.

### What you get

| | |
|---|---|
| **Job** | Title, job URL and ID, the site's standard job title, category, skills, published and first-published date, expiry date, start date, sponsored flag |
| **Workload and contract** | Workload from–to in % (e.g. 80–100%), contract type (permanent, temporary, freelance, internship, side job, apprenticeship), position level (executive, specialist, employee), work hours per week |
| **Place** | City, postcode, street, canton, region, country, coordinates, and every workplace when an ad has several |
| **Salary** | jobs.ch / jobup.ch's salary estimate for the job: min and max in CHF per year, whether it's based on the canton or all of Switzerland, and how many reported salaries it's built on |
| **Requirements** | Required languages and levels (e.g. German, native; French, fluent) |
| **Benefits** | The benefits the employer lists: working from home, flexible hours, public-transport ticket, parking … |
| **The ad** | Full description as text and HTML in the ad's language, plus the site's translation into your chosen language when the ad is in another one |
| **Apply** | How to apply (the site's form, the employer's site, e-mail or post) and the application link |
| **Employer** | Name, profile page, logo, website, switchboard phone, size, founding year, city, industry, rating and number of reviews |

The listing fields come with every job. The salary estimate, description, category, industry, work hours, application link and employer profile come from each job's own page, so they need **Include full details** (or a pasted job URL).

### How to use it

1. Enter one or more **keywords**, e.g. `data analyst`, `Pflegefachfrau`, `comptable`, `software engineer`.
2. Optionally enter a **place** — a city, postcode, canton or region such as `Zürich`, `8001`, `Genève`, `Bern`, `Ticino`.
3. Pick the **sites** (both by default), set **Max jobs**, and run.
4. Optional: **Include full details**, filters (**Workload**, **Contract type**, **Position**, **Published**), **Only new jobs** for scheduled runs.

#### Input examples

The 10 newest data analyst jobs in Zürich on both sites:

```json
{
  "keywords": ["data analyst"],
  "location": "Zürich",
  "maxItems": 10
}
```

Part-time permanent nursing jobs (up to 60%) in Bern and Basel, with full details and the salary estimate:

```json
{
  "keywords": ["Pflegefachfrau", "Pflegefachmann"],
  "locations": ["Bern", "Basel"],
  "workloadMax": 60,
  "employmentTypes": ["PERMANENT"],
  "includeDetails": true,
  "maxItems": 200
}
```

A daily alert: new accounting jobs in French-speaking Switzerland, published in the last 3 days, only the ones not saved before (schedule it daily):

```json
{
  "keywords": ["comptable"],
  "sites": ["jobup.ch"],
  "postedWithinDays": "3",
  "onlyNew": true,
  "maxItems": 0
}
```

Everything published on jobs.ch in the last 24 hours, with German labels:

```json
{
  "sites": ["jobs.ch"],
  "postedWithinDays": "1",
  "language": "de",
  "maxItems": 0
}
```

You can also paste URLs into **jobs.ch / jobup.ch URLs**: job ads (`jobs.ch/en/vacancies/detail/…/`, `jobup.ch/fr/emplois/detail/…/`) or a search-results URL copied from your browser — the filters in it are kept.

### Output example

A real row (description shortened):

```json
{
  "site": "jobs.ch",
  "jobId": "13159d63-65af-44df-8adb-8c2788f2b813",
  "url": "https://www.jobs.ch/en/vacancies/detail/13159d63-65af-44df-8adb-8c2788f2b813/",
  "title": "GEVER-Berater:in / Wirtschaftsinformatiker:in",
  "company": "CM Informatik AG",
  "place": "Schwerzenbach",
  "postalCode": "8603",
  "street": "Ringstrasse 7",
  "canton": "ZH",
  "region": "Region of Zurich / Schaffhausen",
  "workloadMin": 80,
  "workloadMax": 100,
  "workload": "80–100%",
  "employmentTypes": ["Permanent position"],
  "positions": ["Employee"],
  "languageSkills": [{ "language": "German", "languageCode": "de", "level": "Native language", "levelCode": "4" }],
  "benefits": ["No dress code", "Working from home", "Paid maternity leave", "Training", "Overtime compensation", "Flexible working hours"],
  "publishedAt": "2026-10-20T16:38:40+02:00",
  "detailsIncluded": true,
  "normalizedTitle": "Consultant",
  "categories": ["Admin. / HR / Consulting / CEO > Consultancy / Company Development"],
  "expiresAt": "2026-12-05",
  "workHours": "33.6 - 42 hours/week",
  "applicationMethod": "external",
  "applyUrl": "https://cmi.jobportal.abaservices.ch/application-process/35ddfa55-c2bf-48d5-b8dd-512c140b6841/890a8bea-0dc2-87fa-9f3d-5a850220f2ea?jp=ABACUS",
  "salaryMin": 86222,
  "salaryMax": 126222,
  "salaryCurrency": "CHF",
  "salaryPeriod": "year",
  "salaryType": "estimate",
  "salaryRegion": "canton",
  "salarySampleSize": 942,
  "description": "Willst du etwas bewegen? Bist du eine treibende Kraft, die strukturiert ist und gerne im Team arbeitet? …",
  "descriptionLanguage": "de",
  "translatedTitle": "GEVER Consultant / Business Informatics Specialist",
  "translatedDescription": "Do you want to make a difference? Are you a driving force who is organised and enjoys working in a team? …",
  "skills": ["Anforderungsmanagement", "Business Analyse", "Change Management", "IT Projekte", "Projektmanagement"],
  "companyWebsite": "https://www.cmi.ch",
  "companySize": "101 - 250",
  "companyRating": 5,
  "companyReviewCount": 1,
  "companyCity": "Schwerzenbach",
  "searchQuery": "\"data analyst\" in Zürich on jobs.ch",
  "rank": 1,
  "scrapedAt": "2026-10-29T18:35:46.677Z"
}
```

Every row has the same columns; a value the ad doesn't state is `null`.

### Pricing

Pay per result, no monthly fee:

- **$1.40 per 1,000 jobs** saved (listing fields).
- **+ $1.40 per 1,000 jobs** whose full details were read (only with **Include full details** or pasted job URLs) — **$2.80 per 1,000** with everything.
- **$0.10 per 1,000 search pages** that gave no new job (a search with no results, or pages of jobs you already have) — $0.0001 each.
- **$0.002 per run** (start).

Examples: 10 jobs ≈ $0.016; 1,000 jobs ≈ $1.40; 1,000 jobs with full details ≈ $2.80. Jobs dropped as duplicates, jobs your filters leave out and jobs skipped by **Only new jobs** are never charged. Set a maximum charge per run in the run options and the scraper stops before it.

### Use cases

- **Recruiters and staffing agencies**: daily feeds of new Swiss jobs by role and region, with the employer's apply link.
- **Salary benchmarking**: the sites' salary estimates by job title, canton, workload and contract type.
- **Labour-market research**: hiring volume by canton, industry, category, workload and language requirements.
- **Job alerts and job boards**: a scheduled run with **Only new jobs** feeding your own site, newsletter or Slack.
- **Sales prospecting**: which companies are hiring for what, with their website and size.

### Tips

- **Workload** is the Swiss "Pensum": a job listed as 80–100% matches "Workload from 80" and "Workload up to 80".
- Each search lists at most **2,000 jobs** (100 pages). For more, split by place, workload, contract type or publication date; the status message tells you when a search hit the limit.
- jobs.ch and jobup.ch share many ads. Keep both sites on for the widest coverage — the duplicates are saved once and never charged twice.
- **Language** changes the labels (contract types, categories, benefits, regions) and the job links. The description stays in the employer's language; with full details you also get the site's translation into the language you picked whenever the ad is written in another one.
- With **Only new jobs** and the default newest-first order, a search stops as soon as it reaches jobs saved last time, so a daily run reads about one page per search.

### FAQ

**Do I need a jobs.ch or jobup.ch account?** No. The scraper reads the same public pages any visitor sees. It never logs in and never applies to anything.

**Where does the salary come from?** Most Swiss employers don't publish a salary. jobs.ch and jobup.ch show their own **salary estimate** on each job page, based on salaries reported for similar jobs in the same canton (or all of Switzerland). The scraper returns that estimate with `salaryType: "estimate"`, the basis (`salaryRegion`) and the number of salaries behind it (`salarySampleSize`). It needs **Include full details**.

**Why are some jobs marked `isSponsored`?** The sites show a few promoted ads at the top of the results. They are real jobs and are saved like any other (once), flagged so you can filter them.

**What happens if a run is cut short?** Everything saved so far stays in the dataset and the run still ends successfully with a message saying what happened. Raise the timeout or lower **Max jobs** for big runs.

**Which proxy should I use?** Keep the default (Apify Proxy, datacenter). It comes with every Apify plan, including the free one. If the sites refuse datacenter IPs for a while, the run switches to residential proxies when your account has them.

**Is it legal to scrape jobs.ch and jobup.ch?** This Actor only collects publicly available data: job ads that anyone can see on jobs.ch and jobup.ch without an account, such as the title, company, place, workload, contract type and the ad itself. Collecting publicly available data is generally legal, but you're responsible for how you use it. Job ads describe roles, not people, but an ad sometimes names a contact person; the scraper removes those names, personal e-mail addresses and phone numbers from the description (company switchboards and role mailboxes such as jobs@ stay). You must follow privacy laws such as the Swiss FADP, GDPR and CCPA, as well as the sites' terms. If you're unsure, check with a lawyer. More on this: [Is web scraping legal?](https://blog.apify.com/is-web-scraping-legal/)

**Does it access any private data?** No. Everything comes from pages jobs.ch and jobup.ch show to any visitor without logging in. It never touches accounts, saved jobs, alerts or applications, and it follows the sites' robots.txt.

*This Actor is not affiliated with, endorsed by or sponsored by jobs.ch, jobup.ch or JobCloud.*

# Actor input Schema

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

Job titles, skills or companies to search for, one per line (e.g. "data analyst", "Pflegefachfrau", "comptable"). Each keyword is searched on every site you pick. Leave empty and add a place or a filter to list all jobs there; with nothing at all, the run collects the newest jobs (published in the last 24 hours).

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

City, postcode, canton or region, as you would type it on jobs.ch (e.g. "Zürich", "8001", "Genève", "Bern", "Ticino"). Leave empty for all of Switzerland.

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

Optional: more places, one per line. Every keyword is searched in every place.

## `sites` (type: `array`):

Which job boards to search. jobs.ch is the biggest Swiss board (German-speaking Switzerland first); jobup.ch is its sister site for French-speaking Switzerland. A job listed on both is saved once.

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

Maximum number of jobs for the whole run, shared fairly between your searches. 0 = everything (each search lists at most 2,000 jobs — split big searches by place, workload or contract type).

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

Also open each job's own page: the full job description (plus the site's English or French translation), the site's salary estimate (CHF per year), category, industry, work hours, contract start, expiry date, how to apply + the application link, skills and the employer profile (website, size, rating). Charged once per job as "Job full details".

## `onlyNew` (type: `boolean`):

Skip jobs that an earlier run with the same searches already saved (they are not charged). Made for daily or hourly monitoring: with the newest-first order, a search stops as soon as it reaches the jobs saved last time.

## `workloadMin` (type: `integer`):

Only jobs whose workload (Pensum) reaches at least this percentage, e.g. 80 for 80–100% jobs. Leave empty for any.

## `workloadMax` (type: `integer`):

Only jobs that can be done at this percentage or less, e.g. 60 for part-time jobs. Leave empty for any.

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

Only these contract types. Leave empty for all.

## `positions` (type: `array`):

Only these position levels. Leave empty for all.

## `postedWithinDays` (type: `string`):

Only jobs published in this period.

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

Newest first (best for monitoring and with "Only new jobs") or the sites' own relevance order.

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

Optional: job ad URLs (jobs.ch/en/vacancies/detail/…/, jobup.ch/fr/emplois/detail/…/) or search-result URLs copied from your browser — the filters in a search URL are kept. A job URL is always read with its full details.

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

Language of contract types, categories, benefits, regions and job links. jobup.ch has no German version, so its jobs come in English when German is chosen. The job description itself is always in the employer's language; with full details, the site's translation is added when the ad is in another language.

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

Apify Proxy (datacenter) is the default and comes with every Apify plan. If the sites refuse datacenter IPs for a while, the run switches to residential proxies when your account has them.

## Actor input object example

```json
{
  "keywords": [
    "data analyst"
  ],
  "location": "Zürich",
  "sites": [
    "jobs.ch",
    "jobup.ch"
  ],
  "maxItems": 10,
  "includeDetails": false,
  "onlyNew": false,
  "postedWithinDays": "any",
  "sortBy": "date",
  "language": "en",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

All scraped jobs.

## `run` (type: `string`):

Status and statistics for this run.

## `report` (type: `string`):

RUN_REPORT: how the run ended, searches, counts, requests and proxy use.

# 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": [
        "data analyst"
    ],
    "location": "Zürich",
    "maxItems": 10,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("yugenox/swiss-jobs-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": ["data analyst"],
    "location": "Zürich",
    "maxItems": 10,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("yugenox/swiss-jobs-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": [
    "data analyst"
  ],
  "location": "Zürich",
  "maxItems": 10,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call yugenox/swiss-jobs-scraper --silent --output-dataset

```

## MCP server setup

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