# Alpha.ch + Topjobs.ch Scraper - Swiss JobCloud Vertical Boards (`santamaria-automations/alpha-topjobs-ch-scraper`) Actor

Scrape Swiss job listings from alpha.ch or topjobs.ch (JobCloud/Netiva family, shared inventory ~4,700 jobs). 41 fields: title, company, canton, coordinates, workload, salary, contact, apply URL, full description in 4 formats. DE/FR/IT/EN. Pay-per-result.

- **URL**: https://apify.com/santamaria-automations/alpha-topjobs-ch-scraper.md
- **Developed by:** [NanoScrape](https://apify.com/santamaria-automations) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.49 / 1,000 job search results

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?

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

## Alpha.ch + Topjobs.ch Scraper — Swiss JobCloud Vertical Boards

Scrape job listings from [Alpha.ch](https://www.alpha.ch/) **or** [Topjobs.ch](https://www.topjobs.ch/) — two Swiss vertical job boards operated by JobCloud AG on the shared Netiva backend. Both sites share **the same inventory** (~4,700 active listings), the same API, and the same job IDs — the difference is UI branding only. Pick either surface with the `host` input parameter.

Returns 41 fields: title, company, canton, coordinates, workload, salary (CHF), recruiter contact person / email / phone, external apply URL, and full description in 4 formats (plain text, snippet, HTML, markdown). Multi-language (DE / FR / IT / EN).

**Cost-effective**: uses the public JSON API + `/GetById` detail endpoint. No login required, no anti-bot friction (JobCloud endpoints work from datacenter proxies).

***

### Why one actor for two hosts?

Phase 0 verification (2026-09-03) confirmed alpha.ch and topjobs.ch are one platform:

- Same Netiva backend (`jobboard-api.netiva.ch` proxied by tenant subdomains `api.alpha.ch` / `api.topjobs.ch`)
- Same Next.js frontend (identical `buildId`)
- Same job IDs, same ordering, byte-identical SERP responses
- Same result count (~4,735 across both hosts on the same probe)

Building two separate actors would produce duplicate output. Instead, one actor with the `host` input parameter lets you pick which branding surface to attribute the listings to.

***

### Features

- 4 input modes: **searchQueries** (keywords) · **searchUrls** (paste any alpha.ch / topjobs.ch SERP URL) · **startUrls** (alias) · **directUrls** (single-job re-scrape / still-alive checks)
- Multi-language: DE, FR, IT, EN
- Sort by **newest** (default; recommended for scheduled scrapes) or **relevance** (default when a search query is supplied)
- Filter by JobCloud **category tags** (`categoryTags: ["632"]` = Informatik, `["638"]` = Medizin/Pflege, etc.), **region IDs** (`regionIds: ["103"]` = Stadt Zürich, `["105"]` = Deutschschweiz), and **workload range** (`workload: "80-100"`)
- Fleet-canonical field names: `job_url`, `title`, `company_name`, `postal_code`, `posted_at_datetime`, `valid_through`, `employment_type`, `workload_min`/`max`, `salary_min`/`max`, `remote_type`, `apply_url`, `contact_email`, `contact_phone`, `contact_person_name`, etc.
- **Description quartet**: `description_full` (plain text), `description_snippet` (300-char preview), `description_html` (cleaned HTML), `description_md` (markdown for LLM ingestion)
- Rich tag taxonomy: 6 categories exposed as separate fields (employment type, position, region, industry, profession, company size)
- Detail-page enrichment via `/Api/JobsPublic/GetById/{id}` — full JobCloud record with tags, coordinates, valid-through date, contact person, external apply URL (usually the JobCloud sibling jobs.ch/stellenangebote/detail/{jcJobId})
- Multilingual `requirements` / `benefits` extraction: scans DE/FR/IT/EN section headers (Anforderungen / Votre profil / Requisiti / Requirements etc.)
- Salary extraction from Swiss CHF patterns (`CHF 80'000 - 120'000 par an`)
- Fast SERP-only mode: `includeJobDetails: false` returns baseline fields at ~10 items/sec, no PPE detail charge
- Concurrent detail fetching (default 8 workers)
- 3-tier proxy fallback (user proxy → auto → direct) for resilience
- 128 MB memory, ~$0.001 base charge per run

***

### Sample input

```json
{
  "host": "alpha.ch",
  "searchQueries": ["informatik", "buchhalter"],
  "language": "de",
  "sortBy": "newest",
  "includeJobDetails": true,
  "maxResultsPerQuery": 50,
  "maxResults": 100,
  "maxConcurrency": 8,
  "proxyConfiguration": { "useApifyProxy": true }
}
```

**Filter by category + region** (no keyword — returns all Zürich IT jobs):

```json
{
  "host": "alpha.ch",
  "categoryTags": ["632"],
  "regionIds": ["103"],
  "workload": "80-100",
  "maxResults": 100
}
```

**Search URL mode** — paste any alpha.ch / topjobs.ch SERP URL:

```json
{
  "searchUrls": [
    "https://www.alpha.ch/de/jobs?tag=632&region=105",
    "https://www.topjobs.ch/fr/jobs?q=developer&region=111"
  ],
  "includeJobDetails": true,
  "maxResultsPerQuery": 30
}
```

**Direct URL mode** — re-scrape specific jobs:

```json
{
  "directUrls": [
    "https://www.alpha.ch/de/jobs/14727114",
    "https://www.topjobs.ch/de/jobs/14727096"
  ],
  "language": "de"
}
```

***

### Sample output

```json
{
  "id": "14727114",
  "job_url": "https://www.alpha.ch/de/jobs/14727114",
  "source_host": "alpha.ch",
  "title": "Akquisition TU-Submissionen, Gesamtleistungswettbewerbe 80–100%",
  "company_name": "e-selection AG",
  "company_logo_url": "https://media.jobs.ch/images/05389230-0f42-4c2b-953f-369385f87d72/2067x322.png",
  "company_website": "https://www.e-selection.ch",
  "location": "Luzern",
  "postal_code": "6003",
  "region": "Region Zentralschweiz",
  "country": "CH",
  "latitude": 47.05048,
  "longitude": 8.30635,
  "posted_at_datetime": "2026-09-03T19:03:27+00:00",
  "valid_through": "2026-10-18T21:59",
  "employment_type": "permanent",
  "employment_position": "specialist",
  "workload_min": 80,
  "workload_max": 100,
  "workload_text": "80 - 100%",
  "salary_min": null,
  "salary_max": null,
  "salary_currency": "CHF",
  "salary_period": null,
  "remote_type": null,
  "language": "de",
  "description_full": "In dieser Rolle sind Sie die erste Ansprechperson im Akquisitionsprozess...",
  "description_snippet": "In dieser Rolle sind Sie die erste Ansprechperson im Akquisitionsprozess und stehen am Anfang unserer Immobilienprojekte...",
  "description_html": "<p>In dieser Rolle sind Sie die erste Ansprechperson im Akquisitionsprozess...</p><p><strong>Ihre Aufgaben</strong></p><ul><li>Ausarbeitung überzeugender Angebote...</li></ul>",
  "description_md": "In dieser Rolle sind Sie die erste Ansprechperson...\n\n**Ihre Aufgaben**\n\n- Ausarbeitung überzeugender Angebote...",
  "apply_url": "https://www.jobs.ch/de/stellenangebote/detail/f64abc08-a878-42cc-b772-89168f2a4dec/",
  "contact_email": null,
  "contact_phone": null,
  "contact_person_name": "Michael Benjamin",
  "requirements": [
    "Fachkenntnisse und Erfahrung: abgeschlossene Ausbildung in Architektur oder Bauingenieurwesen...",
    "Kundenbeziehung: Kompetenz im Aufbau vertrauensvoller Kundenbeziehungen..."
  ],
  "benefits": [
    "Arbeitsplatz mit Gestaltungsspielraum und direktem Einfluss auf nachhaltige Immobilienprojekte.",
    "Kollegiales Arbeitsklima mit Förderung von Diversität und Chancengleichheit."
  ],
  "tags": ["Festanstellung", "Fachverantwortung", "Region Zentralschweiz", "Bau / Architektur / Engineering"],
  "categories": ["Bau / Architektur / Engineering", "Architektur / Bauplanung / Zeichner"],
  "coordinates": [47.05048, 8.30635],
  "source_query": "",
  "scraped_at": "2026-09-03T21:40:53Z"
}
```

***

### Pricing

Pay-per-event (PPE). Every SERP result is billed as `job-serp-result`; every enriched detail is billed as `job-detail-result` (once per job). A tiny `apify-actor-start` charge covers the run itself.

Typical cost estimate: **~$0.005 per enriched job** at the introductory rate — one of the cheapest ways to build a live Swiss job dataset.

***

### Related actors (JobCloud family)

- [jobs-ch-scraper](https://apify.com/santamaria-automations/jobs-ch-scraper) — jobs.ch, Switzerland's largest general job board
- [jobup-ch-scraper](https://apify.com/santamaria-automations/jobup-ch-scraper) — jobup.ch, French-Swiss sister site
- [arbeit-swiss-scraper](https://apify.com/santamaria-automations/arbeit-swiss-scraper) — arbeit.swiss, government job board
- [swissdevjobs-ch-scraper](https://apify.com/santamaria-automations/swissdevjobs-ch-scraper) — swissdevjobs.ch, tech-focused
- [ictjobs-ch-scraper](https://apify.com/santamaria-automations/ictjobs-ch-scraper) — ictjobs.ch, ICT vertical

***

### Troubleshooting / Issues

Report issues or request fields via [nanoscrape.com/contact](https://nanoscrape.com/contact) or open a GitHub issue.

### Changelog

- **0.1.0** (2026-09-03): Initial build. Phase 0 verified alpha.ch and topjobs.ch share the JobCloud/Netiva backend. Description quartet, `applyPrefillDefaults()`, `sortBy` enum, 3-tier proxy fallback, safeCharge PPE billing.

# Changelog

This Actor's version history is a separate document: https://apify.com/santamaria-automations/alpha-topjobs-ch-scraper/changelog.md

# Actor input Schema

## `host` (type: `string`):

Which JobCloud portal to scrape. Both hosts share the same Netiva backend and return identical job data — the difference is only the branding/URL used in the output job\_url. Pick 'alpha.ch' for jobs surfaced under alpha.ch branding, 'topjobs.ch' for topjobs.ch branding.

## `searchQueries` (type: `array`):

One or more search keywords. Each keyword runs as a separate search. Results are deduplicated across queries. Leave empty (or supply a single '' entry) to scrape all listings.

## `searchUrls` (type: `array`):

Paste one or more alpha.ch / topjobs.ch search URLs with pre-applied filters (e.g. https://www.alpha.ch/de/jobs?tag=632\&region=105). The actor extracts host, language, category tags, region IDs, and workload directly from the URL.

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

Alias for searchUrls (Apify convention). Behaves identically. Provided for compatibility with Apify tools that inject startUrls.

## `directUrls` (type: `array`):

List of alpha.ch or topjobs.ch job detail URLs (e.g. https://www.alpha.ch/de/jobs/14727114). Skips search and scrapes each URL directly. Useful for still-alive checks or targeted re-scrapes.

## `language` (type: `string`):

Interface language for API responses. Note: listing content itself is in the source language (usually German). This affects URL paths and translation of UI labels only.

## `sortBy` (type: `string`):

How to order results. 'newest' = most recently posted first (default; recommended for scheduled scrapes). 'relevance' = default keyword-ranking (best for one-off keyword searches — used automatically when a search query is supplied).

## `categoryTags` (type: `array`):

Filter by JobCloud category tag IDs (see /Api/Filter/GetFilterTree). Examples: 626=Administration/HR, 627=Finanzen, 628=Banking, 630=Marketing, 632=Informatik, 636=Bau/Architektur, 638=Medizin/Pflege. Multiple values combine as OR.

## `regionIds` (type: `array`):

Filter by JobCloud region IDs. Examples: 103=Stadt Zürich, 105=Deutschschweiz, 108=Region Basel, 109=Region Bern, 111=Region Genf, 121=Westschweiz. See sitemap on alpha.ch/topjobs.ch for the full list. Multiple values combine as OR.

## `workload` (type: `string`):

Workload range like '80-100' or '50-100'. Empty means all workloads (0-100).

## `includeJobDetails` (type: `boolean`):

Fetch full job descriptions from the /GetById detail endpoint. When enabled, populates description\_full/snippet/html/md, tags, employment\_type, workload, contact person, apply URL, requirements, benefits. Disable for fast SERP-only scrapes (~30% faster, no PPE detail charge).

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

Hard cap across all queries. Set to 0 for unlimited (per-query limited by maxResultsPerQuery). The platform has ~4,700 total active listings.

## `maxResultsPerQuery` (type: `integer`):

Cap per keyword. Example: 100 results × 3 queries = up to 300 items total (deduplicated). Set to 0 for unlimited per query.

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

Number of detail pages to fetch in parallel when includeJobDetails is enabled. Higher = faster but more proxy bandwidth. 1 = sequential.

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

Apify proxy settings. The Netiva public API works from datacenter; residential is optional. Leave defaults for auto-proxy.

## Actor input object example

```json
{
  "host": "alpha.ch",
  "searchQueries": [
    ""
  ],
  "searchUrls": [],
  "startUrls": [],
  "directUrls": [],
  "language": "de",
  "sortBy": "newest",
  "categoryTags": [],
  "regionIds": [],
  "workload": "",
  "includeJobDetails": true,
  "maxResults": 0,
  "maxResultsPerQuery": 100,
  "maxConcurrency": 8,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `jobListings` (type: `string`):

Dataset containing scraped Swiss job listings from the selected host.

# 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 = {
    "searchQueries": [
        ""
    ],
    "searchUrls": [],
    "startUrls": [],
    "directUrls": [],
    "categoryTags": [],
    "regionIds": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("santamaria-automations/alpha-topjobs-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 = {
    "searchQueries": [""],
    "searchUrls": [],
    "startUrls": [],
    "directUrls": [],
    "categoryTags": [],
    "regionIds": [],
}

# Run the Actor and wait for it to finish
run = client.actor("santamaria-automations/alpha-topjobs-ch-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 '{
  "searchQueries": [
    ""
  ],
  "searchUrls": [],
  "startUrls": [],
  "directUrls": [],
  "categoryTags": [],
  "regionIds": []
}' |
apify call santamaria-automations/alpha-topjobs-ch-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,santamaria-automations/alpha-topjobs-ch-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/VtQU2c36iNePxuccc/builds/11zmOIUMuo3PpDT1g/openapi.json
