# jobup.ch Scraper 🇨🇭 Romandie Jobs (Search + Detail) (`crowley/jobup-ch-scraper`) Actor

Scrape jobup.ch — the #1 job board for French-speaking Switzerland (Geneva, Lausanne, Romandie). Search + full detail: title, company, location, canton, workload %, contract type, salary (CHF), full description, posted date. Fast JSON-API scraping, no browser. Pay-per-result.

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

## Pricing

from $2.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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use 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.ch Scraper 🇨🇭 — Scrape French-Swiss Jobs / Romandie (no login, no API key)

Scrape job postings from **jobup.ch**, the #1 job board of French-speaking Switzerland (Geneva, Lausanne, and all of Romandie), 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 **jobup 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 jobup.ch Scraper do?

You give it a search (a keyword like `infirmier` or `comptable` plus a location like `Genève` or `Lausanne`, or any jobup.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** (durée indéterminée, temporaire, indépendant, stage, revenu complémentaire, apprentissage), 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), canton code, and the direct application URL.

Under the hood it talks to jobup.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 Romandie; build outreach lists by role, canton (GE, VD, VS, NE, FR, JU), and pensum.
- **Labour-market & HR analysts** — measure demand by skill, contract type (permanent vs. temporary), and region; benchmark published CHF salaries in French-speaking Switzerland.
- **Job boards & aggregators** — supplement your own inventory with Romandie coverage from the region's market leader.
- **Founders & sales teams** — find companies that are actively hiring (a strong buying/growth signal) in Geneva, Lausanne, or a target sector.

### ⭐ Why use this scraper?

