Job Bank Canada Jobs Scraper avatar

Job Bank Canada Jobs Scraper

Pricing

from $0.84 / 1,000 results

Go to Apify Store
Job Bank Canada Jobs Scraper

Job Bank Canada Jobs Scraper

Canadian job postings from the Government of Canada's Job Bank: title, employer, city and province, salary with its unit, work arrangement and apply method. Includes LMIA status and employment-equity tags that commercial job boards do not carry.

Pricing

from $0.84 / 1,000 results

Rating

0.0

(0)

Developer

Ibnu Adzim

Ibnu Adzim

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Categories

Share

Job postings from Job Bank (jobbank.gc.ca), the Government of Canada's national employment service and the country's largest single source of vacancies — as structured rows: title, employer, city and province, salary with its unit, work arrangement and how to apply.

Public-sector data. No login, no API key, no browser.


What makes this dataset distinctive

LMIA status. A Labour Market Impact Assessment is what a Canadian employer must obtain before hiring a foreign worker, so "does this posting have one" is the single most consequential fact on the page for anyone applying from outside Canada. It is a first-class filter here (lmia: requested | approved), and no commercial job board publishes it.

Employment equity tags. Job Bank marks postings that explicitly welcome Indigenous people, newcomers to Canada, persons with disabilities, veterans, mature workers, youth and visible minorities. equityGroups filters on them.

Facet counts for the whole search. Every run's summary row carries facetCounts — the board's own tally behind each filter (67 entries on a measured search): jobs per province, per salary band, per work term, per equity group, per LMIA status. That is a free aggregation over the entire result set, not just the pages you fetched.


What you get

One SEARCH_SUMMARY row per search term, then one JOB row per posting.

GroupFields
IdentityjobId, jobNumber, jobUrl, title, employer, postedDate
LocationlocationRaw, city, province
PaysalaryRaw, salaryMin, salaryMax, salaryUnit
Nature of the roleworkArrangement, applyMethod, isNew, postedOnJobBank
Provenance_input, _source, _scrapedAt, pageFound, raw

Salary carries its period. Job Bank quotes pay hourly, weekly, bi-weekly, monthly and annually in a single free-text field, and appends commission language to it. Comparing an hourly figure to an annual one without reading salaryUnit is roughly a 2,000x error, so nothing is converted between periods and the unit sits beside the number.

salaryMax is populated only for a genuine range ("$20.00 to $25.00 hourly"). A trailing "+ 3% commission per sale" stays in salaryRaw and is never mistaken for an upper bound — an earlier build did exactly that and published a maximum salary of three dollars.

postedOnJobBank distinguishes postings the employer submitted here directly from ones syndicated in off external boards — useful when you want the primary source rather than an aggregator's copy.


Example input

{
"queries": ["software developer", "data analyst"],
"provinces": ["ON", "BC"],
"workTerm": "permanent",
"postedWithin": "30d",
"maxPages": 20
}

Looking for roles open to foreign applicants:

{
"queries": ["cook", "truck driver", "farm worker"],
"lmia": "approved",
"maxPages": 40
}

Limits and behaviours worth knowing

This Actor is deliberately slow, and that is the point. Job Bank's robots.txt disallows nothing at all but asks for Crawl-delay: 5. That request is honoured as a floor: five seconds between request starts, concurrency 1 by default, and setting minRequestInterval lower does not speed it up. A public service that hides nothing and asks politely gets read politely.

Work location and LMIA cannot be combined. Upstream they share a single parameter, so sending both would silently drop one. The Actor refuses the combination instead — run two searches.

"More than 30 days" means older, not within. That is Job Bank's own wording for the third postedWithin option, kept rather than reinterpreted.

A search matching nothing reports zero. The result count is read from its own element rather than inferred from the absence of cards, so an empty search comes back as upstreamTotalResults: 0 with stoppedReason: no_results — not as an error and not as a missing value.

Bad filter values are refused before any request rather than sent and guessed at.


How it works

HTTP-only. Job Bank is a server-rendered Jakarta Faces application with no JSON layer anywhere — no __NEXT_DATA__, no JSON-LD, no hydration blob — so the rows come from its HTML, which is unusually clean and semantically classed.

Two things about that HTML would quietly corrupt a dataset and are handled explicitly:

  • Every posting URL carries a session id. Links look like /jobsearch/jobposting/50209079;jsessionid=F2A25A…, where the path parameter is this crawl's server session. Left in, it would rot immediately and make the same job look like a new URL on every run. It is stripped.
  • Screen-reader-only labels sit inside the data elements. Job Bank is built on the Web Experience Toolkit, whose wb-inv class marks text meant only for assistive technology — and those spans live inside the very <li> elements holding the location and salary. Flattening tags naively yields a city of "Location Surrey". They are removed before any field is read.

No WAF was measured: six different TLS fingerprints all received identical pages.