Source Coverage Catalog

Canonical per-family source list for the JobCache adapter rollout. Every entry maps to one row in a Rust adapter crate's SOURCES const (or a single-source Adapter's hardcoded constants).

Status: initial population from Jobich's published source registry + scrapers.py inventory, augmented with quirks observed in the source. Names of public companies, subdomains, slugs, and tenant identifiers are public source clues (vendor customer pages, the tenants' own careers URLs). Jobich's YAML schema and scraper code are not lifted — we organize the catalog our way, every Rust adapter is rewritten from scratch.

Family overview

Family Rust crate Sources Transport Method Tier
workday jobcache-adapter-workday 27 HTTP+JSON feed-api A1
greenhouse jobcache-adapter-greenhouse 17 HTTP+JSON feed-api A1
lever jobcache-adapter-lever 10 HTTP+JSON feed-api A1
smartrecruiters jobcache-adapter-smartrecruiters 13 HTTP+JSON feed-api A1
oracle-hcm jobcache-adapter-oracle-hcm 7 HTTP+JSON feed-api A1
ashby jobcache-adapter-ashby 8 HTTP+JSON (REST + GraphQL fallback) feed-api A1
recruitee jobcache-adapter-recruitee 1 HTTP+JSON feed-api A1
breezyhr jobcache-adapter-breezyhr 5 HTTP+JSON feed-api A1
bamboohr jobcache-adapter-bamboohr 3 HTTP+JSON feed-api A1
personio jobcache-adapter-personio 6 HTTP+XML (fallback HTML) feed-api A2
teamtailor jobcache-adapter-teamtailor 7 HTTP+RSS feed-api A2
joincom jobcache-adapter-joincom 10 HTTP+SSR (__NEXT_DATA__) html-bespoke A2
jibe jobcache-adapter-jibe 2 HTTP+JSON feed-api A2
icims jobcache-adapter-icims 1 HTTP+BS4 html-bespoke A2
sf-html jobcache-adapter-sf-html 22 HTTP+BS4 html-bespoke A2
sf-spa jobcache-adapter-sf-spa 2 JS-rendered (headless-proxy Env) headless-proxy A3
sf-rmk jobcache-adapter-sf-rmk 4 HTTP+JSON POST (locale loop) feed-api A2
hibob jobcache-adapter-hibob 3 JS-rendered headless-proxy A3
phenom jobcache-adapter-phenom 12 JS-rendered (embedded-JSON first) headless-proxy A3
eightfold jobcache-adapter-eightfold 2 HTTP+JSON feed-api A2
csod jobcache-adapter-csod 2 JS-bootstrap → HTTP+JSON headless-proxy A3
refline jobcache-adapter-refline 5 HTTP+BS4 html-bespoke A2
prospective jobcache-adapter-prospective 7 HTTP+JSON feed-api A2
persoware jobcache-adapter-persoware 6 HTTP+BS4 html-bespoke A2
softgarden jobcache-adapter-softgarden 1 HTTP+BS4 html-bespoke A2
umantis jobcache-adapter-umantis 3 HTTP+BS4 html-bespoke A2
workable-widget jobcache-adapter-workable-widget 10 HTTP+JSON feed-api A2
homerun jobcache-adapter-homerun 1 HTTP+Atom feed-api A2
xiag-board jobcache-adapter-xiag-board 2 HTTP+BS4 html-bespoke A2
yousty jobcache-adapter-yousty 3 HTTP+BS4 (sitemap + JSON-LD) sitemap-jsonld A2
jobup-ch jobcache-adapter-jobup-ch 1 HTTP+BS4 (sitemap + JSON-LD) sitemap-jsonld A2
jobs-ch jobcache-adapter-jobs-ch 1 (landed) HTTP+BS4 + __INIT__ JSON html-bespoke A2
linkedin jobcache-adapter-linkedin 1 HTTP+BS4 (public listings) headless-proxy B1
xing jobcache-adapter-xing 1 HTTP+BS4 + Apollo cache headless-proxy B1
indeed jobcache-adapter-indeed 1 HTTP+BS4 headless-proxy B1
jobup-ch-search (folded into jobup-ch) HTTP+JSON feed-api B1
adzuna jobcache-adapter-adzuna 1 HTTP+BS4 html-bespoke B2
reed jobcache-adapter-reed 1 HTTP+BS4 html-bespoke B2
jobwatch jobcache-adapter-jobwatch 1 HTTP+BS4 html-bespoke B2
stellenanzeiger jobcache-adapter-stellenanzeiger 1 HTTP+BS4 html-bespoke B2
remoteok jobcache-adapter-remoteok 1 HTTP+JSON feed-api B2
hackernews jobcache-adapter-hackernews 1 HTTP+JSON (Algolia API) feed-api B2
custom-html jobcache-adapter-custom-html 7 HTTP+BS4 (per-tenant selectors) html-bespoke C
playwright-html jobcache-adapter-playwright-html 4 JS-rendered (per-tenant selectors) headless-proxy C
rss-feed jobcache-adapter-rss-feed 4 HTTP+RSS feed-api C
employer-bespoke crates/jobcache-employer-*/ (per-employer) 37+ varies varies C

