jobs.ch Scraper π¨π Swiss Jobs (Search + Detail)
Pricing
from $2.00 / 1,000 results
jobs.ch Scraper π¨π Swiss Jobs (Search + Detail)
Scrape jobs.ch β Switzerland's #1 job board. Search + full job detail: title, company, location, workload % (80-100%), contract type, salary (CHF), full description, posted date. Fast JSON-API scraping on a Swiss residential proxy. Pay-per-result.
jobs.ch Scraper π¨π β Scrape Swiss Jobs (no login, no API key)
Scrape job postings from jobs.ch, Switzerland's #1 job board, into clean structured data β title, company, company logo, location, canton, workload percentage (the Swiss 80-100% convention), contract type, salary in CHF when published, full job description, remote-work flag, and ISO posted date. A practical jobs.ch API alternative: no login and no API key required; export to JSON, CSV, Excel (XLSX), XML, or an HTML table, or pull results straight from the Apify API. Your $5 free Apify credit β 3,300 full-detail jobs β enough to test seriously before spending a cent.
π What does the jobs.ch Scraper do?
You give it a search (a keyword like software engineer plus a location like Zurich, or any jobs.ch search URL) and it returns every matching job posting as a structured record. Built-in filters narrow the search server-side: publication date (last 24h / 3 / 7 / 30 days), employment types (permanent, temporary, freelance, internship, supplementary income, apprenticeship), and workload range (e.g. only 80-100% roles). It runs in two layers:
- Search cards β fast list of jobs from the results pages (title, company, location, workload %, contract type, snippet, posted date).
- Full detail (default) β fetches each job's detail record for the complete description, published salary (CHF), and the direct application URL.
Under the hood it talks to jobs.ch's own public JSON API over plain HTTP through a Swiss residential proxy β no browser rendering, so runs are fast and cheap. It paginates page by page and stops cleanly at your item budget or the API's 2,000-results-per-search ceiling.
π€ Who is it for?
- Recruiters & staffing agencies β track open roles, hiring companies, and workloads across Swiss cities; build outreach lists by role, canton, and pensum.
- Labour-market & HR analysts β measure demand by skill, contract type (permanent vs. temporary), and region; benchmark published CHF salaries.
- Job boards & aggregators β supplement your own inventory with Swiss coverage from the market leader.
- Founders & sales teams β find companies that are actively hiring (a strong buying/growth signal) in a target city or sector.
β Why use this scraper?
- Switzerland-focused β jobs.ch coverage with Swiss conventions built in: workload as
80-100%, salaries in CHF, locations across all cantons and language regions (searches work in English, German, French, or Italian). - API-grade data quality β records come from jobs.ch's own JSON API, not brittle HTML parsing: stable IDs, exact timestamps with timezone, structured salary ranges. Since jobs.ch offers no public developer API, this actor is the pragmatic jobs.ch API alternative for programmatic access.
- Server-side filters β publication date (24h/3d/7d/30d), employment types, and workload (pensum) range are applied in the jobs.ch API query itself, so you only pay for jobs that match.
- No login, no API key β just a search or a URL.
- Full job descriptions β the complete job-ad text as clean plain text, not just the snippet.
- Budget-safe β
maxItemscap, dedup (you never pay twice for the same job), anti-runaway pagination caps. - All export formats β JSON, CSV, Excel, XML, HTML table, plus API and webhooks.
π What data can you extract from jobs.ch?
| Data point | Example |
|---|---|
title | Software Engineer |
company | Ammann Schweiz AG |
companyUrl | https://www.jobs.ch/en/companies/3812-ammann-schweiz-ag/ |
companyLogo | https://media.jobs.ch/images/44c3746c-β¦/320x320.png |
location | Langenthal |
canton (detail mode) | BE |
isRemote | true when the title/description mentions remote / home office / Homeoffice |
workloadPercent | 80-100% |
contractType | Permanent position |
salaryRaw | CHF 5200+ per month |
salaryMin / salaryMax | 5200 / 6500 |
salaryCurrency | CHF |
salaryPeriod | month |
descriptionSnippet | Ammann ist ein weltweit führender Lieferant von Mischanlagen⦠|
description (detail mode) | Full job-ad text as clean plain text |
applicationUrl (detail mode) | https://ammann-schweiz-ag.contactrh.com/jobs/4473/β¦ |
postedDate | 2026-06-20T08:32:21.000Z |
publishedRaw | 2026-06-20T10:32:21+02:00 |
jobKey | 0e233528-63b7-4b59-a477-0e02f3035ba3 |
url | https://www.jobs.ch/en/vacancies/detail/0e233528-β¦/ |
Note:
salaryRaw/salaryMin/salaryMaxappear only when the employer publishes pay β most Swiss postings don't (salary transparency is not mandatory in Switzerland).descriptionandapplicationUrlare only populated when Scrape full detail pages is on.
π° How much does it cost to scrape jobs.ch?
This actor uses Apify's pay-per-event pricing:
| Event | When it fires | Price (USD) | Per 1,000 |
|---|---|---|---|
| Actor start | Once per run | $0.02 | β |
| Job (search card) | Per unique job, when Scrape full detail pages = off | $0.0008 | $0.80 |
| Job (full detail) | Per unique job, when Scrape full detail pages = on (full description + salary + application URL) | $0.0015 | $1.50 |
Your first results are free: the $5 free credit on Apify's free plan covers roughly 3,300 full-detail jobs (or ~6,200 search cards) β no credit card needed to start. Duplicates are never charged. Real-world cost examples:
| Run | Items | Mode | Approx. cost |
|---|---|---|---|
| Quick scan | 100 jobs | search cards only | $0.02 + 100 Γ $0.0008 = $0.10 |
| Standard | 100 jobs | full detail | $0.02 + 100 Γ $0.0015 = $0.17 |
| Deep pull | 500 jobs | full detail | $0.02 + 500 Γ $0.0015 = $0.77 |
| Max search | 2,000 jobs | full detail | $0.02 + 2,000 Γ $0.0015 = $3.02 |
With the $5/month free Apify plan you can scrape roughly 3,300 full-detail jobs or 6,200 search cards per month at no cost. (Proxy traffic is included in your Apify plan's usage; the API-based approach keeps it minimal.)
π How to scrape jobs.ch
- Open the actor on Apify and click Try for free.
- Enter a Search query (e.g.
software engineer) and a Location (e.g.Zurich) β or paste one or more jobs.ch search URLs into Search URLs. - (Optional) Narrow with Posted within, Employment types, and Min/Max workload; set Max items and toggle Scrape full detail pages.
- Leave the proxy on RESIDENTIAL / Switzerland.
- Click Start, then Export results as JSON, CSV, Excel, XML, or HTML.
π§© Input examples
Minimal β keyword + city:
{"searchQuery": "software engineer","location": "Zurich","maxItems": 100,"scrapeDetails": true,"proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "CH" }}
Filtered β fresh permanent 80-100% roles only:
{"searchQuery": "data engineer","location": "Zurich","publicationDateFilter": "7d","employmentTypes": ["5"],"workloadMin": 80,"workloadMax": 100,"maxItems": 200,"scrapeDetails": true,"proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "CH" }}
Multiple filtered searches β paste jobs.ch URLs (any language; filters like employment type, region, or publication date are preserved):
{"startUrls": [{ "url": "https://www.jobs.ch/en/vacancies/?term=data%20engineer&location=Zurich&employment-types=5" },{ "url": "https://www.jobs.ch/de/stellenangebote/?term=pflegefachfrau®ion=2" }],"maxItems": 300,"scrapeDetails": true,"maxConcurrency": 5,"proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "CH" }}
βοΈ Input parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
startUrls | array | β | jobs.ch search URLs (website or API form, any language). Overrides the query/location fields. All URL filters are preserved. |
searchQuery | string | β | Job keyword (the jobs.ch term), e.g. software engineer. Empty with no startUrls = browse all jobs. |
location | string | β | City, canton, or region, e.g. Zurich, Geneva. Empty = all of Switzerland. |
publicationDateFilter | string | any | Only jobs posted within: 1d, 3d, 7d, 30d, or any. Applied server-side in the API query and re-checked on each record's postedDate. |
employmentTypes | array | β | Contract-type filter: "5" Permanent, "1" Temporary, "2" Freelance, "3" Internship, "4" Supplementary income, "6" Apprenticeship. Empty = all. |
workloadMin / workloadMax | integer | β | Workload (pensum) range 0-100, e.g. 80/100 for 80-100% roles. Maps to the jobs.ch employment-grade filter. |
maxItems | integer | 100 | Stop after this many jobs (0 = unlimited, capped at 2,000 per search by the jobs.ch API). |
scrapeDetails | boolean | true | Fetch each job's detail record for full description, salary, and application URL. |
maxConcurrency | integer | 5 | Parallel requests (capped at 10 internally to stay polite). |
proxyConfiguration | object | RESIDENTIAL / CH | Required. Apify residential proxy, country Switzerland. |
π€ Output example
A real record from query=nurse (full-detail mode):
{"url": "https://www.jobs.ch/en/vacancies/detail/d9f74cbb-ff2b-4f58-be48-a6c2eb704651/","jobKey": "d9f74cbb-ff2b-4f58-be48-a6c2eb704651","title": "Pflegeexpertin / Pflegeexperte / Nurse Practitioner (w/m/d) Tumorpfad 80-100%","company": "UniversitΓ€tsspital ZΓΌrich","companyUrl": "https://www.jobs.ch/en/companies/11393-universitaetsspital-zuerich/","companyLogo": "https://media.jobs.ch/images/2c1f45b0-9c94-4bbf-a2a1-c19c04c34f9d/320x320.png","location": "ZΓΌrich","canton": "ZH","isRemote": false,"workloadPercent": "80-100%","contractType": "Permanent position","salaryRaw": null,"salaryMin": null,"salaryMax": null,"salaryCurrency": null,"salaryPeriod": null,"descriptionSnippet": "Am UniversitΓ€tsspital ZΓΌrich begleitest du Menschen mit onkologischen Erkrankungen in einem hochspezialisierten medizinischen Umfeld - im ...","description": "Am UniversitΓ€tsspital ZΓΌrich begleitest du Menschen mit onkologischen Erkrankungen in einem hochspezialisierten medizinischen Umfeld - im durchgΓ€ngigen Patientenpfad, ΓΌber Fachgrenzen hinweg und mit spΓΌrbarer Wirkung fΓΌr Patientinnen und Patienten.β¦","applicationUrl": "https://ohws.prospective.ch/public/v1/jobs/01d77c02-b241-4cd1-885f-440f56a08b83/","postedDate": "2026-06-16T13:32:41.000Z","publishedRaw": "2026-06-16T15:32:41+02:00","scrapedAt": "2026-07-10T17:11:58.241Z","source": "detail"}
And a record with a published salary (detail mode):
{"url": "https://www.jobs.ch/en/vacancies/detail/bba01a3d-0710-4865-96f5-5fe0090aadcf/","jobKey": "bba01a3d-0710-4865-96f5-5fe0090aadcf","title": "Reinigungsmitarbeiter/in (80β100%) β BΓΌroreinigung","company": "Efe's Reinigung","location": "Urdorf","workloadPercent": "80-100%","contractType": "Permanent position","salaryRaw": "CHF 5200+ per month","salaryMin": 5200,"salaryMax": null,"salaryCurrency": "CHF","salaryPeriod": "month","postedDate": "2026-07-09T12:46:12.000Z","source": "detail"}
In search-card mode (scrapeDetails: false), description and applicationUrl are null and source is "search-card".
π‘ Tips for best results
- Start small. Run with
maxItems: 20first to validate your query, then scale up. - Use
startUrlsfor precise filters. Build the exact search you want on jobs.ch (employment type, region, publication date, categoryβ¦), then paste the URL β every filter is respected. - Beat the 2,000-result ceiling. The API caps one search at 2,000 results. Split broad queries into several
startUrls(per canton, per employment type) to cover more. - Schedule it. Run daily with
publicationDateFilter: "1d"to capture only postings from the last 24 hours β perfect for a fresh-jobs feed. - Search cards are half the requests. If you don't need full descriptions, turn
scrapeDetailsoff: same jobs at almost half the cost and double the speed.
β οΈ Error items
When a request permanently fails after all retries, the actor pushes an error item (no charge) so a run is never silently lossy:
{ "error": "BLOCKED_OR_FAILED", "errorDescription": "Non-JSON response from https://www.jobs.ch/api/β¦", "url": "https://www.jobs.ch/api/v1/public/search?query=β¦", "scrapedAt": "2026-07-10T17:12:00.000Z" }
| Error code | Meaning |
|---|---|
BLOCKED_OR_FAILED | The request failed after 5 retries (network error, rate limit, or an HTML error page instead of JSON). Re-run later or lower concurrency. |
RUN_ABORTED | The whole run hit an unrecoverable error (e.g. proxy pool exhaustion). Results scraped before the abort are kept. |
π Integrations
Push results anywhere Apify connects: Google Sheets, Slack, Discord, Make, Zapier, BigQuery, Snowflake, Airbyte, plain webhooks, and the scheduler. Configure these from the actor's Integrations tab.
π οΈ API usage
Run the actor and fetch the dataset via the Apify API.
Node.js (apify-client):
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });const run = await client.actor('crowley/jobs-ch-scraper').call({searchQuery: 'software engineer',location: 'Zurich',maxItems: 100,scrapeDetails: true,proxyConfiguration: { useApifyProxy: true, apifyProxyGroups: ['RESIDENTIAL'], apifyProxyCountry: 'CH' },});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items);
Python (apify-client):
from apify_client import ApifyClientclient = ApifyClient("YOUR_APIFY_TOKEN")run = client.actor("crowley/jobs-ch-scraper").call(run_input={"searchQuery": "software engineer","location": "Zurich","maxItems": 100,"scrapeDetails": True,"proxyConfiguration": {"useApifyProxy": True, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "CH"},})for item in client.dataset(run["defaultDatasetId"]).iterate_items():print(item)
cURL:
curl -X POST "https://api.apify.com/v2/acts/crowley~jobs-ch-scraper/runs?token=YOUR_APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"searchQuery":"software engineer","location":"Zurich","maxItems":100,"scrapeDetails":true,"proxyConfiguration":{"useApifyProxy":true,"apifyProxyGroups":["RESIDENTIAL"],"apifyProxyCountry":"CH"}}'
π€ Use with AI agents via MCP
This actor is callable from AI agents through the Apify MCP server. Example setup for Claude Desktop / Claude Code / Cursor:
{"mcpServers": {"apify": {"command": "npx","args": ["-y", "@apify/actors-mcp-server", "--actors", "crowley/jobs-ch-scraper"],"env": { "APIFY_TOKEN": "YOUR_APIFY_TOKEN" }}}}
Example prompts: "Find software engineering jobs in Zurich with at least an 80% workload" or "List nursing jobs in Bern posted this week, with company and application link."
β FAQ
Do I need a jobs.ch login or API key? No. Just a search or URL and an Apify residential proxy.
Where does the data come from? From jobs.ch's own public JSON API β the same one the jobs.ch website itself uses. That means stable job IDs, exact timestamps, and structured fields instead of fragile HTML scraping.
What does workloadPercent mean? Swiss job ads state the pensum β the employment percentage. 100% is full-time, 80-100% means the employer accepts anything from 4 days a week to full-time. The scraper collapses the API's grade list (e.g. [80, 85, 90, 95, 100]) into that familiar range.
Why are most salaries empty? Swiss employers rarely publish pay β there is no salary-transparency mandate in Switzerland. When a posting does include it, you get salaryRaw, salaryMin/salaryMax, salaryCurrency (CHF), and salaryPeriod.
Can I search in German or French? Yes. The search index covers all of jobs.ch, and queries work in any of the site's languages (pflegefachfrau, comptable, software engineerβ¦). You can also paste /de/stellenangebote/ or /fr/offres-emplois/ URLs as startUrls.
How many results can I get per search? The jobs.ch API caps a single search at 2,000 results. Use more specific queries/locations or several startUrls to cover more.
Can I get only new jobs? Yes β set publicationDateFilter to 1d (last 24 hours), 3d, 7d, or 30d and schedule the actor. Website URLs with publication-date=N pasted as startUrls are translated to the equivalent API filter too.
Can I find remote jobs? jobs.ch has no structured remote field, so the actor sets an isRemote heuristic flag when the title or description mentions remote, home office, or Homeoffice. Filter your export on isRemote = true.
Can I use this for salary benchmarking? Yes, with the caveat that only a minority of Swiss postings publish pay. Aggregate salaryMin/salaryMax/salaryPeriod (already normalized to CHF) across a role and region for directional benchmarks.
Is it legal? See the Legality section below.
βοΈ Legality
This scraper collects publicly available job-posting data only. It does not log in, bypass paywalls, or collect personal data of job applicants. You are responsible for using it in compliance with jobs.ch's Terms of Service and robots.txt, the EU GDPR, and the Swiss Federal Act on Data Protection (FADP/nDSG). Scrape responsibly: keep concurrency modest, do not overload the site, and use the data lawfully (e.g. recruiting and market research, not spam). Job postings may contain a contact name β treat any personal data per the FADP and GDPR (lawful basis, minimisation, retention limits). When in doubt, consult a lawyer. Background reading: Is web scraping legal?
π¬ Your feedback
Found a bug or want a new field? Open an issue on the actor's Issues tab β we actively maintain it.
π Related scrapers
Other Switzerland-focused scrapers from the same author:
- anibis.ch Scraper π¨π β Swiss classifieds marketplace (search + detail).
- tutti.ch Scraper π¨π β Swiss classifieds (search + detail).
- homegate.ch Scraper π¨π β Swiss real estate listings.
- immoscout24.ch Scraper π¨π β Swiss real estate (search + detail).
- autoscout24.ch Scraper π¨π β Swiss car listings.
π Changelog
1.1 β 2026-07-10
- New
publicationDateFilterinput (1d/3d/7d/30d) β server-side publication-date window (mapped to the API'spublication-date-from/-torange), plus a client-sidepostedDatedouble-check so re-published old ads never slip through. - New
employmentTypesinput β filter by contract type (Permanent, Temporary, Freelance, Internship, Supplementary income, Apprenticeship) via the API'semployment-type-ids[]filter. - New
workloadMin/workloadMaxinputs β pensum range 0-100% via the API's employment-grade filter. - New output fields:
companyLogo(structured logo image URL),canton(e.g.ZH,BEβ from the detail location block),isRemote(heuristic keyword flag: remote / home office / Homeoffice). - Website
startUrlswithpublication-date=Noremployment-types=Nare now translated to the equivalent API filters (the raw website params are silently ignored by the API).
1.0 β 2026-07-10
- Initial release: jobs.ch search + full job-detail scraping via the public JSON API (no browser β fast, cheap HTTP).
- Swiss workload parsing (
80-100%), contract-type mapping, CHF salary ranges (min/max/period), ISO posted dates, clean plain-text descriptions, direct application URLs. startUrlssupport for any jobs.ch search URL (en/de/fr/it) with all filters preserved.- Pay-per-event pricing with dedup and error items;
maxItemsbudget cap and anti-runaway pagination caps (2,000-result API ceiling, matchless-streak guard).