Jobs.ch Swiss Job Listings by Region & Industry
Scrape job listings from jobs.ch, Switzerland's largest job board. Search by keyword or browse by canton, industry, occupational field, or contract type, with filters for workload and seniority. Returns title, company, location, and job URL.
Input schema
Input fields, types, required flags, descriptions, defaults, and prefills come from the published definition. Prefill examples are distinct from defaults.
| Field | Type | Required | Description |
|---|---|---|---|
mode | string | required | What to fetch. `search` runs classic keyword search (or semantic with `searchMode=semantic`). `browse` uses filters only. `byCompany` resolves company names/IDs. `byUrls` accepts jobs.ch search or job-detail URLs. The `byRegion`/`byIndustry`/`byEmploymentType`/`byCategory` modes are kept for backward compatibility and require their matching filter. |
searchMode | string | optional | `classic` uses jobs.ch's exact keyword API with all filters and sort. `semantic` uses the site's fuzzy, cross-language search page (filters still apply; sort is relevance-only). |
searchQueries | array | optional | One or more free-text keywords (mode=search). Multiple queries are run in order and deduplicated by job ID. Leave empty to browse all jobs matching the filters. |
Show all input fields
| Field | Type | Required | Description |
|---|---|---|---|
mode | string | required | What to fetch. `search` runs classic keyword search (or semantic with `searchMode=semantic`). `browse` uses filters only. `byCompany` resolves company names/IDs. `byUrls` accepts jobs.ch search or job-detail URLs. The `byRegion`/`byIndustry`/`byEmploymentType`/`byCategory` modes are kept for backward compatibility and require their matching filter. |
searchMode | string | optional | `classic` uses jobs.ch's exact keyword API with all filters and sort. `semantic` uses the site's fuzzy, cross-language search page (filters still apply; sort is relevance-only). |
searchQueries | array | optional | One or more free-text keywords (mode=search). Multiple queries are run in order and deduplicated by job ID. Leave empty to browse all jobs matching the filters. |
companyNames | array | optional | Company names to resolve against jobs.ch's company directory (mode=byCompany). Exact names are preferred; unmatched names fall back to a keyword search filtered by company name. |
companyIds | array | optional | jobs.ch company IDs (mode=byCompany). IDs are the most reliable company filter and take priority over names. |
startUrls | array | optional | jobs.ch search URLs or job-detail URLs (mode=byUrls). Search URLs carry their own filters; invalid or non-jobs.ch URLs are skipped. |
regions | array | optional | Filter by one or more Swiss canton/language-regions (multi-value OR). Allowed IDs: 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25. Required for mode=byRegion; optional elsewhere. |
industries | array | optional | Filter by one or more employer industries (multi-value OR). Allowed IDs: 1-24. Required for mode=byIndustry; optional elsewhere. |
categories | array | optional | Filter by one or more occupational fields / job functions (multi-value OR). Allowed IDs: 97-261 (165 jobs.ch categories; labels in README). Required for mode=byCategory. |
employmentTypes | array | optional | Filter by one or more contract types (multi-value OR). Allowed IDs: 1 Temporary, 2 Freelance, 3 Internship, 4 Supplementary income, 5 Permanent position, 6 Apprenticeship. Required for mode=byEmploymentType. |
positions | array | optional | Filter by one or more seniority levels (multi-value OR). Allowed IDs: 1 Executive position, 2 Specialist, 3 Employee. |
languageSkills | array | optional | Filter by one or more required working languages (multi-value OR). Allowed values: de, en, fr, it. |
companySegments | array | optional | Filter by employer segment (multi-value OR). Allowed values: gu (large company), kmu (SME), pdl (personnel-service provider). |
location | string | optional | Optional free-text city/place refinement (e.g. Zürich, Geneva, Basel). |
employmentGradeMin | integer | optional | Lower bound of the workload range in percent. |
employmentGradeMax | integer | optional | Upper bound of the workload range in percent. |
publishedSince | string | optional | Only jobs first published within this time window. |
sortBy | string | optional | Classic search sort order: relevance or newest-first. Semantic search always uses relevance. |
includeDetails | boolean | optional | Fetch each job's detail page for full description, apply URL/method, salary estimate, contacts, languages, categories, regions, deadline, benefits and company profile/ratings. Adds one request per job plus one cached request per company. |
descriptionFormat | string | optional | Format of the enriched description: plain text, original HTML, or both. |
maxDescriptionLength | integer | optional | Truncate descriptions to this many characters (0 = no truncation). |
maxItems | integer | optional | Maximum number of jobs to return. jobs.ch serves 20 rows per page and caps each search at 2,000 rows. |
maxPages | integer | optional | Maximum result pages to request per query/filter combination (jobs.ch caps searches at 100 pages). |
locale | string | optional | jobs.ch site language for URLs and translated labels. |
incrementalMode | boolean | optional | When enabled, only new or changed jobs are pushed. State is stored under `stateKey` in the key-value store. |
stateKey | string | optional | Key-value store key for incremental state (default `jobs_ch_incremental_state`). Only used when `incrementalMode` is true. |
Download the published input schema
View complete JSON
{
"description": "Search and browse jobs.ch job listings, resolve companies, or paste jobs.ch search and detail URLs.",
"properties": {
"categories": {
"default": [],
"description": "Filter by one or more occupational fields / job functions (multi-value OR). Allowed IDs: 97-261 (165 jobs.ch categories; labels in README). Required for mode=byCategory.",
"editor": "stringList",
"items": {
"type": "string"
},
"prefill": [],
"title": "Occupational fields",
"type": "array"
},
"companyIds": {
"default": [],
"description": "jobs.ch company IDs (mode=byCompany). IDs are the most reliable company filter and take priority over names.",
"editor": "stringList",
"items": {
"type": "string"
},
"prefill": [],
"title": "Company IDs",
"type": "array"
},
"companyNames": {
"default": [],
"description": "Company names to resolve against jobs.ch's company directory (mode=byCompany). Exact names are preferred; unmatched names fall back to a keyword search filtered by company name.",
"editor": "stringList",
"items": {
"type": "string"
},
"prefill": [],
"title": "Company names",
"type": "array"
},
"companySegments": {
"default": [],
"description": "Filter by employer segment (multi-value OR). Allowed values: gu (large company), kmu (SME), pdl (personnel-service provider).",
"editor": "stringList",
"items": {
"type": "string"
},
"prefill": [],
"title": "Company size",
"type": "array"
},
"descriptionFormat": {
"default": "text",
"description": "Format of the enriched description: plain text, original HTML, or both.",
"editor": "select",
"enum": [
"text",
"html",
"both"
],
"enumTitles": [
"Plain text",
"HTML",
"Both"
],
"prefill": "text",
"title": "Description format",
"type": "string"
},
"employmentGradeMax": {
"default": 100,
"description": "Upper bound of the workload range in percent.",
"maximum": 100,
"minimum": 0,
"prefill": 100,
"title": "Workload % — maximum",
"type": "integer"
},
"employmentGradeMin": {
"default": 0,
"description": "Lower bound of the workload range in percent.",
"maximum": 100,
"minimum": 0,
"prefill": 0,
"title": "Workload % — minimum",
"type": "integer"
},
"employmentTypes": {
"default": [],
"description": "Filter by one or more contract types (multi-value OR). Allowed IDs: 1 Temporary, 2 Freelance, 3 Internship, 4 Supplementary income, 5 Permanent position, 6 Apprenticeship. Required for mode=byEmploymentType.",
"editor": "stringList",
"items": {
"type": "string"
},
"prefill": [],
"title": "Employment types",
"type": "array"
},
"includeDetails": {
"default": false,
"description": "Fetch each job's detail page for full description, apply URL/method, salary estimate, contacts, languages, categories, regions, deadline, benefits and company profile/ratings. Adds one request per job plus one cached request per company.",
"prefill": false,
"title": "Include job details & company profile",
"type": "boolean"
},
"incrementalMode": {
"default": false,
"description": "When enabled, only new or changed jobs are pushed. State is stored under `stateKey` in the key-value store.",
"prefill": false,
"title": "Incremental mode",
"type": "boolean"
},
"industries": {
"default": [],
"description": "Filter by one or more employer industries (multi-value OR). Allowed IDs: 1-24. Required for mode=byIndustry; optional elsewhere.",
"editor": "stringList",
"items": {
"type": "string"
},
"prefill": [],
"title": "Industries",
"type": "array"
},
"languageSkills": {
"default": [],
"description": "Filter by one or more required working languages (multi-value OR). Allowed values: de, en, fr, it.",
"editor": "stringList",
"items": {
"type": "string"
},
"prefill": [],
"title": "Required languages",
"type": "array"
},
"locale": {
"default": "en",
"description": "jobs.ch site language for URLs and translated labels.",
"editor": "select",
"enum": [
"en",
"de",
"fr"
],
"enumTitles": [
"English",
"German (Deutsch)",
"French (Français)"
],
"prefill": "en",
"title": "Language",
"type": "string"
},
"location": {
"default": "",
"description": "Optional free-text city/place refinement (e.g. Zürich, Geneva, Basel).",
"editor": "textfield",
"prefill": "",
"title": "City / place",
"type": "string"
},
"maxDescriptionLength": {
"default": 0,
"description": "Truncate descriptions to this many characters (0 = no truncation).",
"maximum": 50000,
"minimum": 0,
"prefill": 0,
"title": "Max description length",
"type": "integer"
},
"maxItems": {
"default": 20,
"description": "Maximum number of jobs to return. jobs.ch serves 20 rows per page and caps each search at 2,000 rows.",
"maximum": 2000,
"minimum": 1,
"prefill": 3,
"title": "Max items",
"type": "integer"
},
"maxPages": {
"default": 100,
"description": "Maximum result pages to request per query/filter combination (jobs.ch caps searches at 100 pages).",
"maximum": 100,
"minimum": 1,
"prefill": 1,
"title": "Max pages",
"type": "integer"
},
"mode": {
"default": "search",
"description": "What to fetch. `search` runs classic keyword search (or semantic with `searchMode=semantic`). `browse` uses filters only. `byCompany` resolves company names/IDs. `byUrls` accepts jobs.ch search or job-detail URLs. The `byRegion`/`byIndustry`/`byEmploymentType`/`byCategory` modes are kept for backward compatibility and require their matching filter.",
"editor": "select",
"enum": [
"search",
"browse",
"byCompany",
"byUrls",
"byRegion",
"byIndustry",
"byEmploymentType",
"byCategory"
],
"enumTitles": [
"Keyword search",
"Browse all jobs by filters",
"Jobs at specific companies",
"Search or job URLs",
"Browse by canton / region",
"Browse by industry",
"Browse by employment type",
"Browse by occupational field"
],
"prefill": "search",
"title": "Mode",
"type": "string"
},
"positions": {
"default": [],
"description": "Filter by one or more seniority levels (multi-value OR). Allowed IDs: 1 Executive position, 2 Specialist, 3 Employee.",
"editor": "stringList",
"items": {
"type": "string"
},
"prefill": [],
"title": "Seniority levels",
"type": "array"
},
"publishedSince": {
"default": "",
"description": "Only jobs first published within this time window.",
"editor": "select",
"enum": [
"",
"1",
"3",
"7",
"30"
],
"enumTitles": [
"(Any time)",
"Last 24 hours",
"Last 3 days",
"Last 7 days",
"Last 30 days"
],
"prefill": "",
"title": "Published since",
"type": "string"
},
"regions": {
"default": [],
"description": "Filter by one or more Swiss canton/language-regions (multi-value OR). Allowed IDs: 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25. Required for mode=byRegion; optional elsewhere.",
"editor": "stringList",
"items": {
"type": "string"
},
"prefill": [],
"title": "Cantons / regions",
"type": "array"
},
"searchMode": {
"default": "classic",
"description": "`classic` uses jobs.ch's exact keyword API with all filters and sort. `semantic` uses the site's fuzzy, cross-language search page (filters still apply; sort is relevance-only).",
"editor": "select",
"enum": [
"classic",
"semantic"
],
"enumTitles": [
"Classic (exact keyword)",
"Semantic (fuzzy, cross-language)"
],
"prefill": "classic",
"title": "Search relevance",
"type": "string"
},
"searchQueries": {
"default": [],
"description": "One or more free-text keywords (mode=search). Multiple queries are run in order and deduplicated by job ID. Leave empty to browse all jobs matching the filters.",
"editor": "stringList",
"items": {
"type": "string"
},
"prefill": [
"software engineer"
],
"title": "Search queries",
"type": "array"
},
"sortBy": {
"default": "relevance",
"description": "Classic search sort order: relevance or newest-first. Semantic search always uses relevance.",
"editor": "select",
"enum": [
"relevance",
"date"
],
"enumTitles": [
"Relevance",
"Newest first"
],
"prefill": "relevance",
"title": "Sort order",
"type": "string"
},
"startUrls": {
"default": [],
"description": "jobs.ch search URLs or job-detail URLs (mode=byUrls). Search URLs carry their own filters; invalid or non-jobs.ch URLs are skipped.",
"editor": "requestListSources",
"items": {
"properties": {
"url": {
"description": "jobs.ch search or job-detail URL.",
"title": "URL",
"type": "string"
}
},
"required": [
"url"
],
"type": "object"
},
"prefill": [],
"title": "Start URLs",
"type": "array"
},
"stateKey": {
"default": "",
"description": "Key-value store key for incremental state (default `jobs_ch_incremental_state`). Only used when `incrementalMode` is true.",
"editor": "textfield",
"prefill": "",
"title": "Incremental state key",
"type": "string"
}
},
"required": [
"mode"
],
"schemaVersion": 1,
"title": "jobs.ch Scraper Input",
"type": "object"
}
Output schema
Published result links and storage locations. This schema is not an exhaustive contract for every field in a dataset record.
| Field | Type | Required | Description |
|---|---|---|---|
jobs | string | optional | Dataset containing all scraped jobs.ch job listings. |
Download the published output schema
View complete JSON
{
"actorOutputSchemaVersion": 1,
"description": "jobs.ch job records with listings, descriptions, salary estimates, apply links, contacts, languages, categories, regions and company profiles.",
"properties": {
"jobs": {
"description": "Dataset containing all scraped jobs.ch job listings.",
"template": "{{links.apiDefaultDatasetUrl}}/items",
"title": "jobs.ch job listings",
"type": "string"
}
},
"title": "Jobs.ch Swiss Job Listings by Region & Industry Output"
}
Dataset schema
Published dataset views and any declared field definitions. A view can select only a subset of the returned record fields.
Download the published dataset schema
View complete JSON
{
"actorSpecification": 1,
"views": {
"overview": {
"display": {
"component": "table",
"properties": {}
},
"title": "Jobs",
"transformation": {
"fields": [
"title",
"companyName",
"location",
"workloadText",
"contractType",
"salaryMin",
"salaryMax",
"postedDate",
"jobUrl"
]
}
}
}
}
Key-value schema
Published key-value store definitions, when provided by this build.
This definition is not present in the default published build. See the published README for available documentation.
Published README
Read the full published documentation
Jobs.ch Swiss Job Listings by Region & Industry
Scrape jobs.ch, Switzerland's largest job board, through its public search and company APIs. Run classic keyword searches, browse by filters, resolve employers by name or company ID, or paste jobs.ch search/job URLs. Listings can be enriched with the full description, apply URL and method, jobs.ch salary estimate, contacts, language requirements, occupational categories, regions, locations, application deadline, benefits and employer profile/ratings.
No login, cookies, API key or proxy is needed.
Modes
| Mode | What it does |
|---|---|
search |
Classic keyword search across one or more queries. With searchMode: "semantic" it uses jobs.ch's fuzzy, cross-language search page instead. |
browse |
Filter-only browsing with no keyword. |
byCompany |
Resolves company names through jobs.ch's company directory and searches all openings. Company IDs are also accepted and are the most reliable option. |
byUrls |
Accepts jobs.ch search URLs (their filters are preserved) and job-detail URLs. Detail URLs report jobStatus: online, offline, expired or unknown. |
byRegion |
Legacy mode: requires at least one value in regions; filters only. |
byIndustry |
Legacy mode: requires at least one value in industries; filters only. |
byEmploymentType |
Legacy mode: requires at least one value in employmentTypes; filters only. |
byCategory |
Legacy mode: requires at least one value in categories; filters only. |
Filters (all multi-value)
regions— 24 Swiss canton/language-region values.industries— 24 employer-industry values.categories— all 165 jobs.ch occupational categories (job function, distinct from employer industry).employmentTypes— temporary, freelance, internship, supplementary income, permanent, apprenticeship.positions— executive, specialist, employee.languageSkills— German, English, French, Italian.companySegments— large company, SME, personnel-service provider.location— free-text city/place.employmentGradeMin/employmentGradeMax— workload range in percent.publishedSince— last 1, 3, 7 or 30 days.sortBy— relevance or newest-first (classic search).
Multiple values in one filter are combined with OR; different filters are combined with AND. Jobs.ch caps every search at 2,000 rows / 100 pages of 20. Use narrower filters, date windows or several queries to get past the cap.
Output fields
Every record includes the job identity and listing fields:
jobId,title,searchQuery(when a keyword was used),jobStatus(URL mode only)companyId,companyName,companySlug,companyUrl,companyLogoUrllocation,locationDetails[](street, city, postal-code, canton, country, latitude, longitude)workloadMin,workloadMax,workloadTextcontractType,contractTypes[],seniorityLeveltags[],benefitTags[],easyApply,quickApply,homeOfficeOfferedpostedDate,initialPostedDate,postedRelative,initialPostedRelativeisNew,isBoosted,isPaid,isActive,headhunterApplicationAllowedjobUrl,recordType,scrapedAt
With includeDetails: true:
description(plain text),descriptionHtml(original HTML),descriptionSnippetapplyUrl,applyMethod(externalorform),applicationDeadlinesalaryMin,salaryMax,salaryCurrency,salaryPeriod,salaryIsEstimatecontacts[](name,firstName,lastName,role,phone), pluscontactName/contactPhonelanguageSkills[](language,level),skills[],benefits[]category(full taxonomy path),categories[],regions[],cantonCoderoleSummary,industry,workHoursText,totalOpenings,startDate,jobImmediateStartcompanyWebsite,companyOverview,companyDescriptioncompanySize,companySegment,companyIndustry,companyRating,companyReviewCount,companyOpenJobs,companySocialLinks[],companyAddressapplicantLocationRequirement,streetAddress,jobImageUrl,directApply
Empty values are omitted at every depth. 0 and false are preserved when they are real values.
Input reference
| Field | Type | Default | Description |
|---|---|---|---|
mode |
select | search |
See modes above. |
searchMode |
select | classic |
classic or semantic. |
searchQueries |
string list | [] |
One or more keyword queries; deduplicated by job ID. |
companyNames |
string list | [] |
Company names for byCompany. |
companyIds |
string list | [] |
jobs.ch company IDs for byCompany. |
startUrls |
URL list | [] |
jobs.ch search or job-detail URLs for byUrls. |
regions |
string list | [] |
Region/canton IDs. |
industries |
string list | [] |
Industry IDs. |
categories |
string list | [] |
Occupational category IDs (all 165). |
employmentTypes |
string list | [] |
Contract-type IDs. |
positions |
string list | [] |
Seniority IDs. |
languageSkills |
string list | [] |
de, en, fr, it. |
companySegments |
string list | [] |
gu, kmu, pdl. |
location |
text | "" |
City/place refinement. |
employmentGradeMin |
integer | 0 |
Minimum workload %. |
employmentGradeMax |
integer | 100 |
Maximum workload %. |
publishedSince |
select | "" |
1, 3, 7, 30 days. |
sortBy |
select | relevance |
relevance or date. |
includeDetails |
boolean | false |
Fetch job detail and company profile. |
descriptionFormat |
select | text |
text, html or both. |
maxDescriptionLength |
integer | 0 |
Truncate descriptions; 0 = no limit. |
maxItems |
integer | 20 |
1–2,000. |
maxPages |
integer | 100 |
1–100 pages per query/filter combination. |
locale |
select | en |
en, de, fr. |
incrementalMode |
boolean | false |
Push only new/changed jobs. |
stateKey |
text | "" |
Key-value store key for incremental state. |
Examples
Classic keyword search
{
"mode": "search",
"searchQueries": ["software engineer", "data engineer"],
"regions": ["19"],
"employmentGradeMin": 80,
"maxItems": 100
}
Semantic, cross-language search
{
"mode": "search",
"searchMode": "semantic",
"searchQueries": ["pflege"],
"location": "Genève",
"maxItems": 50
}
Company openings with full profiles
{
"mode": "byCompany",
"companyNames": ["Swiss Medical Network"],
"includeDetails": true,
"maxItems": 25
}
Paste search and detail URLs
{
"mode": "byUrls",
"startUrls": [
{"url": "https://www.jobs.ch/en/vacancies/?term=pflege®ion=19"},
{"url": "https://www.jobs.ch/en/vacancies/detail/e5ce43b5-b97a-46cc-a783-93eff2bf064f/"}
],
"includeDetails": true
}
Browse by occupational field, newest first
{
"mode": "browse",
"categories": ["175", "176"],
"publishedSince": "7",
"sortBy": "date",
"maxItems": 200
}
Use cases
- Recruiting and labour-market monitoring by canton, industry or occupational field.
- Company hiring-tracker dashboards built on stable
jobIdvalues. - Job-board aggregation with full descriptions and apply links.
- Salary research using jobs.ch's own annual CHF estimates (always labelled as estimates).
- Incremental monitoring of new or changed postings.
Data source
The actor reads jobs.ch's public search API, its server-rendered search pages for semantic mode, its company directory/profile API, and the job-detail pages' embedded React Query state plus schema.org JobPosting JSON-LD. All requests are anonymous, cookieless and direct.
Limitations
- 2,000-row search ceiling. jobs.ch returns at most 100 pages of 20 rows per search. Partition by filters, date windows or queries to collect more.
- Salary is an estimate. The salary fields come from jobs.ch's own salary-estimation endpoint, are annual CHF, and are marked
salaryIsEstimate: true. They are not employer-disclosed salaries. - Contacts are partial. Job-level contacts appear on a minority of postings (about 7 of 48 in testing); phone numbers are rarer and email addresses are not exposed.
- Apply links are external only. About three-quarters of sampled jobs had an external apply URL. The rest use jobs.ch's on-site form; for those,
applyMethodisformandjobUrlis the apply entry point. - Company matching is best-effort. Exact company names resolve first; otherwise prefix/contains matching is used. Company IDs are the reliable option.
- Semantic search is fuzzy by design. It mirrors jobs.ch's own relevance engine, does not support date sorting, and can return off-topic rows for an unmatched query.
FAQ
Do I need a proxy, cookie or API key? No. Every request is anonymous and direct. Existing tasks that still send a proxy object keep running; the value is ignored.
What is the difference between industry and category? industry is the employer's business sector; category is the job's occupational function. A bank can post an IT-category job, so the two taxonomies are independent.
Why did I get fewer rows than maxItems? Either jobs.ch had no more matches, the 2,000-row cap was reached, or maxPages stopped the crawl. The run's status message says which.
Why are some fields missing on some jobs? jobs.ch does not include every field on every posting, and the actor omits empty values rather than inventing placeholders. Detail fields require includeDetails: true.
Are the salary figures employer-disclosed? No. They are jobs.ch's annual CHF estimate and are always marked salaryIsEstimate: true.
How do I get past 2,000 rows? Split the search into narrower slices — one region, category, company or publication window at a time — and combine the datasets. Deduplicate on jobId.
Does semantic search support sorting? No. jobs.ch's semantic results page is relevance-only. Use classic search with sortBy: "date" for newest-first ordering.
What does incremental mode do? With incrementalMode: true, the actor stores a signature per jobId under stateKey and pushes only new or changed records. Disable it for a full snapshot.
Can I use job URLs from any jobs.ch language? Yes. English, German and French detail/search URLs are accepted, and the record URLs follow the URL's language.
Is this affiliated with jobs.ch? No. It is an independent third-party actor that reads publicly available jobs.ch data.
Python quickstart
Use a schema-derived input and the Python standard library to start a run. An Apify account and token are required for execution.
Source build 9evTfDGOOXMYXba9u. Documentation is regenerated from the default published build; private drafts are not included.