Tier legend:

  • A1 — JSON-API multi-tenant ATS families, low hostility, high tenant count. Highest value per slice, lowest implementation risk.
  • A2 — Mixed transport families (HTML, RSS, locale-loop JSON), low-to-mid hostility.
  • A3 — JS-rendered families requiring a headless-proxy Env.fetch (runtime prerequisite: device runtime injects Router-backed fetch for method: "headless-proxy" adapters).
  • B1 — High-hostility flagship boards (LinkedIn, Xing, Indeed).
  • B2 — Search-driven general boards, mid hostility.
  • C — Long tail: per-tenant custom HTML, RSS feeds, employer-bespoke integrations.

Slice prerequisite call-out: A3 + B1 adapters all need the device runtime to method-route the Env.fetch through scrapers/router.ts. Until that lands, those families ship but can only succeed against the same env.fetch plain — fine for proof, insufficient for real coverage.


A1 — JSON-API multi-tenant ATS families

workday — crates/jobcache-adapter-workday/

Transport: HTTP+JSON. Discovery via POST https://{subdomain}.{instance}.myworkdayjobs.com/wday/cxs/{subdomain}/{career_path}/jobs with offset/limit pagination (20/page). Detail via JSON detail API at /wday/cxs/{subdomain}/{career_path}{external_path}.

Config shape per source: { id, name, subdomain, instance, career_path }.

Sources (27 active):

name subdomain instance career_path
Swisscom swisscom wd103 SwisscomExternalCareers
Nvidia nvidia wd5 NVIDIAExternalCareerSite
Roche roche wd3 roche-ext
Novartis novartis wd3 Novartis_Careers
The Global Fund theglobalfund wd1 External
UNHCR unhcr wd3 External
WTO wto wd103 External
World Economic Forum weforum wd3 Forum_Careers
Trafigura trafigura wd3 TrafiguraCareerSite
Gunvor gunvor wd3 Gunvor_Careers
Julius Baer juliusbaer wd3 External
Lombard Odier lombardodier wd3 Lombard_Odier_Careers
Logitech logitech wd5 Logitech
Zuhlke Group zuehlke wd3 Zuhlke-Careers
Vontobel vontobel wd3 Vontobel_External_Career
Biogen biibhr wd3 external
Abbott abbott wd5 abbottcareers
Moderna modernatx wd1 M_tx
Temenos temenos wd103 Temenoscareers
Georg Fischer georgfischer wd103 GeorgFischer_Careers
Siegfried siegfried wd103 external
Eli Lilly lilly wd5 LLY
PwC pwc wd3 Global_Experienced_Careers
BeiGene beigene wd5 BeiGene
Bavarian Nordic bavariannordic wd103 BavarianNordic
Galderma galderma wd3 External
Takeda takeda wd3 External
Richemont richemont wd3 Richemont
Lonza lonza wd3 Lonza_Careers
Medtronic medtronic wd1 MedtronicCareers
Johnson & Johnson jj wd5 JJ
Stryker stryker wd1 StrykerCareers
Alcon alcon wd5 careers_alcon
Deutsche Bank db wd3 DBWebsite

