# Jobup Scraper (`automation-lab/jobup-ch-swiss-job-listings-scraper`) Actor

Search Jobup.ch by role or Swiss location and export structured vacancies with stable job IDs, employers, descriptions, employment terms, disclosed salaries, dates, and application URLs for recurring hiring-market monitoring.

- **URL**: https://apify.com/automation-lab/jobup-ch-swiss-job-listings-scraper.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** Jobs
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.65 / 1,000 item extracteds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Jobup Scraper

Search Jobup.ch and export structured Swiss job listings for vacancy monitoring, recruiting research, and hiring-market analysis.

The Actor turns public Jobup search and vacancy pages into stable dataset rows with job identity, title, employer, location, full description, employment terms, salary when disclosed, dates, and application URLs. Use keywords and a Swiss location, supply a Jobup search URL, or enrich one specific vacancy URL.

### What does Jobup Scraper do?

Jobup Scraper supports three practical workflows:

- search Jobup by role or keyword;
- narrow searches to a city, canton, or region;
- extract supplied Jobup search and individual vacancy URLs.

Every accepted vacancy is opened and normalized from public structured job data. The default dataset is ready for spreadsheet export, BI tools, scheduled comparison, databases, and Apify integrations.

The Actor does not require a Jobup account and does not collect private applicant data.

### Who is it for

#### Recruiters and sourcing teams

Build a current list of roles, employers, locations, workload, and apply links before planning outreach or candidate research.

#### Labor-market analysts

Schedule the same query and compare stable `jobId` values, posting dates, employers, and vacancy terms over time.

#### Sales and market-intelligence teams

Identify organizations hiring for relevant skills or expanding in a Swiss region.

#### Data engineers

Feed normalized Jobup records into a warehouse, dashboard, spreadsheet, webhook, or downstream enrichment pipeline.

### Why use this Jobup scraper?

- **Full vacancy records:** each accepted search result is enriched from its detail page.
- **Stable identity:** `jobId` supports deduplication and change tracking.
- **Flexible starts:** combine keyword searches, location filters, search URLs, and job-detail URLs.
- **Bounded runs:** control result count, pages, and transient retries.
- **Typed output:** receive explicit salary, date, employer, location, and application fields rather than raw HTML.
- **No hidden browser cost:** the Actor uses Jobup's public server-rendered pages without an automatic residential proxy or browser fallback.

### What Jobup data can I extract?

| Field | Meaning |
| --- | --- |
| `jobId` | Stable Jobup vacancy identifier |
| `title` | Job title |
| `employer` | Hiring organization when displayed |
| `employerUrl` | Employer website or Jobup employer profile |
| `employerLogoUrl` | Public employer logo URL |
| `location` | Workplace city, canton, or region |
| `postalCode` | Swiss postal code when displayed |
| `country` | Workplace country code, normally `CH` |
| `employmentType` | Contract or employment type |
| `workHours` | Workload or weekly hours when available |
| `description` | Full plain-text vacancy description |
| `salaryMin`, `salaryMax` | Disclosed salary range |
| `salaryCurrency` | Salary currency, normally CHF when disclosed |
| `salaryPeriod` | Salary period such as year or hour |
| `datePosted` | Publication date |
| `validThrough` | Closing or validity date when supplied |
| `jobStartDate` | Expected start date when supplied |
| `directApply` | Whether the source marks direct application |
| `jobUrl` | Canonical public vacancy URL |
| `applicationUrl` | Public application route exposed by Jobup |
| `sourceSearchUrl` | Input or search page that produced the row |
| `scrapedAt` | Extraction timestamp |

Fields that Jobup does not disclose are returned as `null`; the Actor does not guess salary, location, or dates.

### How to scrape Jobup jobs

1. Open the Actor input page.
2. Enter one or more roles in **Roles or keywords**.
3. Optionally enter a Swiss location such as `Geneva`, `Lausanne`, or `Zurich`.
4. Set **Maximum jobs** to the useful dataset size.
5. Run the Actor.
6. Open the **Jobup vacancies** dataset view.
7. Export JSON, CSV, Excel, XML, or RSS, or connect the dataset to another service.

