# jobs.ch Salary Figures, Each With the Date It Was Last Reviewed (`gubidonius/jobsch-salaries`) Actor

Swiss median salaries by profession and canton from the jobs.ch salary tool. Every figure carries the date jobs.ch last reviewed it, because they are not all recent: engineer in Ticino was last reviewed in November 2024 while engineer nationally was reviewed this month.

- **URL**: https://apify.com/gubidonius/jobsch-salaries.md
- **Developed by:** [Gregory Bolshakov](https://apify.com/gubidonius) (community)
- **Categories:** Jobs, Business, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.01 / 1,000 salary figure returneds

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

## jobs.ch salaries

Swiss median salaries by profession and canton, from the jobs.ch salary tool. Every figure
comes with the date jobs.ch last reviewed it.

That date is the reason this Actor exists.

### The figures are not all from the same year

On 13 September 2026 the tool gave these answers for the same job:

- engineer, whole of Switzerland, reviewed 10 September 2026, four days old
- engineer, Zurich, reviewed 13 August 2026, one month old
- engineer, Ticino, reviewed 13 November 2024, 670 days old

All three look the same on the page. Nothing visible marks the Ticino number as nearly two
years old, and the review date is not printed anywhere a reader would look.

So a scraper that returns the number alone hands you a 2024 figure with today's date on it.

Every row here carries `lastReviewed`, `ageDays`, and an `isStale` flag against a threshold you
set. On the run above, nurse nationally was one day old and the oldest figure in the same run
was 670 days old.

### Half of what you ask for may not exist

A profession the tool has no figure for returns a page with no figure on it at all. Not a zero,
not an empty number.

Asking for 4 professions across 4 areas gave 8 figures and 8 with nothing published. The gaps
were not only the made up profession. Treuhaender in Ticino and Uri, engineer in Uri and nurse
in Ticino all came back empty. Small cantons are where the figures run out.

Those pairs are listed in the run summary with the reason, and they cost you nothing. No row
ever reports a salary of zero, because that would be a real looking number and completely
wrong.

### It is a median, whatever the page says

The structured figure is called a median. The site's own sentence about the same number reads
"average gross salary of CHF ... per year", putting the median into a sentence that says
average.

It is published here as `medianSalary`, because that is what the number is. The site's sentence
is kept in `statement` so you can see the difference yourself.

It is a model, not a survey of what employers published. jobs.ch states it is calculated for a
100 percent workload. If you want what an employer actually offered, jobs.ch does not publish
it anywhere, and jobsch-job-details records the same finding from the advert side.

### Cantons

Give two letter canton codes, or `ch` for the whole country. Every profession is asked for in
every canton you list, so 3 professions and 4 cantons is 12 requests.

A code jobs.ch does not use stops the run before anything is billed. It does not fall back to
the national figure, because that would publish a country wide number under a canton's name.

### Cost

Both events cost 0.00001 USD. One request per profession and canton pair. You are charged after
rows are written.

A useful run is a few professions against `ch` plus the two or three cantons you care about,
rather than all 27.

All measurements here are from 13 September 2026. Every row carries its own `lastReviewed`, so
you read today's number from your own run.

# Actor input Schema

## `professions` (type: `array`):

Job titles to look up, for example engineer, nurse, Treuhänder or Pflegefachfrau. The tool matches a profession it knows, and a title it does not know returns no figure rather than a zero. German, French and English titles all work.

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

Two-letter canton codes, or ch for the whole country: ch, ag, ai, ar, be, bl, bs, fr, ge, gl, gr, ju, lu, ne, nw, ow, sg, sh, so, sz, tg, ti, ur, vd, vs, zg, zh. Every profession is asked for in every canton listed, so 3 professions and 4 cantons is 12 requests. A code jobs.ch does not use stops the run rather than quietly returning national figures under a canton's name.

## `staleAfterDays` (type: `integer`):

Sets the isStale column. jobs.ch reviews each profession and canton on its own schedule and some are years apart: on 13 September 2026 the figures ranged from 1 day old to 670 days old, the oldest being engineer in Ticino, last reviewed in November 2024. 0 turns the flag off. Every row still carries lastReviewed and ageDays.

## `maxResults` (type: `integer`):

How many figures to return at most, and the most you will be charged for. A profession and area combination with no figure costs nothing and does not use a slot.

## Actor input object example

```json
{
  "professions": [
    "engineer",
    "nurse"
  ],
  "cantons": [
    "ch",
    "zh"
  ],
  "staleAfterDays": 365,
  "maxResults": 200
}
```

# Actor output Schema

## `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 = {
    "professions": [
        "engineer",
        "nurse"
    ],
    "cantons": [
        "ch",
        "zh"
    ],
    "staleAfterDays": 365,
    "maxResults": 200
};

// Run the Actor and wait for it to finish
const run = await client.actor("gubidonius/jobsch-salaries").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 = {
    "professions": [
        "engineer",
        "nurse",
    ],
    "cantons": [
        "ch",
        "zh",
    ],
    "staleAfterDays": 365,
    "maxResults": 200,
}

# Run the Actor and wait for it to finish
run = client.actor("gubidonius/jobsch-salaries").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 '{
  "professions": [
    "engineer",
    "nurse"
  ],
  "cantons": [
    "ch",
    "zh"
  ],
  "staleAfterDays": 365,
  "maxResults": 200
}' |
apify call gubidonius/jobsch-salaries --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,gubidonius/jobsch-salaries"
        }
    }
}
```

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/z9HSrP1r3QH9jGztm/builds/AI6TZVzAmjNLohthe/openapi.json
