Search Swiss jobs on jobs.ch.
Searches jobs.ch, Switzerland's largest job board, like its search page: job title (with the workload, e.g. 80–100 %), company, place, employment type, when it was first published, and the link to the ad. About 20 jobs per page with the total number of matches. jobs.ch's search is fuzzy: a term with no real match still returns loosely related jobs.
- Site
- jobs.ch, Switzerland
- Version
- v2 of 2
- Runs, last 7 days
- No reports yet
- sha256
- bd90902c26a3…
Run it with your agent
after installing WieldAsk in plain words. Your agent finds this recipe and calls it:
“Search Swiss jobs on jobs.ch.”
Read-only: it changes nothing on the site. No login needed.
{ "tool": "run", "arguments": { "recipe_id": "jobs.ch/search-jobs", "params": { "location": "Zürich", "term": "python" } } }
Requests
sent from your browser, in order- 1GET/de/stellenangebote/?location&page&termjobs.ch's search page (/de/stellenangebote/?term=…&location=…&page=…), server-rendered; its ld+json carries the page's 20 jobs as a schema.org ItemList of JobPosting.
Parameters
from your agentReturns
to your agentWhere it can send
- www.jobs.ch
Anything else is blocked by the extension.
Reads from your session
Nothing. Your browser attaches the site's cookies by itself.
Used only in requests to the addresses on the left. Never sent to wield.run or your agent.
Checks
a run passes when- length(output.jobs) >= `15`
- output.total > `40`
- length(output.jobs[?starts_with(url, 'https://www.jobs.ch/de/stellenangebote/detail/')]) == length(output.jobs)
- length(output.jobs[?posted_at != `null` && company != `null`]) == length(output.jobs)
- length(output.jobs[?contains(place || '', 'Zürich') || contains(place || '', 'Zurich')]) > `2`
Raw recipe
Served as canonical JSON from /api/v1/recipes/jobs.ch/search-jobs, so its sha256 matches the ETag and the index.
curl -s https://wield.run/api/v1/recipes/jobs.ch/search-jobs | shasum -a 256 # bd90902c26a3e1d446e4502f7e5b7df1ba6b67fa599696d7e4cb389ae1a9f167
{ "description": "Searches jobs.ch, Switzerland's largest job board, like its search page: job title (with the workload, e.g. 80–100 %), company, place, employment type, when it was first published, and the link to the ad. About 20 jobs per page with the total number of matches. jobs.ch's search is fuzzy: a term with no real match still returns loosely related jobs.", "effect": "read", "id": "jobs.ch/search-jobs", "keywords": [ "jobs.ch", "stellenangebote", "jobs", "stellen", "schweiz", "zürich", "bern", "basel", "teilzeit", "festanstellung", "emploi", "job search", "switzerland", "vacancies" ], "output": { "schema": { "properties": { "jobs": { "items": { "properties": { "company": { "type": [ "string", "null" ] }, "employment_type": { "description": "As jobs.ch shows it, e.g. Festanstellung, Temporär, Praktikum", "type": [ "string", "null" ] }, "place": { "type": [ "string", "null" ] }, "posted_at": { "description": "ISO 8601 with offset", "type": [ "string", "null" ] }, "title": { "type": "string" }, "url": { "type": "string" } }, "type": "object" }, "type": "array" }, "location": { "type": [ "string", "null" ] }, "page": { "type": "integer" }, "term": { "type": "string" }, "total": { "type": [ "integer", "null" ] } }, "required": [ "term", "jobs" ], "type": "object" }, "value": "let $t = steps.search.title || '' in {term: params.term, location: params.location || `null`, total: (let $w = split(replace(replace($t, '’', ''), '\\'', ''), ' ') in (to_number($w[1]) && to_number(join('', [$w[0], $w[1]]))) || to_number($w[0])) || `null`, page: params.page || `1`, jobs: map(&{title: title, company: company || `null`, place: place || `null`, employment_type: type || `null`, posted_at: posted || `null`, url: url}, steps.search.jobs || `[]`)}" }, "params": { "additionalProperties": false, "properties": { "location": { "description": "City, canton or postcode, e.g. 'Zürich', 'Bern', '8001'", "maxLength": 60, "minLength": 2, "type": "string" }, "page": { "default": 1, "maximum": 20, "minimum": 1, "type": "integer" }, "term": { "description": "Job title, skill or company, e.g. 'python', 'pflegefachfrau', 'buchhaltung'", "maxLength": 100, "minLength": 2, "type": "string" } }, "required": [ "term" ], "type": "object" }, "provenance": { "method": "browser-inspect", "notes": "The search page /de/stellenangebote/?term=…&location=…&page=… (the ld+json SearchAction's urlTemplate; paging links use page=) is server-rendered. Its single ld+json script holds WebSite, CollectionPage, BreadcrumbList and an ItemList of the page's 20 JobPosting entries (identifier, title, datePosted, employmentType, hiringOrganization.name, jobLocation.address, url). The total is the number that starts the page title ('121 Python Jobs in Zürich', '3 245 Verkauf Jobs' with a narrow no-break space as thousands separator, which text extraction turns into a plain space, so the first two tokens are joined when both are digits). Pages hold 20–22 jobs; all are ordinary paid ads (isPaid), none marked as promoted. A nonsense term still finds 112 fuzzy matches (totalHits in the page state). The page also carries window state (__INIT__), which needs strip_js_after (schema field pending); the ld+json is enough. The page embeds the AWS WAF captcha SDK URL in its config, but no challenge was served. v2 (2026-11-08 08:34): verify only (jobs.ch writes the place as 'Zürich', 'Zurich', 'Zürich Oerlikon' or a nearby town, so the place check accepts both spellings and needs 3 of 20, not 6); requests and output unchanged.", "recorded_at": "2026-11-07T22:26:00+01:00", "recorded_by": "C4" }, "requires_login": false, "schema": "https://wield.run/schema/recipe/v1.json", "site": { "country": "CH", "id": "jobs.ch", "language": "de", "name": "jobs.ch", "origin": "https://www.jobs.ch", "origins": [ "https://www.jobs.ch" ] }, "steps": [ { "description": "jobs.ch's search page (/de/stellenangebote/?term=…&location=…&page=…), server-rendered; its ld+json carries the page's 20 jobs as a schema.org ItemList of JobPosting.", "expect": { "content_type": "html", "status": [ 200 ] }, "extract": { "embedded_json": { "required": false, "selector": "script[type=\"application/ld+json\"]", "vars": { "jobs": "[?\"@type\" == 'ItemList'] | [0].itemListElement[].item.{id: identifier.value, title: title, company: hiringOrganization.name, place: jobLocation.address.addressLocality, country: jobLocation.address.addressCountry, type: employmentType, posted: datePosted, url: url}" } }, "html": { "title": { "selector": "title" } } }, "id": "search", "request": { "headers": { "Accept": "text/html" }, "method": "GET", "query": { "location": "{{params.location}}", "page": "{{params.page}}", "term": "{{params.term}}" }, "url": "/de/stellenangebote/" } } ], "task": "Search Swiss jobs on jobs.ch", "tos": { "notes": "robots.txt allows the search page (it disallows /api/, login and feature-flag URLs) and has no rules for Claude agents or AI content signals. One request per call; no applications, job alerts or bookmarks.", "reviewed": false }, "verify": { "checks": [ "length(output.jobs) >= `15`", "output.total > `40`", "length(output.jobs[?starts_with(url, 'https://www.jobs.ch/de/stellenangebote/detail/')]) == length(output.jobs)", "length(output.jobs[?posted_at != `null` && company != `null`]) == length(output.jobs)", "length(output.jobs[?contains(place || '', 'Zürich') || contains(place || '', 'Zurich')]) > `2`" ], "example_params": { "location": "Zürich", "term": "python" }, "max_ms": 4000 }, "version": 2 }