Job Bank Scraper
Pricing
from $0.50 / 1,000 job postings
Pricing
from $0.50 / 1,000 job postings
Rating
0.0
(0)
Developer
Harpoon
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Job Bank Scraper — Canadian job postings, with every search filter
Pull live job postings from Job Bank, Canada's official job board, into a clean dataset you can filter, chart, or load into your own app. Search by keyword, or skip the keyword entirely and browse by filters alone — every facet the site offers is on the form: province, economic region, city, date posted, full/part time, term, shift, language, education, experience, salary band, remote/hybrid, government employers, LMIA status, apprenticeship level, internships, green jobs, and the intended-applicant programs.
Open each posting to collect the full description, requirements, tasks, benefits and application link — or switch that off for a fast, cheap run of listing rows.
What can it do?
- 🔎 Keyword search across one or many terms at once, each with its own result cap.
- 🗺️ Location filtering by province, economic region, or a specific city or postal code.
- 🧰 Filter-only browsing — leave the keyword empty to pull e.g. every remote job in British Columbia.
- 🌍 Work arrangement — on site, remote, hybrid, on the road, or relocation-heavy roles.
- 🎓 Requirements — education level, years of experience, and working language.
- 💰 Salary bands — annual and hourly facets, separately.
- 🏛️ Employer and source — federal / provincial / municipal government, verified postings vs. partner boards, and an option to drop staffing agencies.
- 🛂 Immigration filters — LMIA requested or approved (and their exclusions), plus every intended-applicant program including newcomers and international candidates.
- 📄 Full posting details — description, tasks, work conditions, benefits, vacancies, expiry date, who can apply, and the application link.
- 🔁 Resumable runs — an interrupted run continues where it stopped instead of starting over.
What data can I extract?
| Field | Example |
|---|---|
id | 50618828 |
url | https://www.jobbank.gc.ca/jobsearch/jobposting/50618828 |
title | welder-fitter |
employer | Hahn and Sons |
location.city / location.province | Burlington / ON |
location.postal_code | P9N 1L9 |
salary.text | $ 25.00 to $ 38.00 hourly (To be negotiated) |
salary.min / salary.max / salary.unit | 25 / 38 / hourly |
work_setting | On site, Remote, Hybrid |
employment_type | Permanent employment |
work_hours | Full time |
vacancies | 1 |
date_posted | 2026-10-30 |
expires_at | 2026-11-19 |
job_number | 3709116 |
source | Job Bank, indeed.com, Jobillico, CareerBeacon |
source_url | the partner's own posting link, when there is one |
is_new, direct_apply, lmia_requested | true / true / false |
description | full posting summary |
languages | English |
education | ["College/CEGEP", "or equivalent experience"] |
experience | 5 years or more |
tasks | ["Interpret welding process specifications", ...] |
work_settings | ["Dusty", "Hot", "Noisy"] |
work_conditions | ["Fast-paced environment", "Attention to detail", ...] |
benefits | ["Free parking available", "Team building opportunities"] |
who_can_apply | ["a Canadian citizen", "a permanent resident of Canada", ...] |
apply_url | where to actually apply |
search_query | which keyword produced this row |
Fields under location and salary are the only two nested objects; everything else is flat. When Fetch full job details is off, the detail-only fields come back empty or as empty arrays — the listing fields are always filled.
How to use it
- Open the Input tab. The form is prefilled with a working search, so you can press Start straight away.
- Set Search queries — one keyword per line. Or leave it empty to browse by filters alone.
- Set Max results per query. 100 is a good default; 25 is about one page.
- Optionally narrow with the filter sections: location, date, employment, requirements, salary, work arrangement, job type, and intended applicants.
- Leave Fetch full job details on for the complete record, or turn it off for a quick, inexpensive listing run.
- Press Start and watch the run log. Results stream into the Output tab as they are collected.
Input
Two things drive a run: what to search for and how to narrow it. That is the whole mental model.
search_queriesis the list of keywords. Each one is its own search with its own cap.- The filter fields apply to every keyword in the run.
- If
search_queriesis empty, the filters are the search — every matching job is returned.
{"search_queries": ["welder", "millwright"],"max_results": 200,"fetch_details": true,"sort": "date_posted","provinces": ["ON"],"regions": ["9219"],"date_posted": "last_7_days","employment_type": ["full_time"],"work_term": ["permanent"],"workplace": ["on_site"],"experience": ["3_years_or_more"],"salary_hourly": ["30_to_40"],"employment_groups": ["newcomers"]}
The Input tab documents every field, with the accepted values for each filter.
Output
Each job is one row in the dataset. This is a real row, with the long description trimmed:
{"id": "50618828","url": "https://www.jobbank.gc.ca/jobsearch/jobposting/50618828","title": "welder-fitter","employer": "Hahn and Sons","location": {"city": "Burlington","province": "ON","postal_code": "","country": "Canada"},"salary": {"text": "$ 25.00 to $ 38.00 hourly (To be negotiated)","min": 25,"max": 38,"currency": "CAD","unit": "hourly"},"work_setting": "On site","employment_type": "Permanent employment","work_hours": "Full time","vacancies": 1,"date_posted": "2026-10-30","expires_at": "2026-11-19","job_number": "3709116","source": "Job Bank","source_url": "","is_new": true,"direct_apply": true,"lmia_requested": false,"description": "Education: College/CEGEP. or equivalent experience. Work site environment: Dusty. Hot. Noisy. Tasks: Interpret welding process specifications...","languages": "English","education": ["College/CEGEP", "or equivalent experience"],"experience": "5 years or more","tasks": ["Interpret welding process specifications","Operate manual or semi-automatic, fully automated welding equipment","Read and interpret welding blueprints, drawings specifications, manuals and processes"],"work_settings": [],"work_conditions": ["Fast-paced environment","Attention to detail","Standing for extended periods"],"benefits": ["Free parking available", "Team building opportunities"],"who_can_apply": ["a Canadian citizen","a permanent resident of Canada","a temporary resident of Canada with a valid work permit"],"apply_url": "https://www.jobbank.gc.ca/jobsearch/jobposting/50618828","search_query": "welder"}
Rows land in the run's default dataset. Pick a view in the Output tab — Overview for the quick scan, Requirements for education and tasks, By search when you ran several keywords — and export as JSON, CSV, Excel or XML. Each run also writes a small report to the key-value store with the filters used, the total, and the per-keyword counts.
What can you do with the data?
- Build a job feed. Run daily with
sort: date_posted,date_posted: last_48_hoursand a saved task, then diff against yesterday's rows to publish only what is new. - Salary benchmarking. Pull a role across provinces, group by
location.provinceandsalary.unit, and compare advertised pay bands. - Hiring-market research. Count postings by
employer,employment_typeandwork_settingto see who is hiring and whether remote work is actually growing in a sector. - Job-board or aggregator. Feed the dataset into your own site:
title,employer,location,salary.textandapply_urlare enough to render a listing page. - Candidate tooling. Filter to
employment_groups: ["newcomers"]pluslmia: ["approved_lmia"]to surface roles open to applicants from abroad.
How much does it cost?
This Actor bills per result (pay-per-event):
job-posting— charged once per job posting added to the dataset. This is the base price of a row.job-detail— charged in addition for each row that carries the full posting details.
The rates are on this Actor's Pricing tab. In plain terms:
- A listing-only run of 500 jobs = 500 ×
job-posting. - A run of 500 jobs with details = 500 ×
job-posting+ 500 ×job-detail. - Turning Fetch full job details off is the cheapest way to run at scale; you give up the description, requirements, tasks, benefits and expiry date.
Nothing is charged for a run that finds no results, and nothing is charged for rows that were never saved.
FAQ
Do I need an account, cookies, or a login? No. Everything this Actor returns is publicly visible job-posting data. There is nothing to sign into and no credentials to supply.
Can I search more than one keyword in a single run?
Yes — put one keyword per line in Search queries. Each keyword gets its own max_results cap, and every row records which keyword found it in search_query.
Can I search without a keyword? Yes. Leave Search queries empty and the filters become the search — for example every permanent, full-time, remote job in Ontario. If you leave both the keyword and the filters empty you get a clear message instead of an empty run.
Why do some rows have empty description or tasks?
Two reasons. Either Fetch full job details is off, or the posting itself is a short one collected from a partner board — those often carry only a title, employer, salary and a link out. source tells you which board it came from.
Why is vacancies 0 on some rows?
The posting did not state a number of openings. Treat 0 as "not stated".
Why do salary.min / salary.max both read 0?
The posting states no figure, or only a note such as "to be negotiated". salary.text always shows exactly what the employer wrote, so trust it over the numbers.
How many results can I get?
max_results goes up to 10,000 per keyword, and a run can carry many keywords. Large runs are collected a page at a time, so deep result sets stay quick.
What happens on a free plan?
There is no result cap of our own: a free Apify account gets the same results as a paid one, up to whatever max_results you set. The only limits are Apify's own — your plan's monthly usage credit and its concurrent-run allowance.
A run was interrupted. Can I continue it?
Yes. Start the next run with the interrupted run's id as the resume_from_run_id input property and the keywords it already finished are skipped, so only the remainder is scraped. node scripts/resurrect.mjs <run-id> does exactly that for you. It is an API-only property with no form field, because it is run bookkeeping rather than a search setting — the Console's Resurrect button reuses the original input and starts over from the beginning.
Can I use it from the API or an AI agent?
Yes. It is a normal Actor: start it through the Apify API or MCP, then read the dataset. The .md version of this page is machine-readable, and the Input and Output tabs document every field.
It returned nothing. What now? Check the run log — it states the reason per keyword. The usual causes are a filter combination that matches nothing (try fewer filters) and a city that could not be matched, which the log reports while the run continues.
Notes and limitations
- Data comes from Canada's official job board. Coverage, wording and which filters exist are the site's, not this Actor's. If a filter returns nothing, that is a real result.
- Ordering by date and keeping the cap modest is the fastest way to run. Very deep result sets are slower per row than the first few pages.
salaryis as advertised. Many Canadian postings quote hourly pay, some quote annual, some quote "to be negotiated". Theunitfield tells you which, andsalary.textis always the employer's own wording.- Aggregated postings are thinner. Job Bank republishes postings from partner boards; those carry fewer detail fields and link out via
apply_url. expires_atis the advertised-until date, not a promise the posting is still live.- Job postings change and disappear. Ids are stable, so a re-run is the right way to refresh a row.
- The content belongs to the employers and the site. Use the data in line with their terms and your local rules on personal data; do not republish in a way that misrepresents the original posting.
Support
Found a bug or a field that looks wrong? Open an issue on this Actor's page with the run id and the posting URL — that is enough to reproduce it.
Built for the Apify platform. Runs on the Actor's own compute; no setup on your side.