# Job Bank Canada LMIA Jobs Scraper & Employer Leads (`memo23/job-bank-canada-lmia-scraper`) Actor

Scrape LMIA jobs from Canada's Job Bank: employers who asked to hire foreign workers, with the employer's apply email, NOC code, wage, hours and vacancies. Filter by city, province, NOC or date. Schedule it to get only new, updated and expired LMIA jobs. Export JSON or CSV.

- **URL**: https://apify.com/memo23/job-bank-canada-lmia-scraper.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.99 / 1,000 lmia jobs

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Job Bank Canada LMIA Jobs Scraper & Employer Leads

Get every **LMIA job** on Canada's Job Bank (`jobbank.gc.ca`): postings from employers who applied for, or were granted, a Labour Market Impact Assessment to hire a temporary foreign worker. Each row includes the **employer's application email as published on Job Bank**, the NOC 2021 code, wage, hours, vacancies, the employer's address and the LMIA status. Schedule it and it returns only the LMIA jobs that are new, changed or closed since the last run.

Built for immigration consultants and RCICs, foreign-worker recruitment agencies, settlement organisations and anyone who needs a current list of Canadian employers using the Temporary Foreign Worker Program.

### Why use this scraper

- **LMIA only, by default.** No setup: the default input keeps postings with *LMIA requested* or *Approved LMIA* and drops the rest. Every row says which (`lmiaStatus`).
- **Employer contact included.** Job Bank hides the application email behind a "Show how to apply" button. The actor reads that block for you: `applyEmail`, mail and in-person addresses, apply links, the job reference number, required documents and screening questions. No extra charge.
- **Search by what you place.** Keyword, city (resolved through Job Bank's location service so it stays local), province, postal code or NOC 2021 code. Batch several searches in one run.
- **Change-only monitoring.** Run it daily and get only new, updated (with `changedFields`) and expired LMIA jobs. Unchanged jobs are not saved and not charged.
- **Numbers, not text.** Wage `min` / `max` / `unit`, hours per week, vacancies, plus Job Bank's median wage for the occupation and region, so you can compare the offer with the prevailing wage.

![How the Job Bank LMIA Jobs Scraper works](https://muhamed-didovic.github.io/assets/how-it-works-job-bank-lmia.png)

### What LMIA status means

- **LMIA requested:** the employer could not find a Canadian worker and applied for an LMIA. The application is under review.
- **Approved LMIA:** the LMIA was granted for this position.

Both are public flags that Job Bank shows on the posting. They are the strongest public signal that an employer is hiring through the Temporary Foreign Worker Program.

### Supported inputs

| Input | What it does |
|---|---|
| `searchTerms` | Keywords, e.g. `cook`, `truck driver`, `welder`. Empty = every LMIA job in the location. |
| `location` | City, province or postal code: `Calgary`, `Surrey, BC`, `Ontario`, `T2P`. Empty = all of Canada. |
| `searches` | A batch of keyword + location searches, merged and deduplicated. |
| `startUrls` | Job Bank search or `/jobposting/{id}` URLs. |
| `lmia` | *Requested or approved* (default), *Requested*, *Approved*, or *Any* to turn the filter off. |
| `provinces`, `nocCodes` | Provinces or territories; five-digit NOC 2021 codes (`63200` cooks, `73300` transport truck drivers, `65201` food counter attendants). |
| `datePosted` | Any time, last 48 hours, last 30 days. |
| `employmentSchedules`, `employmentTerms`, `workplaceTypes`, `languages` | Full / part time; permanent, contract, seasonal, casual; on site, remote, hybrid, on the road; English, French, both. |
| `annualSalaryRanges`, `hourlySalaryRanges` | Job Bank's salary bands. |
| `sourceType`, `excludePlacementAgencies`, `internationalCandidatesOnly`, `excludeStudentJobs`, `excludeApprenticeJobs` | Job Bank's other facets. |
| `includeApplyInfo` | On by default: reads the "How to apply" block. |
| `enrichEmails` | Opt-in: for jobs without an application email, look for one on the employer's website (billed per email found). |
| `monitoringMode`, `stateName`, `resetState` | Change-only output for schedules. |
| `maxItems` | Cap on rows saved and billed. |

### Use cases

- **Immigration consulting.** A weekly list of employers with open LMIA positions in your clients' occupations, with the email to send a resume to.
- **Foreign-worker recruitment.** Find employers already approved to hire from abroad, by NOC code and province.
- **Compliance and research.** Track which occupations and regions rely on LMIA hiring, and compare posted wages with the median wage.
- **Job alerts.** Schedule a monitoring run per NOC code or city and forward only new postings.

### Monitoring scheduled runs

Set `monitoringMode` to **New, updated and expired only**, choose a `stateName` such as `bc-cooks`, and schedule the actor. The first run is the baseline (every job is `"new"`). Later runs return new jobs, jobs whose content changed (`"updated"`, with `changedFields` such as `["salary", "vacancies"]`), and jobs no longer listed (`"expired"`). Expiry is only decided when every search ran to its last page. State lives in a key-value store in your account (`jobbank-monitor-<stateName>`); `resetState` starts over.

### Output sample

A real LMIA row (lists shortened, email replaced):

```json
{
  "jobId": "50437444",
  "url": "https://www.jobbank.gc.ca/jobsearch/jobposting/50437444",
  "title": "cook",
  "employer": "Harveys/Swiss Chalet",
  "lmiaStatus": "requested",
  "nocCode": "63200",
  "nocTitle": "cook",
  "location": "Smiths Falls, ON",
  "streetAddress": "119 Lombard Street",
  "postalCode": "K7A 5B8",
  "salary": { "value": "19.00 hourly", "min": 19, "max": null, "currency": "CAD", "unit": "HOUR" },
  "workHours": "30 to 35 hours per week",
  "hoursPerWeekMin": 30,
  "hoursPerWeekMax": 35,
  "vacancies": 2,
  "employmentTerm": "Term or contract",
  "employmentSchedule": "Full time",
  "workLocation": "On site",
  "startDate": "Starts as soon as possible",
  "languages": "English",
  "education": ["Secondary (high) school graduation certificate"],
  "experience": "1 year to less than 2 years",
  "tasks": ["Prepare and cook complete meals or individual dishes and foods", "..."],
  "whoCanApply": ["a Canadian citizen", "a permanent resident of Canada", "a temporary resident of Canada with a valid work permit"],
  "medianWage": "19.00 $/hour",
  "medianWageRegion": "Ottawa Region",
  "datePosted": "October 06, 2026",
  "validThrough": "2026-10-27",
  "applyEmail": "hiring@example.ca",
  "apply": { "directApply": true, "emails": ["hiring@example.ca"], "mailAddress": null, "urls": [], "mustInclude": [], "screeningQuestions": [] },
  "postingSource": "Job Bank",
  "sourceType": "job_bank"
}
```

### Key output fields

`lmiaStatus`, `title`, `employer`, `postedBy`, `nocCode`, `nocTitle`, `vacancies`, `salary`, `workHours`, `hoursPerWeekMin`, `hoursPerWeekMax`, `medianWage`, `medianWageRegion`, `location`, `streetAddress`, `postalCode`, `employmentTerm`, `employmentSchedule`, `workSchedule`, `workLocation`, `startDate`, `languages`, `education`, `experience`, `tasks`, `requirements`, `whoCanApply`, `applyEmail`, `apply`, `datePosted`, `validThrough`, `description`, `url`, `jobId`, and in monitoring mode `changeType` and `changedFields`.

### Pricing

Pay per event:

- **$0.99 per 1,000 LMIA jobs** saved, apply email included.
- **$0.05 per contact email found** by the opt-in `enrichEmails` lookup. Jobs that already have an application email are not looked up.
- A small start fee per run, as standard on Apify.

### FAQ

**Is every LMIA job on Job Bank included?** Every posting Job Bank flags as LMIA requested or approved that matches your search. Employers who hold an LMIA but did not flag the posting cannot be identified from Job Bank.

**Why do some rows have no `applyEmail`?** The employer chose Direct Apply, mail, in person or a website instead. Those routes are in `apply`.

**Do I need a proxy?** No. The actor routes through residential proxy by default at no extra cost.

**Can I get all Job Bank jobs, not only LMIA?** Set `lmia` to *Any*. For a general Job Bank scraper, see the JobBank Scraper below.

### Explore more scrapers

- [JobBank Scraper: Canada Jobs, Apply Emails & LMIA](https://apify.com/memo23/jobbank-scraper): the same engine for all Job Bank jobs.
- [Eluta.ca Jobs Scraper](https://apify.com/memo23/eluta-scraper): Canadian employer career pages.

### For AI agents & LLM apps

Rows are typed JSON with a stable schema: `lmiaStatus` as an enum, numbers for wage, hours and vacancies, lists for tasks and requirements. An MCP-enabled agent can call this actor to answer "which employers in Alberta have open LMIA cook positions, and how do I apply?" from live data.

### Disclaimer

This actor collects only publicly available information from `jobbank.gc.ca`. It does not provide immigration advice. Use the data in compliance with applicable laws and the site's terms of use. You are responsible for making sure your outreach to employers complies with PIPEDA, CASL and other applicable rules.

### SEO keywords

LMIA jobs scraper, LMIA jobs Canada, LMIA approved jobs, LMIA employers list, Job Bank LMIA, temporary foreign worker jobs Canada, TFW program jobs, Canada jobs with visa sponsorship, LMIA job alert, Job Bank Canada scraper, NOC code jobs, employer apply email Canada, immigration job leads, foreign worker recruitment Canada.

# Actor input Schema

## `searchTerms` (type: `array`):

Keywords, e.g. `cook`, `truck driver`, `welder`. Each keyword runs as its own search. Leave empty to get every LMIA job in the location.

## `location` (type: `string`):

City, province or postal code, e.g. `Toronto`, `Surrey, BC`, `British Columbia`, `M5V`. Cities are resolved through Job Bank's own location service, so the search stays local. Leave empty for all of Canada.

## `searches` (type: `array`):

Separate keyword + location searches in one run, e.g. `[{"keyword": "carpenter", "location": "Calgary"}, {"keyword": "welder", "location": "Alberta"}]`. The filters below apply to every search.

## `startUrls` (type: `array`):

Paste JobBank search-result URLs or direct `/jobposting/{id}` URLs — each is auto-routed. Scraped in addition to any keywords.

## `lmia` (type: `string`):

Which LMIA postings to keep. 'Requested' = the employer applied for a Labour Market Impact Assessment to hire a temporary foreign worker; 'Approved' = the LMIA was granted. 'Any' turns the LMIA filter off and returns every job.

## `provinces` (type: `array`):

Limit to one or more provinces or territories.

## `nocCodes` (type: `array`):

Five-digit NOC 2021 codes, e.g. `72310` (carpenters), `63200` (cooks), `21232` (software developers).

## `datePosted` (type: `string`):

Only jobs posted in this window.

## `employmentSchedules` (type: `array`):

Work schedule.

## `employmentTerms` (type: `array`):

Permanent, contract, seasonal or casual.

## `workplaceTypes` (type: `array`):

Where the work happens, as published on the posting.

## `languages` (type: `array`):

Language requirement of the job.

## `annualSalaryRanges` (type: `array`):

Job Bank's yearly salary bands (CAD).

## `hourlySalaryRanges` (type: `array`):

Job Bank's hourly wage bands (CAD).

## `sourceType` (type: `string`):

Postings made on Job Bank itself (these carry the apply email), postings Job Bank imports from other boards such as Indeed, or both.

## `excludeStudentJobs` (type: `boolean`):

Drop jobs Job Bank marks as student jobs.

## `excludeApprenticeJobs` (type: `boolean`):

Drop jobs Job Bank marks as apprentice jobs.

## `excludePlacementAgencies` (type: `boolean`):

Drop jobs posted by recruitment and placement agencies.

## `internationalCandidatesOnly` (type: `boolean`):

Job Bank's 'Canadians and international candidates' facet.

## `sort` (type: `string`):

How JobBank sorts results. Use 'Date posted' with a small 'Maximum jobs' to get only the newest postings.

## `includeApplyInfo` (type: `boolean`):

Reads the posting's 'How to apply' block: application email, mailing address, apply URL, required documents and screening questions. Adds one request per Job Bank posting. External postings return their source link.

## `enrichEmails` (type: `boolean`):

For jobs without an application email, discover the employer's website and read its contact/about pages to find one. Adds contactEmail + contactWebsite plus an emailEnrichment object. Billed only when an email is found.

## `monitoringMode` (type: `string`):

'New and updated' saves only jobs that are new, changed, or expired since the previous run (unchanged jobs are not saved or charged). 'All' saves every job with a changeType field. The first run is the baseline and returns everything as new.

## `stateName` (type: `string`):

Keeps one watchlist apart from another. Use a stable name per schedule, e.g. `toronto-nurses`.

## `resetState` (type: `boolean`):

Forget the saved jobs for this state name and search, and start a new baseline.

## `maxItems` (type: `integer`):

Hard cap on jobs saved (and billed). Jobs dropped by a filter or skipped as unchanged do not count.

## `maxConcurrency` (type: `integer`):

Parallel requests. JobBank is a fast government site, so this can be high.

## `proxy` (type: `object`):

Leave empty — the actor already routes through its own residential proxy at no extra cost. Only set this to use your own proxies.

## Actor input object example

```json
{
  "searchTerms": [
    "cook"
  ],
  "location": "Calgary",
  "searches": [],
  "startUrls": [],
  "lmia": "requested_or_approved",
  "datePosted": "any",
  "sourceType": "any",
  "excludeStudentJobs": false,
  "excludeApprenticeJobs": false,
  "excludePlacementAgencies": false,
  "internationalCandidatesOnly": false,
  "sort": "M",
  "includeApplyInfo": true,
  "enrichEmails": false,
  "monitoringMode": "off",
  "stateName": "default",
  "resetState": false,
  "maxItems": 500,
  "maxConcurrency": 10
}
```

# Actor output Schema

## `jobs` (type: `string`):

One row per Job Bank vacancy with title, employer, parsed salary (min/max/unit/currency), location, employment type, dates, description and optional employer contact email.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchTerms": [
        "cook"
    ],
    "location": "Calgary",
    "searches": [],
    "startUrls": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("memo23/job-bank-canada-lmia-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "searchTerms": ["cook"],
    "location": "Calgary",
    "searches": [],
    "startUrls": [],
}

# Run the Actor and wait for it to finish
run = client.actor("memo23/job-bank-canada-lmia-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchTerms": [
    "cook"
  ],
  "location": "Calgary",
  "searches": [],
  "startUrls": []
}' |
apify call memo23/job-bank-canada-lmia-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,memo23/job-bank-canada-lmia-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/xBmYM7T5a1v4ck08X/builds/Qd3tSQYFGdXTIoiZc/openapi.json