- **Romandie-focused** — jobup.ch is where French-speaking Switzerland hires: Geneva, Vaud, Valais, Neuchâtel, Fribourg, Jura. Swiss conventions built in: workload as `80-100%`, salaries in CHF, canton codes, searches in French or English.
- **API-grade data quality** — records come from jobup.ch's own JSON API, not brittle HTML parsing: stable IDs, exact timestamps with timezone, structured salary ranges. Since jobup.ch offers no public developer API, this actor is the pragmatic **jobup API alternative** for programmatic access.
- **Server-side filters** — publication date (24h/3d/7d/30d), employment types, and workload (pensum / taux d'occupation) range are applied in the jobup.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** — `maxItems` cap, 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 jobup.ch?

| Data point | Example |
| --- | --- |
| `title` | `Infirmier diplômé` |
| `company` | `Fondation Résidence du Léman` |
| `companyUrl` | `https://www.jobup.ch/fr/societes/33953-fondation-nos-penates/` |
| `companyLogo` | `https://media.jobup.ch/media/12bc0580-…?dimensions=320x0` |
| `location` | `Corseaux` |
| `canton` (detail mode) | `VD` |
| `isRemote` | `true` when the title/description mentions télétravail / remote / home office |
| **`workloadPercent`** | `80-100%` |
| `contractType` | `Durée indéterminée` |
| `salaryRaw` | `CHF 5200+ per month` |
| `salaryMin` / `salaryMax` | `5200` / `6500` |
| `salaryCurrency` | `CHF` |
| `salaryPeriod` | `month` |
| `descriptionSnippet` | `La Fondation Résidence du Léman à Corseaux, établissement médico-social…` |
| `description` (detail mode) | Full job-ad text as clean plain text |
| `applicationUrl` (detail mode) | Direct apply link when the employer uses an external ATS |
| `postedDate` | `2026-07-04T00:02:35.000Z` |
| `publishedRaw` | `2026-07-04T02:02:35+02:00` |
| `jobKey` | `b5e947c1-d42c-4352-8144-d4f3afaa6571` |
| `url` | `https://www.jobup.ch/fr/emplois/detail/b5e947c1-…/` |

> Note: `salaryRaw`/`salaryMin`/`salaryMax` appear only when the employer publishes pay — most Swiss postings don't (salary transparency is not mandatory in Switzerland). `description` and `applicationUrl` are only populated when **Scrape full detail pages** is on.

### 💰 How much does it cost to scrape jobup.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 jobup.ch

1. Open the actor on Apify and click **Try for free**.
2. Enter a **Search query** (e.g. `infirmier`) and a **Location** (e.g. `Genève`) — or paste one or more jobup.ch search URLs into **Search URLs**.
3. (Optional) Narrow with **Posted within**, **Employment types**, and **Min/Max workload**; set **Max items** and toggle **Scrape full detail pages**.
4. Leave the proxy on RESIDENTIAL / Switzerland.
5. Click **Start**, then **Export** results as JSON, CSV, Excel, XML, or HTML.

### 🧩 Input examples

Minimal — keyword + city:

```json
{
  "searchQuery": "infirmier",
  "location": "Lausanne",
  "maxItems": 100,
  "scrapeDetails": true,
  "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "CH" }
}
````

Filtered — fresh permanent 80-100% roles only:

```json
{
  "searchQuery": "comptable",
  "location": "Genève",
  "publicationDateFilter": "7d",
  "employmentTypes": ["5"],
  "workloadMin": 80,
  "workloadMax": 100,
  "maxItems": 200,
  "scrapeDetails": true,
  "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "CH" }
}
```

Multiple filtered searches — paste jobup.ch URLs (any language; filters like employment type, region, or publication date are preserved):

```json
{
  "startUrls": [
    { "url": "https://www.jobup.ch/fr/emplois/?term=ing%C3%A9nieur%20logiciel&location=Gen%C3%A8ve&employment-types=5" },
    { "url": "https://www.jobup.ch/fr/emplois/?term=infirmier&location=Lausanne" }
  ],
  "maxItems": 300,
  "scrapeDetails": true,
  "maxConcurrency": 5,
  "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "CH" }
}
```

### ⚙️ Input parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `startUrls` | array | – | jobup.ch search URLs (website or API form, any language). Overrides the query/location fields. All URL filters are preserved. |
| `searchQuery` | string | – | Job keyword (the jobup.ch `term`), e.g. `infirmier`, `comptable`. Empty with no `startUrls` = browse all jobs. |
| `location` | string | – | City, canton, or region, e.g. `Genève`, `Lausanne`, `Fribourg`. 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"` Durée indéterminée (permanent), `"1"` Temporaire, `"2"` Indépendant (freelance), `"3"` Stage, `"4"` Revenu complémentaire, `"6"` Apprentissage. Empty = all. |
| `workloadMin` / `workloadMax` | integer | – | Workload (pensum) range 0-100, e.g. `80`/`100` for 80-100% roles. Maps to the jobup.ch employment-grade filter. |
| `maxItems` | integer | `100` | Stop after this many jobs (0 = unlimited, capped at 2,000 per search by the jobup.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=infirmier` (full-detail mode):

```json
{
  "url": "https://www.jobup.ch/fr/emplois/detail/b5e947c1-d42c-4352-8144-d4f3afaa6571/",
  "jobKey": "b5e947c1-d42c-4352-8144-d4f3afaa6571",
  "title": "Infirmier diplômé",
  "company": "Fondation Résidence du Léman - Etablissement médico-social",
  "companyUrl": "https://www.jobup.ch/fr/societes/bab63906-b9c5-43f1-b328-e7f6b25b492d-fondation-residence-du-leman-etablissement-medico-social/",
  "companyLogo": "https://media.jobs.ch/media/6020d277-d0cb-4b6d-8cbe-9f37dc403bf9",
  "location": "Corseaux",
  "canton": "VD",
  "isRemote": false,
  "workloadPercent": "80-100%",
  "contractType": "Durée indéterminée",
  "salaryRaw": null,
  "salaryMin": null,
  "salaryMax": null,
  "salaryCurrency": null,
  "salaryPeriod": null,
  "descriptionSnippet": "Infirmier diplômé \nLa Fondation Résidence du Léman à Corseaux, établissement médico-social de 46 lits à mission gériatrique et ...",
  "description": "La Fondation Résidence du Léman à Corseaux, établissement médico-social de 46 lits à mission gériatrique et psychogériatrique compatible, est à la recherche d'un/e\n\nInfirmier / infirmière à 80-100%\nVos missions :\n• Assurer, développer et garantir la qualité des soins infirmiers…",
  "applicationUrl": null,
  "postedDate": "2026-07-08T08:37:00.000Z",
  "publishedRaw": "2026-07-08T10:37:00+02:00",
  "scrapedAt": "2026-07-11T15:06:44.081Z",
  "source": "detail"
}
```

In search-card mode (`scrapeDetails: false`), `description`, `canton`, and `applicationUrl` are `null` and `source` is `"search-card"`.

### 💡 Tips for best results

- **Start small.** Run with `maxItems: 20` first to validate your query, then scale up.
- **Use `startUrls` for precise filters.** Build the exact search you want on jobup.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 `scrapeDetails` off: same jobs at almost half the cost and double the speed.
- **Cover both Swiss job markets.** jobup.ch and its German-Swiss sibling jobs.ch share the JobCloud platform but have separate inventories — run the [jobs.ch Scraper](https://apify.com/crowley/jobs-ch-scraper) alongside for national coverage.

### ⚠️ 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:

```json
{ "error": "BLOCKED_OR_FAILED", "errorDescription": "Non-JSON response from https://www.jobup.ch/api/…", "url": "https://www.jobup.ch/api/v1/public/search?query=…", "scrapedAt": "2026-07-11T15: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`):

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('crowley/jobup-ch-scraper').call({
  searchQuery: 'infirmier',
  location: 'Genève',
  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`):

```python
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("crowley/jobup-ch-scraper").call(run_input={
    "searchQuery": "infirmier",
    "location": "Genève",
    "maxItems": 100,
    "scrapeDetails": True,
    "proxyConfiguration": {"useApifyProxy": True, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "CH"},
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
```

**cURL**:

```bash
curl -X POST "https://api.apify.com/v2/acts/crowley~jobup-ch-scraper/runs?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"searchQuery":"infirmier","location":"Genève","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:

```json
{
  "mcpServers": {
    "apify": {
      "command": "npx",
      "args": ["-y", "@apify/actors-mcp-server", "--actors", "crowley/jobup-ch-scraper"],
      "env": { "APIFY_TOKEN": "YOUR_APIFY_TOKEN" }
    }
  }
}
```

Example prompts: *"Find nursing jobs in Lausanne with at least an 80% workload"* or *"List accounting jobs in Geneva posted this week, with company and application link."*

### ❓ FAQ

**Do I need a jobup.ch login or API key?** No. Just a search or URL and an Apify residential proxy.

**Where does the data come from?** From jobup.ch's own public JSON API — the same one the jobup.ch website itself uses. That means stable job IDs, exact timestamps, and structured fields instead of fragile HTML scraping.

**What's the difference between jobup.ch and jobs.ch?** Both belong to JobCloud, Switzerland's largest job-ad group. jobup.ch is the French-Swiss (Romandie) board — Geneva, Vaud, Valais, Neuchâtel, Fribourg, Jura — while jobs.ch leads in German-speaking Switzerland. Their inventories differ; scrape both for national coverage.

**What does `workloadPercent` mean?** Swiss job ads state the *pensum* (French: *taux d'occupation*) — 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 French or English?** Yes. The search index covers all of jobup.ch, and queries work in the site's languages (`infirmier`, `comptable`, `software engineer`…). You can also paste `/fr/emplois/` or `/en/jobs/` URLs as `startUrls`.

**How many results can I get per search?** The jobup.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?** jobup.ch has no structured remote field, so the actor sets an `isRemote` heuristic flag when the title or description mentions *télétravail*, *remote*, or *home office*. 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 jobup.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?](https://blog.apify.com/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:

- **[jobs.ch Scraper](https://apify.com/crowley/jobs-ch-scraper)** 🇨🇭 — Switzerland's #1 job board (German-Swiss sibling of jobup.ch).
- **[anibis.ch Scraper](https://apify.com/crowley/anibis-ch-scraper)** 🇨🇭 — Swiss classifieds marketplace (search + detail).
- **[tutti.ch Scraper](https://apify.com/crowley/tutti-ch-scraper)** 🇨🇭 — Swiss classifieds (search + detail).
- **[homegate.ch Scraper](https://apify.com/crowley/homegate-ch-scraper)** 🇨🇭 — Swiss real estate listings.
- **[immoscout24.ch Scraper](https://apify.com/crowley/immoscout24-ch-scraper)** 🇨🇭 — Swiss real estate (search + detail).
- **[autoscout24.ch Scraper](https://apify.com/crowley/autoscout24-ch-scraper)** 🇨🇭 — Swiss car listings.

### 📝 Changelog

#### 1.0 — 2026-07-11

- Initial release: jobup.ch search + full job-detail scraping via the public JSON API (no browser — fast, cheap HTTP).
- Swiss workload parsing (`80-100%`), French contract-type mapping (Durée indéterminée, Temporaire, Indépendant, Stage, Revenu complémentaire, Apprentissage), CHF salary ranges (min/max/period), canton codes, ISO posted dates, clean plain-text descriptions, direct application URLs.
- Server-side filters: `publicationDateFilter` (1d/3d/7d/30d, with a client-side `postedDate` double-check), `employmentTypes`, and `workloadMin`/`workloadMax` (pensum range).
- `startUrls` support for any jobup.ch search URL (fr/en/de) with all filters preserved; website `publication-date=N` and `employment-types=N` params are translated to their API equivalents.
- Remote-work heuristic flag `isRemote` (télétravail / remote / home office).
- Pay-per-event pricing with dedup and error items; `maxItems` budget cap and anti-runaway pagination caps (2,000-result API ceiling, matchless-streak guard).

# Actor input Schema

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

Raw jobup.ch search URLs to scrape. Paste any jobup.ch search page in any language (e.g. a filtered query with location, employment type or region) — filters are preserved. Leave empty to build a search from the Search query + Location fields below.

## `searchQuery` (type: `string`):

Role / keyword to search for (the jobup.ch 'term'), e.g. infirmier, comptable, software engineer. Leave empty (with no startUrls) to browse ALL jobs on jobup.ch. Used only when no startUrls are given.

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

City, canton or region, e.g. Genève, Lausanne, Fribourg, Sion, Neuchâtel. Leave empty to search all of Switzerland. Used only when no startUrls are given.

## `publicationDateFilter` (type: `string`):

Only return jobs published within this window (same filter as the jobup.ch website). The scraper also double-checks each record's postedDate against the window before pushing it.

## `employmentTypes` (type: `array`):

Only return jobs with these contract types (jobup.ch employment-type filter). Leave empty for all types.

## `workloadMin` (type: `integer`):

Minimum employment grade (Swiss pensum / taux d'occupation), 0-100. E.g. 80 keeps only jobs offering at least an 80% workload. Leave empty / 0 for no lower bound.

## `workloadMax` (type: `integer`):

Maximum employment grade (Swiss pensum / taux d'occupation), 0-100. E.g. 60 keeps only part-time jobs up to 60%. Leave empty / 100 for no upper bound.

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

Stop after collecting this many jobs (0 = unlimited, capped at 2,000 per search by the jobup.ch API). Protects your budget.

## `scrapeDetails` (type: `boolean`):

If true, fetch each job's detail record for full data (full description, salary when published, application URL). If false, only the lighter data from search results — about half the requests.

## `maxConcurrency` (type: `integer`):

Parallel requests. The jobup.ch API is fast — 5 is a good default, capped at 10 internally to stay polite.

## `proxyConfiguration` (type: `object`):

REQUIRED. Use Apify RESIDENTIAL proxy with country CH (Switzerland) for reliable, sustained access to the jobup.ch API.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.jobup.ch/fr/emplois/?term=ing%C3%A9nieur%20logiciel&location=Gen%C3%A8ve"
    }
  ],
  "searchQuery": "infirmier",
  "location": "Genève",
  "publicationDateFilter": "any",
  "maxItems": 100,
  "scrapeDetails": true,
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "CH"
  }
}
```

# Actor output Schema

## `jobs` (type: `string`):

Job postings scraped from jobup.ch

# 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 = {
    "startUrls": [
        {
            "url": "https://www.jobup.ch/fr/emplois/?term=ing%C3%A9nieur%20logiciel&location=Gen%C3%A8ve"
        }
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "CH"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("crowley/jobup-ch-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 = {
    "startUrls": [{ "url": "https://www.jobup.ch/fr/emplois/?term=ing%C3%A9nieur%20logiciel&location=Gen%C3%A8ve" }],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "CH",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("crowley/jobup-ch-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    {
      "url": "https://www.jobup.ch/fr/emplois/?term=ing%C3%A9nieur%20logiciel&location=Gen%C3%A8ve"
    }
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "CH"
  }
}' |
apify call crowley/jobup-ch-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=crowley/jobup-ch-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "jobup.ch Scraper 🇨🇭 Romandie Jobs (Search + Detail)",
        "description": "Scrape jobup.ch — the #1 job board for French-speaking Switzerland (Geneva, Lausanne, Romandie). Search + full detail: title, company, location, canton, workload %, contract type, salary (CHF), full description, posted date. Fast JSON-API scraping, no browser. Pay-per-result.",
        "version": "1.0",
        "x-build-id": "RnysgiUpiuO4p4dRu"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/crowley~jobup-ch-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-crowley-jobup-ch-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/crowley~jobup-ch-scraper/runs": {
            "post": {
                "operationId": "runs-sync-crowley-jobup-ch-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/crowley~jobup-ch-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-crowley-jobup-ch-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "proxyConfiguration"
                ],
                "properties": {
                    "startUrls": {
                        "title": "Search URLs",
                        "type": "array",
                        "description": "Raw jobup.ch search URLs to scrape. Paste any jobup.ch search page in any language (e.g. a filtered query with location, employment type or region) — filters are preserved. Leave empty to build a search from the Search query + Location fields below.",
                        "items": {
                            "type": "object",
                            "required": [
                                "url"
                            ],
                            "properties": {
                                "url": {
                                    "type": "string",
                                    "title": "URL of a web page",
                                    "format": "uri"
                                }
                            }
                        }
                    },
                    "searchQuery": {
                        "title": "Search query (job keyword)",
                        "type": "string",
                        "description": "Role / keyword to search for (the jobup.ch 'term'), e.g. infirmier, comptable, software engineer. Leave empty (with no startUrls) to browse ALL jobs on jobup.ch. Used only when no startUrls are given."
                    },
                    "location": {
                        "title": "Location",
                        "type": "string",
                        "description": "City, canton or region, e.g. Genève, Lausanne, Fribourg, Sion, Neuchâtel. Leave empty to search all of Switzerland. Used only when no startUrls are given."
                    },
                    "publicationDateFilter": {
                        "title": "Posted within",
                        "enum": [
                            "any",
                            "1d",
                            "3d",
                            "7d",
                            "30d"
                        ],
                        "type": "string",
                        "description": "Only return jobs published within this window (same filter as the jobup.ch website). The scraper also double-checks each record's postedDate against the window before pushing it.",
                        "default": "any"
                    },
                    "employmentTypes": {
                        "title": "Employment types",
                        "uniqueItems": true,
                        "type": "array",
                        "description": "Only return jobs with these contract types (jobup.ch employment-type filter). Leave empty for all types.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "5",
                                "1",
                                "2",
                                "3",
                                "4",
                                "6"
                            ],
                            "enumTitles": [
                                "Durée indéterminée (permanent)",
                                "Temporaire",
                                "Indépendant (freelance)",
                                "Stage (internship)",
                                "Revenu complémentaire",
                                "Apprentissage"
                            ]
                        }
                    },
                    "workloadMin": {
                        "title": "Min workload (%)",
                        "minimum": 0,
                        "maximum": 100,
                        "type": "integer",
                        "description": "Minimum employment grade (Swiss pensum / taux d'occupation), 0-100. E.g. 80 keeps only jobs offering at least an 80% workload. Leave empty / 0 for no lower bound."
                    },
                    "workloadMax": {
                        "title": "Max workload (%)",
                        "minimum": 0,
                        "maximum": 100,
                        "type": "integer",
                        "description": "Maximum employment grade (Swiss pensum / taux d'occupation), 0-100. E.g. 60 keeps only part-time jobs up to 60%. Leave empty / 100 for no upper bound."
                    },
                    "maxItems": {
                        "title": "Max items",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Stop after collecting this many jobs (0 = unlimited, capped at 2,000 per search by the jobup.ch API). Protects your budget.",
                        "default": 100
                    },
                    "scrapeDetails": {
                        "title": "Scrape full detail pages",
                        "type": "boolean",
                        "description": "If true, fetch each job's detail record for full data (full description, salary when published, application URL). If false, only the lighter data from search results — about half the requests.",
                        "default": true
                    },
                    "maxConcurrency": {
                        "title": "Max concurrency",
                        "minimum": 1,
                        "maximum": 10,
                        "type": "integer",
                        "description": "Parallel requests. The jobup.ch API is fast — 5 is a good default, capped at 10 internally to stay polite.",
                        "default": 5
                    },
                    "proxyConfiguration": {
                        "title": "Proxy configuration",
                        "type": "object",
                        "description": "REQUIRED. Use Apify RESIDENTIAL proxy with country CH (Switzerland) for reliable, sustained access to the jobup.ch API.",
                        "default": {
                            "useApifyProxy": true,
                            "apifyProxyGroups": [
                                "RESIDENTIAL"
                            ],
                            "apifyProxyCountry": "CH"
                        }
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
