# Jobs.ch Scraper — Swiss Jobs, Workload & Apply URL (`crawloop/jobs-ch-scraper`) Actor

Jobs.ch scraper for Swiss job listings. Search by keyword and city, then export title, company, place, workload percent, CHF salary when stated, description, and apply URL as JSON.

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

## Pricing

from $2.49 / 1,000 job listings

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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 — Swiss Jobs, Workload & Apply URL

> **Disclaimer:** Unofficial tool — not affiliated with, sponsored by, or endorsed by JobCloud AG or its affiliates. Data is read from publicly accessible job-search and job-detail pages only. No login. You are responsible for complying with applicable law (including GDPR where personal data appears) and the site’s terms. No warranty on accuracy or availability. Provided for informational and research use.

**Jobs.ch scraper** for public Swiss job listings. A practical **jobs.ch API alternative**: search by **keyword**, **city**, and **workload percent**, or paste a **search / job URL**. Export **title**, **company**, **place**, **workload %**, a **CHF salary** when the text states one, the **description**, and the **apply URL** as **JSON**, **CSV**, or **Excel**. Fast **HTTP**, **no headless browser**, **no login**. Run it with **Python**, **Node.js**, **cURL**, or **Apify MCP**.

Use it for Swiss recruiting coverage, workload filters (80–100%), and a daily watch of new job ids.

