Careerjet Scraper [From $3.5💰] | Jobs | 23 Portals | Salary
Pricing
from $3.30 / 1,000 results
Careerjet Scraper [From $3.5💰] | Jobs | 23 Portals | Salary
Scrape job postings from Careerjet's 23 country portals for job posting aggregation, hiring trend analysis and job board backfill. Structured JSON with parsed salary fields via the Apify API.
Pricing
from $3.30 / 1,000 results
Rating
0.0
(0)
Developer
Ahmed Jasarevic
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Careerjet Scraper
Scrape job postings from Careerjet's 23 verified country portals — France, Spain, Italy, the Netherlands, Poland, Austria, Portugal, Sweden, Switzerland, Czechia, Japan, Brazil, Norway, Finland, Hungary, Slovakia, Romania, Ukraine, Russia, South Korea, Türkiye, Israel and Singapore — in a single run, with no official API account required. One pay-per-event run returns job title, company, location, parsed salary, posting date, full description and the outbound apply URL for every ad it finds.
Built for recruiters, job-board operators, labour-market analysts and hiring-signal lead-gen teams
that need cross-country vacancy data as a job listings data feed. Careerjet is an aggregator —
it never hosts the vacancy, so each row carries the employer (company, companyUrl) and the
origin site (publisherName / publisherUrl) it indexed the ad from.
Main use cases
- Job posting aggregation — collect openings across many countries in one run instead of merging per-board exports by hand.
- Job market research and hiring trend analysis — snapshot the same query on a schedule to see which roles, employers and locations are growing.
- Job board backfill and job feed syndication — populate a niche job board with structured listings that link out to the employer's own application page.
- Salary benchmarking and recruitment data — parsed
salaryMin/salaryMax/salaryCurrency/salaryPeriodfields turn raw ads into comparable pay ranges. - Competitor hiring tracking and job board monitoring — watch a company's or sector's live vacancies as a leading demand signal.
How it works
The Actor is a plain CheerioCrawler — server-rendered HTML over HTTP, no browser at all.
Careerjet's markup is classic server-rendered PHP (no __NEXT_DATA__, no client-side search
index), so every job card can be parsed from the raw response. A run works in five steps:
- A 12-second probe of the first listing URL logs HTTP status, byte count and whether the answer is the Cloudflare Turnstile challenge — the first log lines say why the run will return rows or 0 rows.
- Listing pass parses every served page into cards.
- Listing recovery rotates challenged listing pages through up to three fresh exit IPs.
- Detail pass, probed first fetches one
/jobad/<id>per cookie-less host to test whether the host is gated; gated hosts have their remaining ads skipped instead of fetched. - Bounded detail retry re-fetches at most 10 challenged detail pages once from a fresh IP.
Careerjet gates every content page behind a Cloudflare Turnstile interstitial served from
/bi-verifycc. This is an IP-reputation gate, not a JavaScript-rendering gate — a datacenter
IP is always challenged, a residential one is usually served — which is why the whole Actor stays on
plain HTTP with the RESIDENTIAL proxy group pre-enabled.
Scrape job listings from Careerjet's 23 verified country portals
Careerjet's gate is per portal, not per IP. Some domains serve their listing page to any residential exit IP; others answer every request — from any exit IP, and however many different ones are tried — with the interstitial. Verified over plain HTTP on the RESIDENTIAL group on 2026-10-07:
| Listing page served (23) | Listing page always gated (13) |
|---|---|
.fr .es .it .nl .pl .at .pt .se .ch .cz .jp .com.br .no .fi .hu .sk .ro .ua .ru .co.kr .com.tr .co.il .com.sg | .com (US) .co.uk .de .ie .com.au .co.in .be .dk .co.za .sg .co.nz .ae .ly |
The gated portals are what the input dropdown omits: they answer every request with the interstitial and can only ever return 0 rows here, so the Actor never claims them.
Why rows = portals × 5. Careerjet serves a 5-card, pager-less listing page to a guest
session and ignores page=N (measured on 16 portals; ?page=2..4 returns page 1's ads, which the
row buffer deduplicates). A run therefore scales by portals, never by pages, and maxItems
is the only row-shaped input: with countryDomains empty the Actor plans ceil(maxItems / 5)
verified portals (30 → 6, 100 → 20 of the 23 available). An explicit portal list is the starting
point — when it cannot hold maxItems the Actor tops it up with more verified portals and logs
which ones it added. The practical ceiling is 115 rows (the most the 23 verified portals hold).
Measured run times (Actor run only — Apify adds container start-up on top):
| Run | Result |
|---|---|
.fr, 5 rows, details on | 10 s — 5 rows card-only; details tried twice (fresh exit IP on the 2nd try) |
.es, 5 rows, details on | 8 s — 5 rows card-only |
| 6 open portals in one run, listing only | 25 s — 20 rows; 2 gated pages rotated 3× then reported |
| 5 default portals, details on | 9 s — 25 rows card-only; 1 probe/host, 20 ads skipped, 5 challenges (was 35) |
.com (US) alone, details on | 8 s — 0 rows, 4 exit IPs challenged, explicit error instead of silence |
| 14 remaining portals, one bulk probe | 26 s — 11 served (55 rows), .co.nz .ae .ly challenged from 3 exit IPs each |
Job posting aggregation across country portals in a single run
Careerjet renders every job card server-side in plain HTML, with no client-side templating. The
same card markup appears on search-results pages and in Careerjet's own "Latest jobs"
section.jobs-contextual block, so the parser was validated offline against real, live
server-rendered HTML rather than assumed:
<section class="jobs-contextual standard"><ul class="jobs"><li><article class="job clicky" data-url="/jobad/us222530badcca995d5d84006ae36d046c"><header><h3><a href="/jobad/us222530badcca995d5d84006ae36d046c">…</a></h3></header><p class="company">UniFirst</p><ul class="location"><li><svg class="icon" /> Tacoma, WA</li></ul><ul class="salary"><li><svg class="icon" /> $31.25-33.65 per hour</li></ul><div class="desc">Description: …</div><footer><ul><li><span class="badge">Just now</span></li></ul></footer></article></li></ul></section>
Detail-page selectors come from Careerjet's own published stylesheet
static.careerjet.org/css/all.min.css, which declares the layout explicitly:
#job>.container>header .job-title /* title */#job>.container>header .company a /* company */#job>.container>header .details>li /* location, date, contract type, salary */#job>.container>.content /* description body */#job .actions a /* apply button */
Every selector has a looser fallback, so a partial markup change degrades a field instead of breaking the run.
Exact endpoints used
| Purpose | URL pattern |
|---|---|
| Search (most portals) | https://{portal}/jobs?s={keywords}&l={location}&sort={sort}&page={n} |
| Search (UK) | https://www.careerjet.co.uk/search/jobs?s=…&l=… |
| Search (DE) | https://www.careerjet.de/suchen/stellenangebote?s=…&l=… |
| Job detail | https://{portal}/jobad/{2-letter country}{32 hex chars} |
The s / l parameter names are not guesswork — they come from Careerjet's own
<form id="search-main" method="get" action="/jobs">, whose inputs are name="s" (What) and
name="l" (Where). Localized search routes are stored per portal in src/countries.ts; if a
portal's primary route renders zero cards the Actor retries once on that portal's next known route.
Proxy is effectively required
Careerjet puts every content page behind a Cloudflare Turnstile interstitial served from
/bi-verifycc, confirmed on all 29 country domains tested plus a Google-Cloud-hosted reader
proxy — i.e. every datacenter IP is challenged, regardless of User-Agent, headers or HTTP
version.
| Attempt | Result |
|---|---|
| Cheerio, direct (no proxy) | blocked |
Cheerio + Apify Proxy RESIDENTIAL, apifyProxyCountry: GB | listing served; job-ad pages still challenged |
| Spoofed User-Agent (Googlebot / bingbot / empty) | blocked — the WAF judges the IP, not the UA |
TLS-impersonating client (curl_cffi) | blocked — Chrome's TLS + HTTP/2 fingerprint is not enough |
Apify UNBLOCKER proxy group | 502 (no such group for this account) |
Careerjet RSS / sitemap / /jobview/ / /clk/ / /apply/ | 404 — legacy surfaces are gone |
| Playwright headless Chrome + RESIDENTIAL | blocked — waited the full 45 s |
| Playwright headed under Xvfb + RESIDENTIAL | cleared on a fresh exit IP, but only after up to ~60 s/page |
A headed browser did work — and that is exactly what this build drops. It costs 40–90 s per page and
the 2 GB Playwright image, and the only thing it ever buys beyond plain HTTP is the full
description. Enable the proxy input and pick the RESIDENTIAL group, ideally with
apifyProxyCountry set to the portal's country (FR for .fr, JP for .jp). The Actor pins the
run to one sticky exit IP because a cached clearance cookie only passes from the address it was
earned on, and switches to a fresh session whenever a pass comes back challenged. If every
listing page was challenged it exits with an explicit error — it never silently returns an empty
dataset.
Output Fields: structured job data across all 23 country portals
One row per job posting; exactly one row per ad id — listing data is buffered and the
/jobad/<id> detail fetch overwrites it, so a row is never duplicated or half-empty. The Output tab
is driven by .actor/output_schema.json and renders the same dataset through two views:
Overview (one line per job) and Details (every extracted field).
| Field | Notes |
|---|---|
adId | Ad id: 2-letter country code + 32 hex chars, e.g. us222530badcca995d5d84006ae36d046c. Unique across portals — the row key. |
countryCode | The 2-letter prefix parsed out of adId. |
domain | Careerjet country portal the row was scraped from. |
url | Canonical /jobad/<id> page. |
title | Job title as rendered by Careerjet. |
company, companyUrl | Hiring company + employer profile page when the card links one (else null). |
location, locations[] | Raw location text plus the comma/slash split parts for multi-site ads. |
salary | Salary exactly as Careerjet renders it (may be null — many ads publish none). |
salaryMin, salaryMax | Lower/upper bounds parsed out of salary. |
salaryCurrency, salaryPeriod | Currency symbol/code detected; pay period (hour/week/month/year). |
datePostedLabel, datePosted | Relative label as shown, plus a best-effort ISO-8601 date. |
description, descriptionHtml | Full posting body (plain text + HTML) — detail fetches only. |
listingExcerpt | Teaser text from the search-results card. |
employmentType, badges | Contract / working-hours chips and all footer chips (incl. posting date). |
applyUrl | The portal's apply link (detail fetches only). |
publisherName, publisherUrl | Origin site Careerjet indexed the ad from, derived from applyUrl. |
query, queryLocation | Keywords and location the search that surfaced this ad was run with. |
source, scrapedAt | Emitted by careerjet; ISO-8601 parse timestamp. |
The salary/date parsers were validated offline against a real, unchallenged listing fragment: 5/5
cards parsed with 0 missing adId/title/company/location/url, and
$18.50-19.25 per hour → 18.5/19.25/USD-hour, £45,000 – 55,000 per year →
45000/55000/GBP-year, €2.500 per maand → 2500/EUR-month.
Input Parameters
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
startUrls | array | No | [] | Explicit Careerjet search URLs. When filled, keywords, location and countryDomains are ignored. |
keywords | string | No | software engineer | Job title, keywords or company, sent as the s search parameter. |
location | string | No | "" | City or region, sent as the l parameter. Empty = country-wide search. |
countryDomains | string[] | No | [] | Multiselect of the 23 verified portals. Empty = the Actor auto-selects ceil(maxItems / 5) portals and tops up a short selection. |
sort | enum | No | relevance | relevance | date | salary. |
maxItems | integer | No | 100 | The row target (1–2000). The Actor plans ceil(maxItems / 5) portals and stops at the target. |
includeJobDetails | boolean | No | true | Fetch each /jobad/<id> over HTTP for the full description + apply URL. Gated pages ship card-only. |
maxRequestsPerCrawl | integer | No | 400 | Safety cap on total HTTP requests (1–5000). |
proxy | object | No | RESIDENTIAL, Apify Proxy on | Pre-enabled. RESIDENTIAL is effectively required; the run pins one sticky session. |
Free Apify accounts are automatically capped at 10 jobs per run.
Example Input And Output
Row target only (recommended) — portals are derived from maxItems:
{"keywords": "software engineer","location": "","sort": "date","maxItems": 100}
Plans ceil(100 / 5) = 20 verified portals, one request each, and stops at 100 rows.
Pinned to three portals:
{"keywords": "nurse","countryDomains": ["www.careerjet.fr", "www.careerjet.es", "www.careerjet.it"],"maxItems": 15}
Fast listing-only run (no detail fetches — ~1 request per portal):
{ "keywords": "nurse", "includeJobDetails": false, "maxItems": 50 }
Example output row:
{"adId": "fr222530badcca995d5d84006ae36d046c","countryCode": "fr","domain": "www.careerjet.fr","url": "https://www.careerjet.fr/jobad/fr222530badcca995d5d84006ae36d046c","title": "Ingénieur logiciel senior","company": "Acme SAS","companyUrl": "https://www.careerjet.fr/company/acme","location": "Paris, Île-de-France","locations": ["Paris", "Île-de-France"],"salary": "45 000 € - 55 000 € par an","salaryMin": 45000,"salaryMax": 55000,"salaryCurrency": "€","salaryPeriod": "year","datePostedLabel": "il y a 2 jours","datePosted": "2026-10-03T00:00:00.000Z","description": "Nous recrutons un ingénieur logiciel senior pour …","listingExcerpt": "Nous recrutons un ingénieur logiciel senior …","employmentType": ["Temps plein", "CDI"],"badges": ["il y a 2 jours", "Temps plein"],"applyUrl": "https://careers.acme.example/jobs/1234","publisherName": "careers.acme.example","publisherUrl": "https://careers.acme.example","query": "software engineer","queryLocation": "","source": "careerjet","scrapedAt": "2026-10-05T12:00:00.000Z"}
Careerjet API vs this scraper
Careerjet does run an official API, but it is not open: v4
(search.api.careerjet.net/v4/query) requires a publisher account, an API key via HTTP Basic auth,
and a mandatory IP whitelist. It caps depth at 10 pages × 100 results and omits the origin site.
The legacy affiliate API (public.api.careerjet.net/search) is dead, internal hosts
(api.careerjet.net) return Unrecognized request URL on every route, and
ac.careerjet.net is only the type-ahead autocompleter, not job data.
This Actor is the no-account route to the same listings: it reads what a guest browser sees and
returns 28 structured fields the partner API does not, including publisherName / publisherUrl.
If you already hold a Careerjet partner account, prefer the official v4 API for supported access —
and use this Actor when you need cross-portal coverage without signing up.
Integrations & Automation
- Apify API / Dataset API — every run's rows are available as JSON via
/v2/datasets/{datasetId}/items?format=json&clean=true. - Webhooks — trigger downstream jobs on run completion (e.g. push new rows to your DB or ATS).
- Zapier / Make — connect the dataset to hundreds of apps without writing code.
- Schedules — recommend a daily run per target query for job market monitoring, or weekly for slower-moving hiring-trend snapshots. Recurring runs build the time series that makes trend analysis possible, and they improve the Actor's Store recommendation signals.
Related Actors
- careerjet-job-scraper
- careerjet-mcp-server
- careerjet-job-search-api
- job-posting-aggregator
- ats-job-aggregator
FAQ
Is there an official Careerjet API?
Yes — Careerjet publishes a v4 partner API at search.api.careerjet.net/v4/query, but it requires a
publisher account, an API key with HTTP Basic auth (empty password) and a whitelisted IP, and it
caps results at 10 pages × 100. The legacy affiliate API is dead. This Actor needs no account.
What are the alternatives to this Actor?
Apify Store carries several Careerjet actors (see Related Actors) plus broader job-posting aggregators. Commercial job-data vendors such as TheirStack, JobsPikr, Jobo Data and jobdataapi publish job feeds too. The open-source JobSpy library is a common alternative for multi-board scraping in Python. This Actor's differentiator is no-account access to 23 verified Careerjet portals with parsed salary fields and the outbound apply URL.
How do I aggregate job listings from multiple job boards?
Run the Actor once across as many Careerjet portals as you need — set keywords and let
maxItems drive portal selection, or pin countryDomains explicitly. Every row lands in one
dataset keyed by adId, so there is no per-board export to merge by hand.
Is there a job listings API?
The Actor's own Dataset API is one: GET /v2/datasets/{datasetId}/items?format=json&clean=true
returns the structured rows as JSON. Combined with the pay-per-event pricing (result $0.0035),
you get a job-listings API without a partner agreement.
How do I get salary data for benchmarking from job postings?
Each row carries salaryMin, salaryMax, salaryCurrency and salaryPeriod, parsed from the raw
salary string — for example £45,000 – 55,000 per year becomes 45000/55000/GBP/year. Ads with no
published salary have salary: null and null bounds, so filter those out before averaging.
Is it legal to scrape job postings from Indeed or LinkedIn?
Legality depends on the source's Terms of Service and your jurisdiction. This Actor does not scrape
Indeed or LinkedIn — it scrapes Careerjet, whose robots.txt disallows the paging/legacy routes but
not the /jobad/ detail path this Actor reads. Review those terms and your own compliance
requirements before running at scale.
Which countries does Careerjet cover in this Actor?
23 portals are verified to return rows: France, Spain, Italy, the Netherlands, Poland, Austria, Portugal, Sweden, Switzerland, Czechia, Japan, Brazil, Norway, Finland, Hungary, Slovakia, Romania, Ukraine, Russia, South Korea, Türkiye, Israel and Singapore. Careerjet's US, UK, Germany, Ireland, Australia, India and several other portals are always gated and are omitted from the input dropdown.
What is the difference between a job board and a job aggregator?
A job board hosts vacancies on its own domain; Careerjet is an aggregator — it indexes ads from
employers and other sites and links out to apply. That is why every row here carries
publisherName / publisherUrl (the origin site) and applyUrl (the outbound apply link).
SEO Keywords
careerjet api, careerjet scraper, job aggregator api, job listings data feed, job postings api, scrape job listings, job posting aggregation, job market research, hiring trend analysis, track competitor hiring, job board monitoring, job feed syndication, recruitment data, salary benchmarking, job board backfill, indeed api, linkedin jobs scraper, jobspy, job search engines, indeed alternative, ats job feed, structured job data, job data api, european job market data
For AI Agents & LLM Apps
Purpose: return structured Careerjet job postings (title, company, location, parsed salary, dates, description, apply URL, publisher) from one or more of 23 verified country portals.
Minimal working input:
{ "keywords": "software engineer", "maxItems": 30 }
Variant — pinned portals with full details:
{"keywords": "nurse","countryDomains": ["www.careerjet.fr", "www.careerjet.es"],"includeJobDetails": true,"maxItems": 10}
Output fields: adId, countryCode, domain, url, title, company, companyUrl,
location, locations, salary, salaryMin, salaryMax, salaryCurrency, salaryPeriod,
datePostedLabel, datePosted, description, descriptionHtml, listingExcerpt,
employmentType, badges, applyUrl, publisherName, publisherUrl, query, queryLocation,
source, scrapedAt.
Behaviors an agent should know:
- If
startUrlsis set,keywords,locationandcountryDomainsare ignored. maxItemsis the only row-shaped input; leaves unset → default 100 rows (billed). Careerjet yields 5 rows per portal per search, so the Actor plansceil(maxItems / 5)portals — the practical ceiling is 115 rows across the 23 verified portals.- Careerjet's US/UK/DE/IE/AU/IN/be/dk/za/sg/nz/ae/ly portals are always gated and return 0
rows; they are omitted from
countryDomains. includeJobDetails: true(default) adds billed detail-page requests and enablesdescription,descriptionHtmlandapplyUrl. Gated job-ad pages ship card-only rows instead — the run names the affected hosts and does not fail.- The RESIDENTIAL proxy is effectively required; datacenter IPs are always challenged (0 rows). The run pins one sticky session and rotates only on a challenge.
Billing: pay-per-event — result $0.0035 per result (DIAMOND $0.0033) plus Actor Start
$0.00005 per run.
Legal & Compliance Disclaimer
This Actor is an independent tool and is not affiliated with, endorsed by, or sponsored by
Careerjet. It reads only publicly available Careerjet pages over HTTP (no login bypass, no
CAPTCHA solving, no browser) and does not access any authenticated or private surface. Users are
responsible for complying with Careerjet's Terms of Service, its robots.txt, and applicable
data-protection law (e.g. GDPR for EU/UK job data) when using the extracted postings. The dataset
contains employer names, job postings and outbound apply links; do not use it for unsolicited
commercial outreach in violation of applicable law.