Jobs.ch & Jobup.ch Scraper (Switzerland)
Pricing
from $1.33 / 1,000 jobs
Jobs.ch & Jobup.ch Scraper (Switzerland)
Scrape jobs.ch and jobup.ch: the same list as the site page, both sites in one run, each job charged once. Full job pages included. Detects and refuses the different list the site silently serves to some addresses. Pay per job.
Pricing
from $1.33 / 1,000 jobs
Rating
0.0
(0)
Developer
Hexagone Data
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
10 hours ago
Last modified
Categories
Security
Limited permissionsShare
Scrape job offers from jobs.ch and jobup.ch, the two big Swiss job boards of JobCloud, in one run. Paste the search URL your browser shows, filters included, or a job URL, or just type search terms, and get one record per job with its full job page: title, company, places, workload, contract, dates, ad text, translation, skills, languages, contacts published on the ad, apply link, employer salary and company data.
What makes it different:
- The same list as the site's page. The site silently serves a different list to some addresses it classifies as suspect, without any message. Usually shorter, not always: measured on
2026-11-03,
notaron jobs.ch shows 213 jobs on the page and 41 to an address classified suspect;notaron jobup.ch 41 and 22;vendeuron jobup.ch 1,213 and 895; butpflegefachpersonin Basel 173 and 180. Datacenter addresses are often in that case (measured: 3 sessions of 8, then 9 of 14). This Actor reads the classification on every list page, refuses such a page, reads it again from another address, and says so in the run summary (suspectPagesRetried,otherListSeen). Such a page is never delivered, never counted, never charged. - Two sites in one run, each job charged once. Real run on
notar: 208 on jobs.ch + 41 on jobup.ch − 14 in common = 235 jobs delivered (crossSiteDuplicates= 14). Promoted rows that the site repeats on other pages are delivered once too (measured: 1,554 rows for 1,494 jobs onengineer). - Measured against other Actors of this Store (2026-11-03: one or two searches each, lists only, job pages not compared, so a small sample).
notarin Basel on jobs.ch, 15 jobs on the site's page: one Actor delivered 15, two others delivered 0 in a run that ended as succeeded.notairein Sion on jobup.ch, 14 on the page: another Actor delivered 1. The same two searches with this Actor, later that day: 10 of 10 and 13 of 13, compared job by job with the site's list read just after (the lists had moved). - Full job pages included in the price, read by default.
- Every list is checked against the site's total: distinct jobs seen vs jobs announced, both numbers in the summary. Anything missing beyond a tolerance is reported as
partial_collection, never asdone(no tolerance on a list of one page; 2 jobs or 0.5% on a longer one, always shown under Checks).
Try it with 20 jobs
- Keep the prefilled start URL (
https://www.jobs.ch/en/vacancies/?term=engineer) and Maximum number of jobs = 20. - Click Start. You pay for 20 jobs: $0.043 (full job pages included, start fee included).
- Open Jobs overview for the table, Run summary for what was collected and why anything is missing.
What to paste
| You paste | Example | What you get |
|---|---|---|
| A search on jobs.ch, in English, German or French | https://www.jobs.ch/en/vacancies/?term=engineer, https://www.jobs.ch/de/stellenangebote/?term=pflege, https://www.jobs.ch/fr/offres-emplois/?term=vendeur | the search, checked against the site's total |
| A search on jobup.ch, in English or French | https://www.jobup.ch/en/jobs/?term=manager, https://www.jobup.ch/fr/emplois/?term=notaire&location=Sion | same |
| A search with filters | https://www.jobs.ch/en/vacancies/?term=developer&location=Lausanne&employment-grade-min=60&employment-grade-max=80, ...&publication-date=7, ...&category=102&category=103 | filters kept exactly as in the URL, repeated values included |
| A category page of jobs.ch | https://www.jobs.ch/en/vacancies/admin-hr-consulting-ceo/legal-attorneys-court/ | the category, checked against the site's total |
| One company on jobup.ch | https://www.jobup.ch/en/jobs/?company=1177 | its jobs |
| A job | https://www.jobs.ch/en/vacancies/detail/<id>/ | that job with its details |
Filters seen working: term, location, category, region, employment-type, position, language-skill, industry, publication-date, employment-grade-min, employment-grade-max,
company, benefit. page=, and jobid= when you clicked a job on the page, are ignored: the run reads the whole search.
On jobs.ch a filter alone (?location=Basel) is read with an empty term=, as the site needs it. A remote list of URLs (requestsFromUrl) is not read: it is rejected with the reason, paste the URLs themselves.
Rejected with the reason, no job charged: home pages, company pages (paste a search, or ?company=<id> on jobup.ch), /de/ on jobup.ch (it has no German pages), other sites.
A search page without any term or filter (https://www.jobs.ch/en/vacancies/) and jobup.ch category paths show no list on the site itself: status no_listing, not an empty search.
No URL? Fill Search terms: each one is searched on the chosen Sites (both by default), with Location, Language (de becomes fr on jobup.ch, and the run says so), Published in the last
N days (1, 3, 7, 30), Minimum/Maximum workload % and Contract type. URLs and search terms can be combined. A job URL only works on the site that publishes it: the same id on the other site is a 404.
Output
One record per job and run (id is the key: a job found by two of your URLs or on both sites is delivered once). Unknown values are null, never "". Real record from an Apify run of this Actor (search controller on jobs.ch, 2026-11-04), abridged: only the ad texts are shortened. Every field is listed in the
dataset schema:
{"id": "4b94317f-b4aa-4940-af41-6615cd97256f","site": "jobs.ch","url": "https://www.jobs.ch/en/vacancies/detail/4b94317f-b4aa-4940-af41-6615cd97256f/","language": "en","title": "Controller (m/w/d)","companyName": "Glutz AG","companyId": "ac026824-252f-4ee1-a901-05cadfe519ab","companyLogo": null,"place": "Solothurn","locations": [{"street": null,"postalCode": "4502","city": "Solothurn","canton": "SO","country": "CH","latitude": 47.21054,"longitude": 7.53692}],"canton": "SO","workloadMin": 80,"workloadMax": 100,"contractTypes": ["permanent"],"positionLevels": ["specialist"],"publishedAt": "2026-10-26T08:28:53+01:00","firstPublishedAt": "2026-10-26T07:28:53+00:00","isNew": false,"isPromoted": false,"easyApply": false,"benefits": [],"sourceUrl": "https://www.jobs.ch/en/vacancies/?term=controller","scrapedAt": "2026-11-04T01:23:20Z","detailsStatus": "ok","descriptionHtml": "<p></p><p><p> </p> <p>Deine Analysen schaffen Entscheidungsgrundlagen!</p> <p>Als wertvolle Ergänzung in unser...","descriptionText": "Deine Analysen schaffen Entscheidungsgrundlagen!\nAls wertvolle Ergänzung in unserem Controllingteam übernimmst...","originalLanguage": "de","translationLanguage": "en","translatedTitle": "Controller (m/f/d)","translatedDescriptionText": "Your analyses create the basis for decisions!\nAs a valuable addition to our controlling team, you take on a st...","expiresAt": "2026-12-11","street": null,"postalCode": "4502","languageSkills": [],"skills": [],"category": "Commercial / Finance / Trusts / Real Estate / Controlling","contacts": [],"applicationMethod": "external","applyUrl": "https://jobs.glutz.com/cvdropper/90673bfe6d6e4f2eaa29510162aac414/DE?src=w1zlliu0e3cinwsif5x43lsgn56uamnyvpwv1xaus8fdzozoxu634b58weydmlz3","salaryMin": null,"salaryMax": null,"salaryCurrency": null,"salaryPeriod": null,"salaryEstimateMin": 85000,"salaryEstimateMax": 125000,"salaryEstimateScope": "canton","salaryEstimateStatus": "ok","adLink": null,"companyUrl": "https://www.jobs.ch/en/companies/ac026824-252f-4ee1-a901-05cadfe519ab-glutz-ag/","companyWebsite": "https://www.glutz.com/jobs","companyEmployees": "101 - 250","companyJobCount": 3,"companyRating": 2.67,"companyRatingCount": 6}
Good to know:
titleanddescriptionTextare the original text of the ad, as the employer wrote it, never translated.translatedTitleandtranslatedDescriptionTexthold the site's translation into the language of the page, and are null when the ad is already in that language.salaryMin,salaryMax,salaryCurrency,salaryPeriod: what the employer published (measured: 4 job pages of 126). They are never mixed with the estimate below.salaryEstimateMin,salaryEstimateMax,salaryEstimateScope: the site's estimate as the site gives it (decimals included), for the canton or for Switzerland. The page shows the estimate to signed-in visitors only.salaryEstimateStatus:ok(estimate present),none(page read as a normal visitor, the site gives none on this site: a job listed on both can have an estimate on one and none on the other, measured on 9 of 51),not_read(the site hides it from addresses it classifies as suspect and none other could be found: the rest of the job is delivered and counted insalaryEstimatesNotRead),nullwhen details were not asked for or the job page could not be read.companyNameandcompanyIdare null when the employer is not disclosed, never the site's "undisclosed employer" label. A few ads carry another label in that field (measured:Company: Confidential, 1 of 2,306).isPromoted: the row first seen for this job is a promoted one on the site (a promoted job also has its normal row elsewhere in the list; a promoted row carries nolocations: they are filled from the job page whenincludeDetailsis on, and stay[]otherwise).contractTypesis[]when the ad has none: never invented; an id this Actor does not know is delivered asunknown:<id>and counted.contacts: the contact block the page shows on the ad, as published:firstName,lastName,role,phone, each null when the ad does not give it;[]when the ad shows no name or phone (measured: 650 job pages of 3,281 have a contact, 420 of them with a phone number; 3 others show-as the number, delivered as such), and null when details were not asked for or could not be read. There is no email field: an address the employer wrote stays in the ad text or, rarely, inrole.companyRatingis given for 1 review or more, even though the page only shows it from a few reviews; null with 0 reviews.locationsis[]andcantonnull on about 1 job in 4 (measured: 1,027 of 4,491), where the site gives only a place name:placeis still filled. Keep it in mind if you filter by canton.detailsStatus:ok,failed(the job page could not be read, for example removed since the list was read: the job is still delivered, detail fields null, and counted indetailsFailed), ornullwhen details were not asked for. A pasted job URL always reads its job page, even withincludeDetailsoff.
Run summary
Each run writes a readable Run summary (limited to the job count if the run is interrupted during a write) (and the same as JSON in OUTPUT). Per start URL: status, searchVersion, jobsAnnouncedBySite (last total seen), jobsSeen (distinct), jobs (newly delivered), promotedRowsRepeated, missingVsAnnounced, toleranceJobs, detailsFailed, suspectPagesRetried, otherListSeen (jobs of the different list served to a suspect address, refused), failedPages, reason. For the run: crossSiteDuplicates, detailPages, detailsFailed, detailsSkipped, detailProbes (single test requests while job pages are stopped, see Known limits), detailPagesSuspectRetried, salaryEstimatesNotRead, detailPagesReadAsHuman (0 with job pages read: none was read as a normal visitor, and the summary says so), suspectPagesRetried, listingAccess.
| Status | Meaning |
|---|---|
done | every page read from the site's normal search, no page came back short before the last one, and the distinct jobs reach the last total: exactly on a list of one page (total and rows come from the same answer), within 2 jobs or 0.5% (the larger) on a longer one, then with the warning JOBS_MISSING_WITHIN_TOLERANCE. A repeated promoted row never makes up for a missing job. Job pages that could not be read do not change this status: the row gives their number (detailsFailed) and they are listed under Checks |
partial_collection | something is missing: both numbers and the cause; warning PARTIAL_COLLECTION. Includes the case where every address tried was classified suspect |
stopped_at_limit | your maxItems or maximum charge was reached |
no_results | the site announced 0 jobs: the search is empty, the URL is wrong or the site is down (NO_RESULTS warning) |
no_listing | the site shows no list on this URL (NO_LISTING warning): add a search term, or a filter this site knows |
no_new_jobs | every job seen on this URL was really delivered by an earlier URL |
job_not_available | a pasted job does not exist on this site; no job charged |
blocked_by_site, rate_limited, network_error, http_error, not_found, site_maintenance | the site refused or did not answer even after new addresses: never reported as an empty search |
unrecognized_page | the page has no job list the Actor can read; never reported as an empty search |
not_processed, interrupted, internal_error | the URL was not reached or finished (limit reached before it, the last 5 of that kind on that site refused: warning URLS_NOT_TRIED, run stopped, unexpected error) |
A run where no URL delivers anything fails with the exact cause. Warnings (PARTIAL_COLLECTION, JOBS_MISSING_WITHIN_TOLERANCE, TERM_NOT_IN_TITLES, UNEXPECTED_DATA, JOBS_SKIPPED, UNKNOWN_CONTRACT_ID, URLS_NOT_TRIED, SALARY_ESTIMATES_NOT_READ, DETAILS_NOT_READ, DETAILS_STOPPED, JOB_NOT_AVAILABLE, NO_LISTING...) are listed under Checks, "Checks passed" is
shown only when there is none. Codes starting with ANO_ come from the shared run checks: access verdicts, a field suddenly empty, an internal error.
Pricing
Pay per event: $1.90 per 1,000 jobs ($0.0019 per job delivered), full job pages included: the same price with or without includeDetails, and for a pasted job URL. Plus a $0.005 start fee per run (charged by Apify once per GB of memory; the
default is 1 GB).
Apify subscribers pay less automatically, on every event: 10% off on the Bronze plan, 20% on Silver, 30% on Gold and above ($1.71, $1.52 and $1.33 per 1,000 jobs).
| Jobs | With full job pages (default) | Without full job pages |
|---|---|---|
| 20 | $0.043 | $0.043 |
| 1,000 | $1.905 | $1.905 |
Only jobs written to your dataset are paid, each one once, even when both sites or several of your URLs show it. A refused page, a repeated row or a job already delivered is not charged. A run that delivers nothing costs the start fee only. Your maximum charge per run is respected: the run stops cleanly before the job that would exceed it, and no job page is read for a job that could not be paid.
Known limits
- The site's search is semantic: a rare or absurd word returns jobs without any link to it (
hufschmied: 318 jobs;xqzwvkjh: 48). They are delivered because they are the site's own list, with the warningTERM_NOT_IN_TITLESwhen no title read holds the word. Some jobs of ordinary searches are looser matches too: the samenotaris 213 on the page, 41 in the strict search. - The site's list is semantic, and this Actor delivers the list the page shows. Part of the jobs its exact-word list holds are not in its semantic list (measured on two exact-word lists: of 20 jobs, 13 are in the semantic list; of 20 others, 10).
- There is no exact-match mode: this Actor reads what the page shows to a normal visitor. The stricter list served to suspect addresses is refused on purpose.
- AI translation on the page: on some ads the page shows the AI translation straight away, title included.
titleanddescriptionTextstay the original,translatedTitleandtranslatedDescriptionTexthold what the page shows. - Salary estimates depend on the address: if no address the site treats as normal is found after 10 sessions, the job is still delivered,
salaryEstimateStatus: not_read, with a warning that gives the number. - Ad texts are delivered as published: when an employer writes a contact person's name, phone number or email address inside the ad, it stays in
descriptionHtmlanddescriptionText.contactsholds only the contact block of the page, not what the ad text says. - The site's total moves by a few jobs during a read (measured: 1,495 to 1,497): the last total is the one compared, within the tolerance above; the summary says when the total did not move.
- Largest list read in full: 1,509 jobs (76 pages). The largest one announced and read in part: 6,497 jobs (325 pages, pages 100 and 200 answered). Not measured beyond.
- If every address tried is classified suspect, the page is refused and the URL ends
partial_collection: the different list is never delivered instead. With the proxy turned off or your own proxy URLs, no Swiss residential fallback is used. - A job listed on both sites is delivered once, with the fields and the link of the first site read. Measured on 43 jobs in common: 0 job page failed. A job listed on one site only is a 404 on the other.
- Time measured on Apify: with full job pages, 0.7 to 1 s per job on the longest lists (910 jobs in 662 s, 756 in 726 s), 1.5 to 2.6 s per job on smaller ones (393 jobs in 588 s, 59 in 155 s), more on very small lists (7 jobs in 46 s); without job pages, 2.7 to 5.7 s per page (29 pages in 77 s; 59 pages in 336 s; 1,485 jobs in 264 s).
- If the site refuses job pages: after 5 refused or unreadable in a row, job pages stop being read. Single test requests are then sent before some of the next result pages, from the address in use, which can be the refused one; job pages resume only when a test request is answered. A test request that lands on a removed job proves nothing, so that result page also goes without job pages. Until then jobs are still delivered and charged at the normal price, with
detailsStatus: failedand empty detail fields, the URL can still enddone, and a whole run can end with no job page read even if the site works again from other addresses. The warningDETAILS_STOPPEDgives the numbers. A new run on the Apify proxy uses new addresses: run those URLs again. - If the site refuses or fails on 5 start URLs in a row (blocks, rate limits, no answer, 5xx errors; a 4xx error does not count), counted apart for job URLs and for lists on each site, the other URLs of that kind on that site are not tried in this run, even if the site works again minutes later: status
not_processed, warningURLS_NOT_TRIED, nothing charged for them. Run them again later. - Default run timeout: 4 hours.
maxItemsdefaults to 20: set 0 to read everything.
Support
Questions, a URL that does not work, a field you need: open an issue on the Actor's Issues tab with the run link.
This Actor is not affiliated with, endorsed or sponsored by JobCloud, jobs.ch or jobup.ch. These names are trademarks of their owners, used here only to describe the websites the data comes from. Job ads are collected from the pages served to anonymous visitors, without any account.