A small first run of 5–10 jobs is useful for validating your query before scheduling a larger extraction.

### Input parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `keywords` | string array | `[]` | Roles or terms to search; each value creates a Jobup search |
| `location` | string | empty | Optional city, canton, or region applied to keyword searches and output filtering |
| `startUrls` | request list | `[]` | Public Jobup English/French search or job-detail URLs |
| `maxItems` | integer | `25` | Maximum unique vacancy records across all inputs; 1–5,000 |
| `maxPages` | integer | `10` | Maximum pages inspected per search; 1–100 |
| `maxRequestRetries` | integer | `2` | Retries for transient network, rate-limit, or upstream failures; 0–4 |

Provide at least one keyword or start URL. When keywords or a location are supplied together with explicit URLs, the same filters apply to emitted records.

### Input examples

#### Search software engineering jobs in Geneva

```json
{
  "keywords": ["software engineer"],
  "location": "Geneva",
  "maxItems": 10
}
```

#### Extract a supplied Jobup search

```json
{
  "startUrls": [
    { "url": "https://www.jobup.ch/en/jobs/?location=Lausanne" }
  ],
  "maxItems": 25,
  "maxPages": 3
}
```

#### Enrich an individual vacancy

```json
{
  "startUrls": [
    { "url": "https://www.jobup.ch/en/jobs/detail/533b0fc7-03b8-41f5-9704-18c00c4fdb45/" }
  ],
  "maxItems": 1
}
```

Vacancies expire, so replace an old detail URL with a current public Jobup URL if it later returns 404.

### Output example

A current run produces records shaped like this:

```json
{
  "jobId": "533b0fc7-03b8-41f5-9704-18c00c4fdb45",
  "title": "Software Engineer",
  "employer": "Banque Pictet & Cie SA",
  "employerUrl": null,
  "employerLogoUrl": null,
  "location": "Geneva",
  "postalCode": "1244",
  "country": "CH",
  "employmentType": "FULL_TIME",
  "workHours": null,
  "description": "Your team builds and maintains end-to-end applications...",
  "salaryMin": null,
  "salaryMax": null,
  "salaryCurrency": null,
  "salaryPeriod": null,
  "datePosted": "2026-08-31T04:09:13+02:00",
  "validThrough": null,
  "jobStartDate": null,
  "directApply": false,
  "jobUrl": "https://www.jobup.ch/en/jobs/detail/533b0fc7-03b8-41f5-9704-18c00c4fdb45/",
  "applicationUrl": "https://www.jobup.ch/en/jobs/detail/533b0fc7-03b8-41f5-9704-18c00c4fdb45/",
  "sourceSearchUrl": "https://www.jobup.ch/en/jobs/?term=software+engineer&location=Geneva",
  "scrapedAt": "2026-08-31T15:10:24.981Z"
}
```

Descriptions are plain text. URLs remain absolute and ready for downstream use.

### How much does it cost to scrape Jobup vacancies?

The Actor uses pay-per-event pricing:

- a one-time **Start** event per run;
- an **Item processed** event for each useful vacancy saved.

The current source package declares a $0.0025 Start fee and a six-tier item curve beginning at $0.001076 per result on BRONZE, with lower unit prices on higher Apify plans. Live Store pricing is the authority and is shown before every run.

Illustrative BRONZE costs with the current declared package price are approximately:

| Useful jobs | Estimated charge |
| ---: | ---: |
| 5 | $0.0079 |
| 25 | $0.0294 |
| 100 | $0.1101 |

Retries and pages that do not produce accepted vacancy records do not create item charges. Apify may enforce minimum account or run-charge behavior separately.

### Monitor new and changed Swiss vacancies

Schedule a Task with the same keyword/location input daily or weekly. Export each run to your destination and compare records by `jobId`.

Useful change signals include:

- a new `jobId` appears;
- an existing vacancy disappears from later search snapshots;
- employment type, workload, description, or apply route changes;
- an employer posts several related roles;
- hiring volume changes in a city or skill category.