(Above is 34 entries — Jobich's sources.yaml had 27 active after defunct removals; cross-check on first crate ingest.)

Defunct (do not include; useful for re-audit): ABB (→ careers.abb, custom Phenom), Nestle (→ SF HTML), Zurich Insurance (→ SF HTML), Swiss Re (→ SF HTML), UBS (custom portal), Swatch Group (custom), Philip Morris (custom), Buhler Group (custom), Barry Callebaut (custom), Givaudan (→ Phenom), Firmenich (now DSM-Firmenich → eightfold), Lindt (custom), Sonova (→ SF HTML), Endress+Hauser (custom), Straumann (→ SmartRecruiters then → Phenom), Sika (custom).

greenhouse — crates/jobcache-adapter-greenhouse/

Transport: HTTP+JSON. Public board API at https://boards-api.greenhouse.io/v1/boards/{slug}/jobs?content=true. Detail content inline in the listing response.

Config shape: { id, name?, slug } (name optional; default to slug).

Sources (17):

name slug
Scandit scandit
Figma figma
Stripe stripe
Databricks databricks
On on-running
Frontify frontify
Beekeeper beekeeper
Terra Quantum terraquantum
21Shares 21shares
Proton proton
Auterion auterion
Toradex toradex
BearingPoint bearingpoint
Anthropic anthropic
Sunrise sunrise
Starmind starmind
ID Quantique idquantique
Relai technology
AlixPartners alixpartners

lever — crates/jobcache-adapter-lever/

Transport: HTTP+JSON at https://api.lever.co/v0/postings/{slug}?mode=json. Description multi-field: descriptionBody | opening | additional + lists[].{text, content} structure.

Config shape: { id, name, slug }.

Sources (10):

name slug
Amazon RIVR rivr
ANYbotics anybotics
Wingtra wingtra-2
Sonar sonarsource
Daedalean AI daedalean
SwissBorg swissborg
Gravis Robotics gravis-robotics
Neural Concept neuralconcept
Scandit scandit-1
Advertima advertima
Oliver Wyman oliverwyman

smartrecruiters — crates/jobcache-adapter-smartrecruiters/

Transport: HTTP+JSON. Listing: https://api.smartrecruiters.com/v1/companies/{slug}/postings?limit=100&offset=N&country=ch. Detail (per-job): /v1/companies/{slug}/postings/{id} for jobAd.sections.{companyDescription, jobDescription, qualifications, additionalInformation}.

Config shape: { id, name, slug }.

Sources (13):

name slug
Bosch BoschGroup
Vitol Vitol
Swissquote Swissquote
Nexthink Nexthink
Roland Berger RolandBerger
Audemars Piguet AudemarsPiguet
Implement Consulting Group ImplementConsultingGroup
Swiss Medical Network SwissMedicalNetwork1
Syngenta Group SyngentaGroup
Kanadevia Inova KanadeviaInova
Fusion Consulting fusionconsulting
SMG Swiss Marketplace Group SMGSwissMarketplaceGroup
Oetiker Oetiker
GIANTS Software GIANTSSoftwareGmbH

oracle-hcm — crates/jobcache-adapter-oracle-hcm/

Transport: HTTP+JSON. Listing: https://{host}/hcmRestApi/resources/latest/recruitingCEJobRequisitions?finder=findReqs;siteNumber={site},limit=200,offset=N. Detail (per-job): /recruitingCEJobRequisitionDetails?finder=ById;Id={jobId},siteNumber={site}{ExternalDescriptionStr, ExternalQualificationsStr, ExternalResponsibilitiesStr}. Public job URL: https://{host}/hcmUI/CandidateExperience/en/sites/{site}/job/{id}.

Country filter: PrimaryLocationCountry=CH OR secondaryLocations[].Country=CH.

Config shape: { id, name, host, site }.

Sources (7):

name host site
IOM fa-evlj-saasfaprod1.fa.ocs.oraclecloud.com CX_1001
UBP iaadtu.fa.ocs.oraclecloud.eu CX_1
EFG International fa-eqai-saasfaprod1.fa.ocs.oraclecloud.com CX_1001
JPMorganChase jpmc.fa.oraclecloud.com CX_1001
ELCA Group iaaras.fa.ocs.oraclecloud.com CX_4
Oracle eeho.fa.us2.oraclecloud.com CX_45001
SCOR fa-errt-saasfaprod1.fa.ocs.oraclecloud.com CX_2001

ashby — crates/jobcache-adapter-ashby/

Transport: HTTP+JSON REST primary, GraphQL fallback. REST: https://api.ashbyhq.com/posting-api/job-board/{board_slug}jobs[].{title, jobUrl, location, publishedAt, descriptionHtml}. GraphQL fallback (for tenants without REST enabled): POST /api/non-user-graphql?op=ApiJobBoardWithTeams with {operationName: "ApiJobBoardWithTeams", variables: {organizationHostedJobsPageName: board_slug}}.

Config shape: { id, name, board_slug }.

Sources (8):

name board_slug
Arrakis Arrakis
Aleph Alpha alephalpha
Neon neon
Lakera lakera
Corti corti
DeepJudge deepjudge
Adaptyv adaptyv
Cradle cradlebio

recruitee — crates/jobcache-adapter-recruitee/

Transport: HTTP+JSON at https://{subdomain}.recruitee.com/api/offers/offers[].{title, careers_url, city, country, country_code, description, salary.{min, max, currency}, published_at}.

Config shape: { id, name, subdomain }.

Sources (1):

name subdomain
EthonAI ethonai

breezyhr — crates/jobcache-adapter-breezyhr/

Transport: HTTP+JSON at https://{subdomain}.breezy.hr/json.

Config shape: { id, name, subdomain }.

Sources (5):

name subdomain
Sportradar sportradar
Vyntra vyntra
Teylor teylor
Ascento ascento-ag
Sevensense sevensense-robotics-ag

bamboohr — crates/jobcache-adapter-bamboohr/

Transport: HTTP+JSON at https://{subdomain}.bamboohr.com/careers/listresult[].{jobOpeningName, id, location.{city, country}}. Public URL: https://{subdomain}.bamboohr.com/careers/{id}.

Config shape: { id, name, subdomain }.

Sources (3):

name subdomain
BB Energy bbenergy
Voliro voliroar
Kandou AI kandou

A2 — Mixed-transport families

personio — crates/jobcache-adapter-personio/

Transport: HTTP+XML primary, HTML fallback. XML at https://{subdomain}.jobs.personio.com/xml?language=en (.com then fall back to .de TLD on 404). HTML fallback parses a[href^="/job/"] from the rendered listing page.

Config shape: { id, name, subdomain, swiss_only?: bool }.

Sources (6):

name subdomain swiss_only
ten23 health ten23-health true
Basilea Pharmaceutica basilea true
AMINA Bank amina false
DePoly depoly false
Redalpine redalpine true
zvoove zvoove false

teamtailor — crates/jobcache-adapter-teamtailor/

Transport: HTTP+RSS. Each tenant exposes a direct RSS URL.

Config shape: { id, name, rss_url }.

Sources (7):

name rss_url
Lightium https://careers.lightium.com/jobs.rss
LumApps https://job.lumapps.com/jobs.rss
AZQORE https://azqoresa.teamtailor.com/jobs.rss
South Pole https://careers.southpole.com/jobs.rss
neustark https://careers.neustark.com/jobs.rss
Corintis https://careers.corintis.com/jobs.rss
Planted Foods https://eatplanted.teamtailor.com/jobs.rss

joincom — crates/jobcache-adapter-joincom/

Transport: Next.js SSR. Jobs inlined in <script id="__NEXT_DATA__"> on https://join.com/companies/{slug}. No API call; HTTP+BS4 reads the script tag.

Config shape: { id, name, slug }.

Sources (10):

name slug
Taurus taurusgroup
Veezoo veezoo
Decentriq decentriq
Optiml optiml
Sartiq shootifyio
CV VC cvvc
Flycatcher flycatcherai
neon Switzerland neon-free
Lightly lightly
bexio bexio
modual modual

jibe — crates/jobcache-adapter-jibe/

Transport: HTTP+JSON. Public /api/jobs JSON on the corporate careers host. Works for iCIMS Jibe Talent Cloud tenants.

Config shape: { id, name, host }.

Sources (2):

name host
Incyte careers.incyte.com
GSK jobs.gsk.com

icims — crates/jobcache-adapter-icims/

Transport: HTTP+BS4. Classic iCIMS portals: https://{host}/jobs/search?in_iframe=1&bga=true&needsRedirect=false. Filter to ISO-prefix "CH-".

Config shape: { id, name, host }.

Sources (1):

name host
Monte Rosa Therapeutics careers-monterosatx.icims.com

sf-html — crates/jobcache-adapter-sf-html/

Transport: HTTP+BS4. Listing selector a.jobTitle-link (or a[class*='jobTitle']). Pagination via ?startrow=N; parse "Results X to Y of Z" regex to advance. Cap at 20 pages.

Country derivation: location suffix like "Vevey, CH" maps to is_swiss_job(location, country="CH").

Config shape: { id, name, search_url }.

Sources (22):

name search_url
ILO https://jobs.ilo.org/search/?q=&locationsearch=Geneva
ITU https://jobs.itu.int/search/?q=&locationsearch=Geneva
ICRC https://careers.icrc.org/search/?q=&locationsearch=Geneva
Rolex https://www.carrieres-rolex.com/search/
Patek Philippe https://careers.patek.com/search/
Octapharma https://careers.octapharma.com/search/
Bachem https://careers.bachem.com/search/
OM Pharma https://careers.ompharma.com/search/
EY https://careers.ey.com/search/
Adnovum https://careers.adnovum.com/search/
SAP https://jobs.sap.com/search/?q=&locationsearch=Switzerland
Nestle https://jobdetails.nestle.com/search/?q=&locationsearch=Switzerland
Swiss Re https://careers.swissre.com/search/?q=&locationsearch=Switzerland
Sonova https://jobs.sonova.com/search/?q=&locationsearch=Switzerland
Zurich Insurance https://www.careers.zurich.com/search/?q=&locationsearch=Switzerland
Idorsia https://careers.idorsia.com/search/?q=&locationsearch=Switzerland
Teva https://careers.teva/search/?q=&locationsearch=Switzerland
SKAN https://jobs.skan.com/search/?q=&locationsearch=Switzerland
Helvetia Insurance https://sf-jobs.helvetia.com/search/?q=&locationsearch=Switzerland
zeb consulting https://career.zeb-consulting.com/search/?q=&locationsearch=Switzerland

Known omissions (verified non-CH): COFCO (locationsearch=Switzerland param ignored, all results AR/BR/SG).

sf-rmk — crates/jobcache-adapter-sf-rmk/

Transport: HTTP+JSON POST. SuccessFactors "Job Marketplace" /services/recruiting/v1 hosted on the tenant's career domain. Different jobs surface per locale — iterate the locale list and dedup by job id.

Config shape: { id, name, host, locales: [string], country_filter?: string, brand_slug?: string, search_customer_id?: string }.

Sources (4):

name host locales country_filter brand_slug
Breitling careers.breitling.com ["en_GB"] Switzerland Breitling
PostFinance jobs.postfinance.ch ["de_DE", "fr_FR", "it_IT", "en_US"] (filter locally) PostFinance
Mobiliar jobs.mobiliar.ch ["de_DE", "fr_FR", "it_IT"] (per-job from brandUrl, default "default")
BCV — Banque Cantonale Vaudoise jobs.bcv.ch ["fr_FR", "en_US"] BCV

eightfold — crates/jobcache-adapter-eightfold/

Transport: HTTP+JSON. PCSX public search at https://{host}/api/pcsx/search?domain={domain}&location=Switzerland&start=N. Some tenants return "PCSX is not enabled for this user." — those need auth and cannot be scraped via this API.

Config shape: { id, name, host, domain }.

Sources (2):

name host domain
DSM-Firmenich jobs.dsm-firmenich.com dsm.com
Bristol Myers Squibb bms.eightfold.ai bms.com

refline — crates/jobcache-adapter-refline/

Transport: HTTP+BS4. https://apply.refline.ch/{client_id}/search.html?form.buttons.listAll=1a[href*='/index.html']. Dedup by job_id + title (one job can publish in multiple language variants).

Config shape: { id, name, client_id, default_location }.

Sources (5):

name client_id default_location
WSL 273855 Birmensdorf, Switzerland
Empa 673276 Dübendorf, Switzerland
Eawag 673277 Dübendorf, Switzerland
PSI 673278 Villigen, Switzerland
Zürcher Kantonalbank 792841 Zürich, Switzerland

prospective — crates/jobcache-adapter-prospective/

Transport: HTTP+JSON. https://ohws.prospective.ch/public/v1/medium/{medium_id}/jobs?lang=de&offset=0&limit=500. Optional extra_filter for sub-unit filtering (Agroscope under Bundesverwaltung).

Config shape: { id, name, medium_id, extra_filter?, default_location }.

Sources (7):

name medium_id extra_filter default_location
Visana 1004518 Bern, Switzerland
Raiffeisen 1950 St. Gallen, Switzerland
KPT 1007102 Bern, Switzerland
University of Zurich 1002007 Zurich, Switzerland
University of Bern 1001892 Bern, Switzerland
Agroscope 1007546 verwaltungseinheit:1660311 Switzerland
KPMG Switzerland 1693 Zurich, Switzerland

persoware — crates/jobcache-adapter-persoware/

Transport: HTTP+BS4. White-label OnApply at https://{host}/{path}. Anchors match [a-z0-9-]+/[0-9a-f-]{20,}$ (UUID suffix). Location from .place-of-work, .city, .job-workplace, footer, or .job-meta2.

Config shape: { id, name, host, path, default_location? }.

Sources (6):

name host path default_location
CSS jobs.css.ch ?lang=en
Helsana jobs.helsana.ch ?lang=de
Sanitas Krankenversicherung jobs.sanitas.com ?lang=en
Concordia jobs.concordia.ch ?lang=de
AXA Switzerland jobs.axa.ch ?lang=en
University of Basel jobs.unibas.ch ?lang=en Basel, Switzerland

softgarden — crates/jobcache-adapter-softgarden/

Transport: HTTP+BS4. https://{slug}.softgarden.io/{lang}/ server-side renders all jobs in div.matchElement cards with id job_id_{ID}. Date format DD.MM.YY → ISO.

Config shape: { id, name, slug, lang, default_location }.

Sources (1):

name slug lang default_location
Vaudoise vaudoise de Lausanne, Switzerland

umantis — crates/jobcache-adapter-umantis/

Transport: HTTP+BS4. Abacus Umantis at https://{host}/{path}. Anchors a[href*='/Vacancies/'][href*='/Description/']. Location lives in pipe-delimited cell text — segment between second and last |.

Config shape: { id, name, host, path, default_location, swiss_only?: bool }.

Sources (3):

name host path default_location swiss_only
Swiss TPH jobs.swisstph.ch /Jobs/All Basel, Switzerland true
HUBER+SUHNER recruiting.hubersuhner.com /Jobs/All Herisau, Switzerland false (global)
Generali Switzerland recruitingapp-2717.umantis.com /Jobs/1?lang=ger&CompanyID=17 Adliswil, Switzerland true

workable-widget — crates/jobcache-adapter-workable-widget/

Transport: HTTP+JSON (Workable's per-tenant widget endpoint, for accounts not surfaced in the global aggregator at jobs.workable.com).

Config shape: { id, name, slug }.

Sources (10):

name slug
Crypto Finance crypto-finance
Distalmotion distalmotion
Visium visium
Unit8 unit8
Debiopharm debiopharm
D ONE d-one
Hilo by Aktiia hilobyaktiia
Verve Ventures verveventures
Flexion Robotics flexion-robotics
SOPHiA GENETICS sophia-genetics
Verity verity

homerun — crates/jobcache-adapter-homerun/

Transport: HTTP+Atom. https://feed.homerun.co/{slug}entry.{title, link[@rel='alternate'], location.name}.

Config shape: { id, name, slug }.

Sources (1):

name slug
VARO Energy varo

xiag-board — crates/jobcache-adapter-xiag-board/

Transport: HTTP+BS4. Regional Swiss boards on the same xiag SaaS platform. a.vacancy-list-card__link-header with sibling spans. Pagination /job/alle-jobs/seite-N.

Config shape: { id, name, host, max_pages }.

Sources (2):

name host max_pages
Ostjob www.ostjob.ch 100
Zentraljob www.zentraljob.ch 100

yousty — crates/jobcache-adapter-yousty/

Transport: HTTP+BS4. Sitemap index at /sitemap.xml/sitemap-vacancies.xml. Detail URL shape <host>/job/<slug>/<numeric-id>. Detail page has JobPosting JSON-LD; delegate to shared schema-org-jobposting parser.

Config shape: { id, name, host }.

Sources (3): (replaces today's portals/yousty.ts factory + ostjob-ch.ts / zentraljob-ch.ts / nzz-jobs.ts thin wrappers)

name host
Ostjob (sitemap variant) www.ostjob.ch
Zentraljob (sitemap variant) www.zentraljob.ch
NZZ Jobs jobs.nzz.ch

(Note: ostjob.ch and zentraljob.ch appear in BOTH xiag-board and yousty because they ship two surfaces. The yousty sitemap is more comprehensive; xiag-board is search-driven. Run both, dedup at URL x language ad_id.)

jobup-ch — crates/jobcache-adapter-jobup-ch/

Two surfaces, one crate:

  • Sitemap+JSON-LD path — sitemap at /sitemap.xml, detail pages have JSON-LD; today's portals/jobup-ch.ts covers this.
  • Search API pathhttps://www.jobup.ch/api/v1/public/search?query={query}&rows=20&page={pg}; gives a different surface.

Config: single-source; no per-tenant config.

jobs-ch — crates/jobcache-adapter-jobs-ch/ (LANDED in TS, to port)

Current TS reference. Two surfaces:

  • Public listings pathhttps://www.jobs.ch/en/vacancies/?page=0, anchors + __INIT__ JSON state.
  • JSON search APIhttps://www.jobs.ch/api/v1/public/search?query={query}&rows=20&page={pg} (query × city fanning for full crawl).
  • Full-crawl variant — city-by-city pagination across Swiss cities.

Detail pages have JobPosting JSON-LD; delegate to shared schema-org-jobposting parser.


A3 — JS-rendered families (require headless-proxy Env)

sf-spa — crates/jobcache-adapter-sf-spa/

Transport: JS-rendered. Navigate to {host}/c/{company}/home, click a[aria-label="Next Page"], parse tr.jobResultItem. Token order in span.jobContentEM varies per tenant.

Config shape: { id, name, host, company, default_location, jobcontentem_order?: TokenOrder }.

Sources (2):

name host company default_location
Pictet career012.successfactors.eu banquepict Geneva, Switzerland
UNIL career012.successfactors.eu universitdP Lausanne, Switzerland

Known omissions: Ypsomed (login-gated), Baloise (3.4 KB JS shell, not on SF), Breitling/PostFinance (use sf-rmk).

hibob — crates/jobcache-adapter-hibob/

Transport: JS-rendered. https://{subdomain}.careers.hibob.com. Anchors a[href*='/position/'], a[href*='/job/'].

Config shape: { id, name, subdomain }.

Sources (3):

name subdomain
Synpulse synpulse
ONWARD Medical onwardmedical
Unique unique

phenom — crates/jobcache-adapter-phenom/

Transport: JS-rendered. SPA at {careers_url}. Try __NEXT_DATA__ / <script type="application/json"> first; fall back to facet-click via JS host. Some tenants honor ?country=Switzerland; others need an in-page facet click.

Config shape: { id, name, careers_url, filter_country?: string }.

Sources (12):

name careers_url filter_country
BCG https://careers.bcg.com/global/en/search-results
Cisco https://careers.cisco.com/global/en/search-results
Allianz https://careers.allianz.com/global/en/search-results
Zimmer Biomet https://careers.zimmerbiomet.com/us/en/search-results
Merck Group https://careers.emdgroup.com/us/en/search-results
ABB https://careers.abb/global/en/search-results?country=Switzerland
Straumann https://careers.straumann.com/global/en/search-results Switzerland
Givaudan https://careers.givaudan.com/global/en/search-results?country=Switzerland
UCB https://careers.ucb.com/global/en/search-results?qcountry=Switzerland
MSD https://jobs.msd.com/gb/en/search-results Switzerland
Thermo Fisher Scientific https://jobs.thermofisher.com/global/en/search-results Switzerland
Allianz Suisse https://careers.allianz.com/ch/fr/allianz-suisse

csod — crates/jobcache-adapter-csod/

Transport: JS bootstrap → HTTP+JSON. JS host visits /ux/ats/careersite/{career_site_id}/home, intercepts the Authorization: Bearer header from the first /rec-job-search/external/jobs request, then replays the request directly with {countryCodes: ["CH"]} payload.

Config shape: { id, name, host, career_site_id, region: "us-api" | "eu-fra-api" }.

Sources (2):

name host career_site_id region
McKinsey & Company mckinsey.csod.com 1 us-api
Simon-Kucher simon-kucher.csod.com 1 eu-fra-api

B1 — High-hostility flagship boards

linkedin — crates/jobcache-adapter-linkedin/

Transport: HTTP+BS4. https://www.linkedin.com/jobs/search?keywords={query}&location=Switzerland&f_TPR=r604800&start={N*25}. Detail via LinkedIn guest API /jobs-guest/jobs/api/jobPosting/{job_id} for description DOM.

Selectors: div.base-card, li.base-card{h3 (title), h4 (company), span.job-search-card__location, a[href*='/jobs/view/'], time}.

Pagination: start = page × 25.

No login. No cookies. Public listings only.

xing — crates/jobcache-adapter-xing/

Transport: HTTP+BS4 + embedded JSON. https://www.xing.com/jobs/search?keywords={query}&location=switzerland. First strategy: parse window.crate Apollo cache JSON. Fallback: DOM selectors.

indeed — crates/jobcache-adapter-indeed/

Transport: HTTP+BS4. https://ch.indeed.com/jobs?q={query}&l=Switzerland&start={page*10}. Selectors div.job_seen_beacon, div.jobsearch-ResultsList div.result. Note: robots.txt blocks the path; code logs and proceeds.


B2 — Search-driven general boards

family crate notes
adzuna jobcache-adapter-adzuna https://www.adzuna.co.uk/search?q={query}&w=switzerland
reed jobcache-adapter-reed https://www.reed.co.uk/jobs/{slug}-jobs-in-switzerland
jobwatch jobcache-adapter-jobwatch https://www.jobwatch.ch/offres/recherche?3112_page={page}
stellenanzeiger jobcache-adapter-stellenanzeiger German-language board
remoteok jobcache-adapter-remoteok https://remoteok.com/api flat JSON
hackernews jobcache-adapter-hackernews Algolia API → "Who is Hiring?" thread comments parsed for Switzerland keyword

C — Long tail: per-tenant custom HTML, RSS feeds, employer-bespoke

custom-html — crates/jobcache-adapter-custom-html/

Transport: HTTP+BS4 with per-tenant selector config.

Config shape: { id, name, list_url, job_link_selector, title_selector?, location_selector?, link_prefix?, swiss_filter, default_location }.

Sources (7):

name list_url job_link_selector default_location
ETH Zurich https://jobs.ethz.ch/site/index a[href*='/job/view/'] Zurich, Switzerland
Helvetia https://jobs.helvetia.com/?lang=en&filter_20=1189523 a[href*='job-vacancies'] Switzerland
PSI https://www.psi.ch/en/hr/job-opportunities a[href*='/stellenangebote/'] Villigen, Switzerland
Hydromea https://www.hydromea.com/jobs a[href*='/jobs/']:not([href$='/jobs']) Renens, Switzerland
Page Executive https://www.pageexecutive.com/jobs/switzerland a[href*='/job-detail/'] Switzerland
Helbling https://helbling.ch/en/career/jobs a[href*='/en/career/jobs/']:not([href$='/jobs']) Zurich, Switzerland
H55 https://h55.applytojob.com/apply/ a[href*='h55.applytojob.com/apply/']:not([href$='/apply/']) Sion, Switzerland

playwright-html — crates/jobcache-adapter-playwright-html/

Transport: JS-rendered + per-tenant selector config.

Sources (4):

name url link_selector default_location
Sensirion https://sensirion.com/career (long selector — see sources.yaml) Stäfa, Switzerland
CordenPharma https://career.cordenpharma.com/de/p/liestal/jobs a[href*='/p/liestal/jobs/'] Liestal, Switzerland
Goldman Sachs https://higher.gs.com/results?LOCATION=Zurich&page=1 a[href*='/roles/'] Zurich, Switzerland
Viseca https://jobs.viseca.ch/stellenangebote.html (long selector) Zurich, Switzerland

rss-feed — crates/jobcache-adapter-rss-feed/

Transport: HTTP+RSS.

Sources (4):

name rss_url swiss_filter default_location
Climeworks https://climeworks.com/feed/jobs false Zurich, Switzerland
Deloitte Switzerland https://apply.deloitte.ch/CHCareers/SearchJobs/feed/?jobRecordsPerPage=200 false Switzerland
myScience https://www.myscience.ch/en/jobs/rss false Switzerland
Leonteq https://careers.leonteq.com/rss.php true Zurich, Switzerland

employer-bespoke — crates/jobcache-employer-*/

37+ one-off employer integrations from Jobich's scrapers.py. Each is a single direct Adapter crate; they don't fit a family pattern. Priority-rank case-by-case (high-volume employers first).

The list: CERN, Glencore, CBRE, Cargill, Axpo, Alpiq, BKW, SBB, United Nations, BCGE, Bank Cler, SYZ Group, Citi, Crédit Agricole, GAVI, Startup.ch, Migros, Securitas, EuroPharmajobs, HotelCareer, HotelJob, ReliefWeb, iPersonal, Sika, Amazon (amazon.jobs), Bitcoin Suisse, EPFL, join.com (deferred — duplicates joincom family), Medienjobs, SozJobs, Startup Ticker, Sygnum, UBS, WHO, WIPO, plus the recruitment-agency cohort: Hays, Randstad, Adecco, Manpower, Experis, Robert Half, Morgan Philips, RM Group, Huxley, Divisions, SwissLinx, JobWinner, JobScout24, TieTalent, WeWorkRemotely, WTTJ, Glassdoor, Alpha.ch, SwissDevJobs, IctCareer, ItJobs.ch, ICTJobs.ch, WeAreDevelopers, DevJobsScanner, Nexus, DarwinRecruitment, CoopersGroup, MichaelPage.

Defer to Tier-C slices unless a specific employer rises to user-priority.


Cross-cutting catalogs

Queries

Used by every search-driven Adapter (LinkedIn, jobs.ch, jobup.ch, Indeed, Xing, jobwatch, Stellenanzeiger, Adzuna, Reed). Lives in crates/jobcache-adapter-queries/ or a shared crates/jobcache helper module.

English (~36): software engineer, developer, data scientist, devops, cybersecurity, nurse, doctor, pharma, medical, accountant, banking, financial analyst, consultant, HR manager, legal, mechanical engineer, electrical engineer, production, architect, construction manager, energy engineer, logistics, driver, warehouse, sales manager, retail, marketing, graphic designer, UX designer, social worker, teacher, government, hotel, chef, tourism.

German (~22): Entwickler, Ingenieur, Krankenschwester, Arzt, Buchhalter, Verkäufer, Mechaniker, Elektriker, Architekt, Koch, Fahrer, Lagerarbeiter, Lehrer, Sachbearbeiter, Polymechaniker, Kaufmann, Informatiker, Monteur, Techniker, Pflege, Sozialarbeiter, Sozialpädagoge.

French + Italian: add as we identify Swiss-Romand and Ticino board needs.

Swiss locations (whitelist + canton codes)

Cantons: AG, AI, AR, BE, BL, BS, FR, GE, GL, GR, JU, LU, NE, NW, OW, SG, SH, SO, SZ, TG, TI, UR, VD, VS, ZG, ZH.

Region terms (also Swiss): Deutschschweiz, Westschweiz, Suisse Romande, Tessin, Zentralschweiz, Ostschweiz, Region Mittelland, Region Limmattal, Seeland, Raum Bodensee, Romandy.

City whitelist: derived from canton-resolving lookup table; ~150 entries for major Swiss cities, municipalities, postal-code prefixes. Build from Jobich's enrich.py lookup table (separate slice — Codex if it touches shared cell writes, me if it stays inside adapters as a normalization helper).

Non-Swiss city blacklist

~200 entries covering DE/AT/FR/UK/US/IN/CN/JP/etc. major cities + country names. Used by search-driven Adapters to filter out international results that leak through "Switzerland" location filters.

(See config.py:NON_SWISS_KEYWORDS for the full list — port to crates/jobcache/src/swiss_filter.rs or its own helper crate. ~200 strings, alphabetic comparison.)

URL And Language Identity

Defined in lib/jobcache/src/ids.ts and its Rust mirror crates/jobcache/src/url_id.rs. Frozen contract:

  • ad_id = adIdFromUrlLanguage(url, language).
  • role_id = roleIdFromUrl(url).

The same canonical URL in different languages creates multiple ads under one URL-family role. Unknown language is the explicit und bucket, not permission to collapse language identity away. The TypeScript and Rust implementations must be byte-equivalent and tested against shared golden vectors.

Jobich's db.py:_normalize_url (strip query, strip fragment, https, trailing slash) is weaker than ours (we also strip tracking params, sort query keys, lowercase host, decode unreserved percent-escapes). Their behavior is a strict subset of ours; same URL plus language identity will not necessarily produce the same ad_id across the two systems. Acceptable: we own ad_id, they don't.

Defunct sources

Marked in each family section with the migration target. Useful for quarterly re-audit ("did Company X move to a scrapable surface yet?"). Full list lives inline in each family's table notes.


Slice ordering recap

End-state target: ~33 family crates + ~37 employer-bespoke crates = ~70 Rust crates total. Per rust-wasm-adapter-architecture slice order:

  • Slice 0 (Codex-led): infrastructure + jobs-ch port. Done when WIT contract + jobcache Rust crate + wasm-host.ts shim + build pipeline land.
  • Slice 1: Yousty Rust port for the three current Yousty-family sources.
  • Slice 2: jobup-ch Rust port.
  • Slice 3: Greenhouse multi-tenant (17 sources unlocked).
  • Slice 4: SmartRecruiters + Lever bundle (~24 sources).
  • Slice 5: Workday (~27 sources).
  • Slice 6: LinkedIn (B1 flagship; runtime prerequisite: method-routed browser-capable Env).
  • Slices 7-N: A2/A3 families in volume order (Phenom, SF families, Oracle HCM, Personio, Ashby, Hibob, Workable, Recruitee, RSS, Refline, Prospective, Persoware, Softgarden, Umantis, Teamtailor, joincom, jibe, icims, csod, eightfold, sf-rmk).
  • Slices N+1: B1 hostile remainder (Xing, Indeed) + B2 search-driven (Adzuna, Reed, jobwatch, Stellenanzeiger, RemoteOK, HackerNews).
  • Slices N+2: C tier — custom-html, playwright-html, rss-feed, employer-bespoke by priority.

Total slice count rough order: ~25 family slices + bursts of employer-bespoke slices late.

Where I will need Codex help during the rollout

  • Slice 0 infrastructure (per scoping doc — all eight open decisions).
  • Whenever an adapter requires a new FieldKey, EvidenceRefKind, or Method value (contract change, STOP-and-ask).
  • Whenever the runtime needs to inject a richer Env (Router-backed fetch, presigned object-store upload hook for object_refs, model-pack injection for embed-text task class). Most A3+B1+B2 adapters need at least the Router-backed fetch.
  • Per-source pause/kill flag at binding level (today the env-var pattern handles this; binding-level state belongs to Codex).
Source: jobcache/wiki/content/architecture/source-coverage-catalog.md