How to scrape jobs.ch: export Swiss job listings to CSV
You want job listings from jobs.ch, Switzerland's largest job board, in Excel or in your own analysis? The Jobs.ch Scraper collects the ads for your keywords and location and returns them as a table: title, employer, city, canton, workload, date and a link to the ad. No code, a few minutes. This guide is for recruiters and relocation agencies hiring into Switzerland, expats searching for a job, and analysts tracking the Swiss labour market. A German version of this tutorial is available.
In this tutorial
What you get
jobs.ch has no export function for job listings. The jobs.ch scraper collects the ads for your keywords and stores them as a table that you can download as CSV, Excel or JSON. It finds jobs across all of Switzerland, including ads in French and Italian. Each row is one ad, shortened here and filled with made-up placeholder values to show the structure:
{
"id": "...",
"title": "...",
"company_name": "...",
"location": "...",
"canton": "ZH",
"employment_type": "...",
"workload_min": 60,
"workload_max": 100,
"posted_at": "...",
"source_url": "https://www.jobs.ch/...",
"source_platform": "jobs.ch",
"search_query": "..."
}The values depend on each ad. A real run with numbers follows in the example below. Two local terms you will see in the data: Pensum is the workload as a percentage (100 is full-time, 60 means three days a week), and a Kanton is one of the 26 Swiss cantons, shown as a two-letter code such as ZH for Zürich.
All fields at a glance
This is how the fields are laid out according to the actor's output schema. Without includeJobDetails, the fields that only exist on the detail page (for example the full ad text and the requirements) are missing.
| Field | Type | Content |
|---|---|---|
id | Text | Unique listing ID |
title | Text | Job title |
company_name | Text | Employer (the older field company is deprecated) |
location / canton | Text | City and canton as a two-letter code (ZH, BE, AG ...) |
employment_type | Text | Type of employment, for example permanent, temporary, freelance, full-time or part-time |
workload_min / workload_max | Number | Workload (Pensum) in percent |
remote_option | Text | remote, hybrid or onsite, if given |
salary_min / salary_max / salary_currency / salary_period | Number / Text | Salary, if the ad states one |
description_snippet / description_full | Text | Short description and full ad text |
requirements | List | Requirements listed in the ad |
posted_at / expires_at | Text | Publication and expiry date |
contact_firstname, contact_lastname, contact_email, contact_phone, contact_position | Text | Contact person, if the ad names one |
apply_url / apply_email | Text | Application link and application email, if given |
company_profile_url / company_jobs_url / company_job_count | Text / Number | Company profile on jobs.ch, link to all of the employer's jobs and their count |
company_website, company_industry, company_size, company_rating | Text / Number | Company data from the profile, filled when includeCompanyDetails is on, per the schema |
company_benefits | List | Employer benefits, if listed on jobs.ch |
source_url / source_platform | Text | Link to the original ad and platform (jobs.ch) |
search_query / scraped_at | Text | The search term that found the ad and the time of retrieval |
Example: nursing jobs in Zürich
We ran this search once locally (2026-10-21): keyword “Pflegefachperson” (registered nurse), location Zürich, ads from the last 30 days, classic search, recruitment agencies hidden. The result:
- 426 jobs in the table.
- 382 of them with a nursing title. This is a filter we applied to the table after the run: titles containing Pflege, FaGe, Fachfrau/Fachmann Gesundheit, HF, Spitex, Pflegehelfer or Pflegeassistenz.
- The jobs come from 102 employers.
- The largest employer is Universitätsspital Zürich (the university hospital) with 64 jobs.
- 332 jobs are also open part-time (
workload_minbelow 100).
This was the input. The search term is German because that is how the ads are written; the actor also accepts French and Italian terms.
{
"searchQueries": ["Pflegefachperson"],
"locations": ["Zürich"],
"language": "de",
"maxResultsPerQuery": 2000,
"sortBy": "newest",
"publicationDate": "30",
"searchMode": "classic",
"companyFilter": "exclude-hr",
"includeJobDetails": true
}locations: city or region, for example Zürich, Bern or Basel.publicationDate: age of the ads in steps of 1, 3, 7 or 30 days. Here “30” means the last 30 days.searchMode:classicsearches for the exact keyword.semantic(default) searches with AI support and recognises synonyms.companyFilter:exclude-hrhides recruitment agencies and staffing firms (for example Adecco, Randstad, Manpower).only-hrshows only them,allshows everything. The actor recognises agencies by words in the company name such as Personal, Recruiting, Zeitarbeit or Stellen.maxResultsPerQuery: upper limit per keyword. 2000 is far above the 426 results, so the limit did not apply.sortBy:newestputs the newest ads first.
exclude-hr only checks the company name. Check the employer list briefly before you pass it on.The cost of this run is in the Cost section.
You can do the grouping by employer and part-time in Excel, or let Claude do it through the Apify MCP server (set-up in the next section). A prompt for that:
Use the Apify MCP server. Run the actor santamaria-automations/jobs-ch-scraper with this input:
{"searchQueries": ["Pflegefachperson"], "locations": ["Zürich"],
"language": "de", "maxResultsPerQuery": 2000, "sortBy": "newest",
"publicationDate": "30", "searchMode": "classic",
"companyFilter": "exclude-hr", "includeJobDetails": true}
Before you start the run, tell me what it will cost and wait for my OK.
When it's done, group the jobs by company_name and count them.
Also count, per employer, how many jobs allow part-time work
(workload_min below 100). Show the 10 largest employers as a table.Create an Apify account
- Open the jobs.ch scraper page on Apify.
- Click Try for free and sign up with Google or email.
- You land in the Apify Console on the actor's Input tab. For small runs, the monthly free credit of the Free plan is enough.
Enter keywords and location
In searchQueries you enter one or more search terms. Each term runs as its own search, and duplicate ads are removed across all terms. You set the city in locations and cantons in cantons. A small test run looks like this:
{
"searchQueries": ["Pflegefachperson"],
"locations": ["Zürich"],
"maxResultsPerQuery": 20,
"sortBy": "newest",
"includeJobDetails": true
}maxResultsPerQuery: upper limit per keyword (default 10, 0 means unlimited).maxResults: upper limit across all keywords together (0 means unlimited).sortBy:newestsorts by date, newest first.relevance(default) uses the order of jobs.ch.includeJobDetails:true(default) also fetches the detail page of each ad.falseis faster and cheaper but returns fewer fields.
maxResultsPerQuery to 10 to 30. You see within a short time whether the results fit your keyword, and you pay only a few cents.Run the actor and look at the result
Click Save and Run at the bottom. On the run page you see progress in the Log tab and the results as a table in the Dataset tab. Several keywords in one run look like this:
{
"searchQueries": ["Pflegefachperson", "Pflegefachfrau", "Pflegefachmann"],
"locations": ["Zürich"],
"maxResultsPerQuery": 50,
"includeJobDetails": false
}Export the data
- Open the Dataset tab in the run.
- Click Export.
- Choose the format: CSV, Excel, JSON and more are available. With the field selection you export only the columns you need, for example
title,company_name,location,workload_minandsource_url.
Available filters
The actor filters during the search by these values:
- Keyword (
searchQueries), city (locations) and canton (cantons). - Type of employment (
employmentType): permanent, temporary or freelance. - Workload (
workloadMinandworkloadMax), for example 40 to 60 percent. - Age of the ad (
postedWithinDays): only ads from the last N days. - Employer type (
companyFilter): all, without recruitment agencies, or only recruitment agencies. - Title (
titleFilter): comma-separated terms, for example “Pflege, Fachfrau”. An ad is kept if its title contains one of the terms. Ads that are dropped are removed before the detail page is fetched, so you do not pay for their details. - Sorting (
sortBy) and language (language: de, fr or en) of the search interface. - Your own search URL (
startUrls): set up the search on jobs.ch and paste the URL. - Single ads (
directUrls) and specific companies (companyNames): for example to check whether a job is still online, or to fetch all jobs of one employer.
Use it from Claude (MCP)
The scraper is available as an MCP (Model Context Protocol) tool. Paste this server URL into an MCP-compatible client such as Claude Desktop, Claude.ai, Cursor or VS Code:
https://mcp.apify.com?tools=santamaria-automations/jobs-ch-scraperOnce connected, you can paste the prompt from the example above. It asks Claude to tell you the cost first and to wait for your OK before it starts a run. Swap the keyword and city for your own, for example “Projektleiter Bau” in Basel.
API and schedules
You can also call the actor from your own scripts, n8n or Make. This call starts a run and returns the results directly (Apify endpoint run-sync-get-dataset-items):
curl -X POST "https://api.apify.com/v2/acts/santamaria-automations~jobs-ch-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"searchQueries": ["Pflegefachperson"], "locations": ["Zürich"], "publicationDate": "30", "searchMode": "classic", "companyFilter": "exclude-hr", "maxResultsPerQuery": 100}'You find your API token in the Apify Console under Settings → API & Integrations. For retrievals that should run regularly, create a schedule in the Console under Schedules. Set sortBy to newest and compare the results with the previous retrieval (field id) so you only process new ads.
What it costs
The actor charges per result (prices live from the Apify Store, as of 2026-10-21, Free plan; higher Apify plans pay slightly less):
- Start: $0.001 per run.
- Listing from the results page (
includeJobDetailsoff): $0.00287 per result. - Listing with detail page (
includeJobDetailson, default): $0.005 per result.
Each ad is charged once, either as a results-page listing or with details. Worked example for the search above with 426 jobs: with details $0.001 + 426 x $0.005 = $2.131, so about $2.13. Without details $0.001 + 426 x $0.00287 = $1.224, so about $1.22. Assumptions: Free plan prices, each of the 426 jobs is charged once, one start event per run (at 128 MB memory). You always find the current prices on the actor's Apify page.
What people use the data for
Recruiting and relocation
See which employers in a region are hiring for a profession, how many jobs are open and which workloads they offer. With company_job_count and company_jobs_url you get quickly to all jobs of one employer. Ads show the canton, which helps when you match candidates to a place to live.
Staffing agencies
With only-hr you see which agencies advertise which jobs. With exclude-hr you get the direct ads of employers who are hiring themselves and may be potential clients.
Salary and market analysis
If an ad states a salary, you find it in salary_min, salary_max and salary_period. Run the search regularly and count how many jobs are added per week and canton.
Frequently asked questions
Do I need to code to use the jobs.ch scraper?
No. You fill in a form in the Apify Console, click Save and Run and download the table. You only need code if you want to call the actor from your own system through the API.
Can I reuse a jobs.ch search I already set up?
Yes. Set up your search with all filters on jobs.ch, copy the URL from the browser and paste it into the startUrls field. The actor reads the filters from the URL.
What does the exclude-hr filter do?
With companyFilter set to exclude-hr, the actor hides recruitment agencies and staffing firms. It recognises them by the company name, for example words like Personal, Recruiting, Zeitarbeit or Stellen. This is a name match, not a register, so check the table briefly: an employer with such a word in its name can be hidden too.
Why are there fewer nursing titles in the table than results?
The search also returns ads where the term appears only in the text. In the example run, 382 of 426 jobs had a nursing title (a title containing Pflege, FaGe, Fachfrau/Fachmann Gesundheit, HF, Spitex, Pflegehelfer or Pflegeassistenz, applied as a filter after the run). The titleFilter field keeps only ads whose title contains one of your terms.
How much does the jobs.ch scraper cost?
On the Free plan it is $0.001 per run, plus $0.00287 per listing from the results page or $0.005 per listing with its detail page. The example search with 426 jobs and details costs about $2.13.
Am I allowed to use the data?
That depends on your use case, especially for contact person details such as name, email or phone number. Check this for your case before you store or reuse the data.
Related resources
- Jobs.ch Scraper, actor page: input fields, output fields and prices at a glance.
- Jobs.ch Scraper on Apify: the actor with documentation and all parameters.
- Arbeit.Swiss Scraper, actor page: jobs from arbeit.swiss, the Swiss federal job platform.
- How to Scrape Every Job Online: job listings from 110+ job boards with one call.
- German version of this tutorial