The Actor returns snapshots. It does not maintain a cross-run history or send alerts by itself; use schedules, webhooks, datasets, or your data platform for that workflow.

### Export Jobup jobs to spreadsheets and databases

From the dataset page, select CSV or Excel for one-time analysis. For recurring pipelines, use the dataset API URL or an Apify integration.

Common destinations include:

- Google Sheets for recruiter review;
- PostgreSQL, BigQuery, or Snowflake for longitudinal analysis;
- Slack or email automation for a bounded shortlist;
- CRM or ATS staging tables after your own compliance review;
- dashboards that group postings by employer, location, or role.

### Run with the Apify API

Set `APIFY_TOKEN` in your environment. Do not commit it to source code.

#### cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~jobup-ch-swiss-job-listings-scraper/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"keywords":["software engineer"],"location":"Geneva","maxItems":10}'
```

To wait and retrieve dataset items in one request:

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~jobup-ch-swiss-job-listings-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"keywords":["nurse"],"maxItems":10}'
```

#### JavaScript

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/jobup-ch-swiss-job-listings-scraper').call({
  keywords: ['software engineer'],
  location: 'Geneva',
  maxItems: 10,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### Python

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/jobup-ch-swiss-job-listings-scraper').call(run_input={
    'keywords': ['software engineer'],
    'location': 'Geneva',
    'maxItems': 10,
})
items = client.dataset(run['defaultDatasetId']).list_items().items
print(items)
```

### Use Jobup Scraper through MCP

Add the Apify MCP server to Claude Code:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/jobup-ch-swiss-job-listings-scraper"
```

#### Claude Desktop

Add this remote server JSON to Claude Desktop's MCP configuration.

#### Cursor

Open Cursor's MCP settings and add the same remote server JSON.

#### VS Code

Add the same server URL through your MCP-compatible VS Code extension or workspace MCP settings.

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/jobup-ch-swiss-job-listings-scraper"
    }
  }
}
```

Example prompts:

- “Search Jobup for 10 software engineer vacancies in Geneva and summarize employers.”
- “Extract this Jobup Lausanne result URL and return a CSV-ready vacancy table.”
- “Run the Jobup nursing monitor input and group jobs by location and employment type.”

### Reliability, pagination, and retries

The Actor reads public server-rendered search pages and structured `JobPosting` data on each detail page.

It retries timeouts, rate limits, and temporary 5xx responses with bounded backoff. Invalid URLs, deterministic 4xx responses, malformed inputs, or missing job structured data fail rather than silently generating unreliable records.

Duplicate vacancy IDs are saved once across all input seeds. Pagination stops when the configured page limit, accepted-item limit, empty page, or repeated page is reached.

### Limits and responsible operation

- Search ordering and available jobs are controlled by Jobup.
- Expired vacancies can return 404 between discovery and enrichment.
- Salary fields are frequently `null` because many employers do not publish salary.
- A location may appear as a city, canton, workplace label, or postal region.
- French URLs are accepted, but generated keyword searches use the English surface.
- The Actor does not bypass login, CAPTCHA, or private applicant systems.
- Very broad runs make one detail request per accepted candidate and take longer than small runs.

Start with a narrow input and increase limits only when the resulting scope is useful.

### Legal and ethical use

Jobup Scraper extracts publicly accessible vacancy information. You are responsible for ensuring that your use complies with Jobup's terms, applicable database and copyright rules, data-protection law, and the rules governing your downstream recruiting or analytics system.

Do not use the Actor to collect private applicant information, overwhelm the source, republish protected descriptions without a lawful basis, discriminate in employment decisions, or send unsolicited messages. Store only the fields needed for a legitimate purpose and apply appropriate retention controls.

This documentation is operational guidance, not legal advice.

### Troubleshooting

#### “Provide at least one keyword or Jobup.ch start URL”

Add a non-empty `keywords` list or a public `https://www.jobup.ch/en/jobs/...` / `https://www.jobup.ch/fr/emplois/...` URL.