| Actor | Focus |
| :--- | :--- |
| Jobs.ch Scraper ◄── you are here | Swiss jobs.ch listings, workload, apply URL |
| [Stepstone Jobs Scraper](https://apify.com/crawloop/stepstone-jobs-scraper) | DACH commercial job board |
| [Arbeitsagentur Jobs Scraper](https://apify.com/crawloop/arbeitsagentur-jobs-scraper) | Germany public employment board |
| [LinkedIn Jobs Scraper](https://apify.com/crawloop/linkedin-jobs-scraper) | Public LinkedIn job listings |
| [France Travail Scraper](https://apify.com/crawloop/france-travail-scraper) | Public France Travail offers |
| [InfoJobs Job Scraper](https://apify.com/crawloop/infojobs-jobs-scraper) | Spain job listings |

### When to use this Actor

- You need a **jobs.ch scraper** that returns structured Swiss listings
- You want **keyword + city + workload %** without an employer account
- You need the **description** and **apply URL** on the same row as the company and place
- You run a **daily monitor** and only want job ids that were not seen last time

### When not to use this Actor

- **Candidate profiles or CVs** — public job listings only
- **Logged-in recruiter tools** — no login
- **Other Swiss boards in the same row** — this Actor reads jobs.ch
- **Germany-only listings** — use [Arbeitsagentur Jobs Scraper](https://apify.com/crawloop/arbeitsagentur-jobs-scraper) or [Stepstone Jobs Scraper](https://apify.com/crawloop/stepstone-jobs-scraper)

### Key features

- Public search JSON plus the public job page (description, apply URL, CHF amount when stated)
- Workload percent from the listing (`80-100%`)
- Place name search. A name that is both a city and a canton matches both on the public index
- English, German, or French job-page text
- Monitor mode stores seen job ids and bills only new rows
- 256 MB HTTP run, proxy off by default

### Input

| Field | What it does |
| :--- | :--- |
| `keyword` | Job title, skill, or words to search |
| `location` | City or region (`Zurich`, `Bern`, `Genève`) |
| `workloadMin` / `workloadMax` | Keep jobs whose percent range overlaps this band |
| `postedWithinDays` | Keep jobs published in this window. Sorted by date |
| `companyName` | Optional case-insensitive company match |
| `language` | `en`, `de`, or `fr` for the job page text |
| `sortBy` | `relevance` or `date`. Monitor mode uses date |
| `startUrls` | Search or job URLs. They replace keyword and location |
| `includeDetails` | Fetch description, apply URL, and salary text. Default on |
| `maxItems` / `maxPages` | Caps. The search returns 20 jobs per page |
| `monitorMode` | Push only new job ids on later runs |

```json
{
  "keyword": "developer",
  "location": "Zurich",
  "workloadMin": 80,
  "workloadMax": 100,
  "language": "en",
  "maxItems": 50,
  "includeDetails": true,
  "proxyConfiguration": { "useApifyProxy": false }
}
```

Job URL example inside a run input:

```json
{
  "startUrls": [
    { "url": "https://www.jobs.ch/en/vacancies/?term=developer&location=Zurich" }
  ],
  "maxItems": 20
}
```

### Output

One dataset row is one job.

| Field | Meaning |
| :--- | :--- |
| `jobId` | Public job id |
| `title`, `company`, `companyId`, `place` | Card identity |
| `workloadMin`, `workloadMax`, `workloadText` | Stated workload percent |
| `publicationDate` | When the listing was published |
| `description` | Job text from the public page |
| `salaryText`, `salaryMin`, `salaryMax`, `salaryPeriod` | CHF amount when the text states one. Period is `HOUR`, `MONTH`, or `YEAR` |
| `applyUrl` | External apply link when the page shows one |
| `url` | Public job page |

```json
{
  "jobId": "5becfe9b-5978-45d9-92e9-00c07d95e81c",
  "title": "System Engineer Cloud Technologies (80 - 100%)",
  "company": "BELIMO Automation AG",
  "place": "Hinwil",
  "workloadText": "80-100%",
  "workloadMin": 80,
  "workloadMax": 100,
  "salaryText": "CHF 90'000 – 110'000 per year",
  "salaryMin": 90000,
  "salaryMax": 110000,
  "salaryPeriod": "YEAR",
  "applyUrl": "https://jobs.example/apply/4473",
  "url": "https://www.jobs.ch/en/vacancies/detail/5becfe9b-5978-45d9-92e9-00c07d95e81c/"
}
```

### Use cases

- Build a daily list of new developer or nursing roles around Zurich at 80–100%
- Collect company, place, workload, and the apply link for a recruiting CRM
- Read CHF amounts that the job text actually states, next to the description
- Watch one keyword and skip job ids you already stored

### Integration examples

#### Node.js

```javascript
import { ApifyClient } from "apify-client";

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor("crawloop/jobs-ch-scraper").call({
  keyword: "developer",
  location: "Zurich",
  workloadMin: 80,
  maxItems: 30,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient()
run = client.actor("crawloop/jobs-ch-scraper").call(run_input={
    "keyword": "developer",
    "location": "Zurich",
    "maxItems": 30,
})
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(len(items), items[0].get("workloadText"))
```

#### cURL

```bash
curl "https://api.apify.com/v2/acts/crawloop~jobs-ch-scraper/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"keyword\":\"Pflege\",\"location\":\"Bern\",\"maxItems\":20}"
```

### MCP and AI assistants

Use this Actor from AI tools via [Apify MCP](https://docs.apify.com/platform/integrations/mcp).
Connect your Apify account, then call this Actor by its Store name `crawloop/jobs-ch-scraper`.

Example prompts:

- "Run Jobs.ch Scraper for developer in Zurich, workload 80-100, max 20, and return title, company, workload, and apply URL as JSON"
- "Scrape new Pflege jobs in Bern and summarize place and workload percent"
- "Chain Jobs.ch Scraper then Stepstone Jobs Scraper for the same keyword across Swiss and DACH boards"

### Suite next step

For other DACH commercial listings, run [Stepstone Jobs Scraper](https://apify.com/crawloop/stepstone-jobs-scraper) after this Actor.

### FAQ

**Does this use an employer API key?**
No. It reads the public search JSON and the public job page. No login.

**Why can Zurich include more than the city?**
On the public search index, a place name that is both a city and a canton matches both. Winterthur can appear in a Zurich search for that reason.

**Where does the CHF salary come from?**
Only when the job text states an amount. Many listings describe pay without a number. Those rows leave the salary fields empty. Workload percent is separate and comes from the listing itself.

**What does monitor mode bill?**
Only new job rows. A later run that finds nothing new still starts, and it does not charge a job row for ids already stored. If you change the keyword or workload, reset the monitor store.

# Actor input Schema

## `keyword` (type: `string`):

Job title or skill (for example developer, Pflege, comptable).

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

Each extra keyword is searched separately. Results are deduplicated by job id.

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

City or region name (Zurich, Bern, Genève). A name that is both a city and a canton matches both on the public search index.

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

Optional extra cities or regions. Each one is a separate search, combined with each keyword.

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

Keep jobs whose workload range overlaps this percent. Example: 80 keeps 80–100% roles. Leave empty for any workload.

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

Upper bound of the workload overlap. Leave empty for 100.

## `postedWithinDays` (type: `integer`):

Keep jobs published within this many days. The search is sorted by date so older pages stop the run.

## `companyName` (type: `string`):

Optional case-insensitive match on the company name. Applied after the search page is read.

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

Language of the public job page used for the description and title.

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

Relevance matches the public search default. Date puts the newest jobs first. Monitor mode and posted-within-days always sort by date.

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

Optional public search or job URLs. When set, they replace keyword and location. Job URLs contain /detail/{id}.

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

Open each public job page for the description, apply URL, and a CHF amount when the text states one. One dataset row is still one job.

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

Maximum job rows across all searches. 0 means no cap other than max pages.

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

Safety cap. The public search returns 20 jobs per page.

## `monitorMode` (type: `boolean`):

On a schedule, push and charge only job ids not seen in a previous run. The first run stores the ids it returns. Newest jobs first.

## `monitorStoreName` (type: `string`):

Named key-value store for seen job ids. Use one name per watch. Reset it if you change the workload or keyword.

## `resetMonitorState` (type: `boolean`):

Clear the seen-job cache before this run.

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

Leave proxy off. The public search JSON and job pages answer without a proxy. Enable one only if you see HTTP 403 or 429.

## Actor input object example

```json
{
  "keyword": "developer",
  "location": "Zurich",
  "language": "en",
  "sortBy": "relevance",
  "includeDetails": true,
  "maxItems": 50,
  "maxPages": 10,
  "monitorMode": false,
  "monitorStoreName": "jobs-ch-jobs-monitor",
  "resetMonitorState": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

Default dataset items.

# 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 = {
    "keyword": "developer",
    "location": "Zurich",
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("crawloop/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 = {
    "keyword": "developer",
    "location": "Zurich",
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("crawloop/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 '{
  "keyword": "developer",
  "location": "Zurich",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call crawloop/jobs-ch-scraper --silent --output-dataset

```

## MCP server setup

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