# Swiss Job Search API — jobs.ch, jobup.ch, jobscout24, LinkedIn (`jobhiker/swiss-job-search-api`) Actor

Swiss job postings with canton, Pensum (workload %), contract type and a 133-role taxonomy. Deduplicated across JobCloud's three boards.

- **URL**: https://apify.com/jobhiker/swiss-job-search-api.md
- **Developed by:** [Job Hiker](https://apify.com/jobhiker) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 results

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 Job Search API

Job postings from Switzerland's four main boards — **jobs.ch**, **jobup.ch**,
**jobscout24.ch** and **LinkedIn** — with the structure a Swiss hiring dataset
needs and an international feed does not model.

Around **74,000 live postings**, roughly **2,500 new per day**.

### What makes this Swiss rather than global

A worldwide LinkedIn feed carries far more postings than this and costs less per
thousand. It cannot carry any of the following.

| Field | Coverage | Why it matters |
| --- | --- | --- |
| `canton` | 92% | The two-letter code the posting's location resolves to, via a gazetteer over every Swiss municipality, locality and postal code — not a string match on the city name. Cantonal boundaries are how Swiss hiring, tax and commuting actually work. |
| `workload_pct_min` / `max` | 40% | The **Pensum**, as the range the employer advertised: `80–100%`. No international job schema models this, because it is not the full-time/part-time question. Swiss ads state it more often than they state a salary. |
| `contract_type` | 40% | `PERMANENT`, `TEMPORARY`, `INTERNSHIP` or `APPRENTICESHIP` — the contract, separate from the hours above. |
| `category` / `department` | ~90% | A 150-role taxonomy grounded in ISCO-08, under 33 departments, assigned per posting. Ask for `backend_engineer`, not `title LIKE '%engineer%'`. |
| `source_job_id` | 100% | jobs.ch, jobup.ch and jobscout24.ch are one backend serving one vacancy under one id. This is that id, so the three boards are already deduplicated rather than arriving as three copies. |
| `min_years` | 98% | Years of experience required, parsed from the posting text. `-1` means the ad states none — distinct from "not looked at". |
| `language` | 97% | Which language the ad is written in. German, French, Italian and English postings sit side by side in this market. |

Also included: `title`, `company`, `company_id` (the employer as an entity, not
a spelling), `location`, `url`, `apply_url`, `logo_url`, `description` (full
text), `posted_at`, `scraped_at`, and `salary_min`/`max`/`currency`/`period`
where the source stated one — exact when present, which is about 2% of rows.

### Usage

Everything is optional. With no input you get the most recent postings across
all of Switzerland.

```json
{
  "postedWithinDays": 2,
  "cantons": ["ZH", "ZG"],
  "departments": ["software_engineering"],
  "maxJobs": 5000
}
```

**For a daily sync, use `postedWithinDays: 2` rather than 1.** The overlap costs
one duplicate day and means a posting that arrives late is never missed. Rows
carry a stable `id`, so de-duplicating on your side is trivial.

### Departments

Pass these keys in `departments`. Each holds several roles; pass role keys such
as `backend_engineer` in `categories` for a narrower cut.

| Key | Department |
| --- | --- |
| `software_engineering` | Software Engineering |
| `data_ai` | Data & AI |
| `it_infrastructure` | IT Infrastructure, Security & Support |
| `product_design` | Product & Design |
| `engineering` | Engineering |
| `production` | Production & Manufacturing |
| `metal_automotive` | Metalwork, Machinery & Automotive |
| `electrical` | Electrical & Electronics Trades |
| `construction_trades` | Construction & Building Trades |
| `architecture_planning` | Architecture, Construction Management & Planning |
| `crafts_food` | Crafts, Watchmaking & Food Production |
| `science_pharma` | Science, Research & Pharma |
| `environment_agriculture` | Environment, Agriculture & Animals |
| `medicine_nursing` | Medicine, Nursing & Care |
| `therapy_pharmacy` | Therapy, Psychology & Pharmacy |
| `medical_assistants` | Medical Assistants & Technicians |
| `social_work` | Social Work & Counselling |
| `education` | Teaching & Childcare |
| `finance` | Finance, Insurance & Real Estate |
| `sales` | Sales & Business Development |
| `marketing` | Marketing & Communications |
| `hr` | HR & Recruiting |
| `legal` | Legal & Compliance |
| `management` | Management & Consulting |
| `office_customer_service` | Office, Administration & Customer Service |
| `logistics` | Supply Chain & Logistics |
| `transport` | Driving, Rail & Aviation |
| `hospitality` | Hospitality, Food Service & Tourism |
| `retail` | Retail |
| `personal_services` | Beauty, Sports & Personal Services |
| `facilities_security` | Facilities, Cleaning & Security |
| `public_sector` | Public Administration, Police & Defence |
| `media_culture` | Media, Arts & Culture |

### Freshness

The crawl runs four times a day. `posted_at` is what the board published, and
`scraped_at` is when it reached this dataset — filter on the first, monitor the
second.

### Limits

- Coverage is Switzerland. There is no international data here and there will
  not be.
- `salary_*` is on ~2% of postings. Swiss ads mostly do not state pay; the
  Pensum is the field that carries the equivalent signal in this market.
- `apply_url` is on ~12%. Most boards keep the apply step on their own site;
  `url` always points at the posting.
- Postings are not re-verified after they are collected, so a small share of
  older rows will have been filled at source. Filter on `postedWithinDays` if
  that matters to you.

# Actor input Schema

## `maxJobs` (type: `integer`):

How many postings to return. The whole pool is around 74,000; a daily sync usually wants postedWithinDays instead of a large number here.

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

Only postings published in the last N days. Leave empty for the whole pool. For a daily sync, 2 is the safe value — it overlaps one day so a late-arriving posting is never missed.

## `cantons` (type: `array`):

Two-letter Swiss canton codes, e.g. ZH, BE, GE. Resolved from the posting's location text by a gazetteer, present on about 92% of rows. Leave empty for all of Switzerland.

## `departments` (type: `array`):

High-level fields, e.g. software\_engineering, medicine\_nursing, construction\_trades. 33 in total; see the README for the list.

## `categories` (type: `array`):

Specific roles, e.g. backend\_engineer, data\_scientist, clinical\_research. 150 in total. More precise than departments.

## `sources` (type: `array`):

jobs.ch, jobup.ch, jobscout24.ch, linkedin.com. The three JobCloud boards serve one posting under one id, already deduplicated. Leave empty for all.

## `query` (type: `string`):

Full-text search across title, company and description. Matches by prefix, so 'kubernet' finds 'Kubernetes'.

## Actor input object example

```json
{
  "maxJobs": 1000,
  "cantons": [],
  "departments": [],
  "categories": [],
  "sources": []
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

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

No description

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("jobhiker/swiss-job-search-api").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("jobhiker/swiss-job-search-api").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 '{}' |
apify call jobhiker/swiss-job-search-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jobhiker/swiss-job-search-api"
        }
    }
}
```

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/CqgjPrMzADceiCOjn/builds/6vWgAdj9VJcSrMeb2/openapi.json