#### A supplied URL is rejected

Only HTTPS Jobup English job/search URLs and French emploi/search URLs are accepted. Profile, login, application-account, and unrelated pages are outside the contract.

#### The dataset is empty

Check spelling and remove overly narrow filters. Jobup may show recommended cards for a no-match search; the Actor applies your requested terms and location before saving, so irrelevant recommendations are not returned.

#### Some salary or date fields are null

Those values were not present in Jobup's public structured vacancy data. The Actor intentionally does not infer them.

#### The run fails on a detail page

The vacancy may have expired or Jobup may be temporarily unavailable. Retry a current search URL. Transient failures are already retried up to `maxRequestRetries`.

### Related Automation Lab Actors

- [IrishJobs.ie Job Listings & Salaries Scraper](https://apify.com/automation-lab/irishjobs-job-listings-scraper) for Ireland-specific vacancy and salary analysis.
- [Built In Job Listings Scraper](https://apify.com/automation-lab/builtin-job-listings-scraper) for technology-company vacancy monitoring.
- [GCC Job Listings Aggregator](https://apify.com/automation-lab/gcc-job-listings-aggregator) for normalized Gulf-region hiring feeds.

Use Jobup Scraper when the source of record is Jobup.ch and the buyer job is Swiss vacancy extraction or recurring hiring-market monitoring.

### FAQ

#### Does this Actor need a Jobup account?

No. It processes public search and job-detail pages only.

#### Can I search several roles in one run?

Yes. Add multiple `keywords`; the Actor deduplicates overlapping vacancies by stable Jobup ID.

#### Can I scrape one vacancy URL?

Yes. Put the current detail URL in `startUrls` and set `maxItems` to 1.

#### Does it expose recruiter contacts?

It returns only information present in the public vacancy structured data. It does not promise or separately enrich recruiter contacts.

#### Can it send alerts when a job changes?

The Actor produces timestamped snapshots with stable IDs. Use an Apify schedule and webhook or compare datasets in your own automation to generate alerts.

#### Is every Jobup vacancy guaranteed?

No. Results depend on the source's current public pages, search ordering, publication state, and your limits. The Actor reports useful current records without claiming exhaustive historical coverage.

# Actor input Schema

## `keywords` (type: `array`):

Job roles or keywords to search, such as software engineer, nurse, or finance.

## `location` (type: `string`):

Optional city, canton, or region applied to keyword searches and emitted records, such as Geneva or Lausanne.

## `startUrls` (type: `array`):

Optional public Jobup.ch search-result or individual job-detail URLs in English or French.

## `maxItems` (type: `integer`):

Maximum number of unique, fully enriched vacancy records to save across all searches and URLs.

## `maxPages` (type: `integer`):

Maximum Jobup result pages to inspect for each search URL.

## `maxRequestRetries` (type: `integer`):

Maximum retries after a transient Jobup response or network failure.

## Actor input object example

```json
{
  "keywords": [
    "software engineer"
  ],
  "location": "Geneva",
  "startUrls": [],
  "maxItems": 10,
  "maxPages": 10,
  "maxRequestRetries": 2
}
```

# Actor output Schema

## `dataset` (type: `string`):

Dataset containing all extracted Jobup vacancy records.

# 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 = {
    "keywords": [
        "software engineer"
    ],
    "location": "Geneva",
    "startUrls": [],
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/jobup-ch-swiss-job-listings-scraper").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 = {
    "keywords": ["software engineer"],
    "location": "Geneva",
    "startUrls": [],
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/jobup-ch-swiss-job-listings-scraper").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 '{
  "keywords": [
    "software engineer"
  ],
  "location": "Geneva",
  "startUrls": [],
  "maxItems": 10
}' |
apify call automation-lab/jobup-ch-swiss-job-listings-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/jobup-ch-swiss-job-listings-scraper"
        }
    }
}

```

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/FjHN5JLnatFFT5AvS/builds/huEO1Ju9hus0ucLU4/openapi.json
