# Canada Job Postings Data: Salaries by NOC & Region (Job Bank) (`maritimio/canada-job-postings-data`) Actor

Canadian job postings from Job Bank open data as clean JSON. Filter by NOC code, keyword, province, region and French requirement. Salaries normalized to annual CAD, plus monthly aggregates.

- **URL**: https://apify.com/maritimio/canada-job-postings-data.md
- **Developed by:** [Maritimio](https://apify.com/maritimio) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 1,000 job postings

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

### What does Canada Job Postings Data do?

This Actor turns the **Government of Canada's Job Bank open data** into clean JSON you can filter. Pick occupations (NOC 2021 codes or keywords), provinces, economic regions, months and a French-language requirement, and get back:

- **Individual job postings**, each with its salary normalized to **annual CAD** so hourly, weekly and monthly wages compare on one scale.
- **Monthly aggregates** per occupation and region: posting count, vacancies, median annual salary and the **share of postings that require French**.

It reads the official monthly files that Employment and Social Development Canada (ESDC) publishes on open.canada.ca under the Open Government Licence - Canada. It does not scrape any website.

**You pay only for rows you receive.** Months published in a layout the Actor cannot read are skipped and free.

### Why use it?

- **Annual salaries you can compare.** Every posted wage with a usable amount and unit is converted to a full-time annual figure (hour × 2,080, day × 260, week × 52, every two weeks × 26, month × 12). The original amount and unit stay in the row.
- **Bad source values are flagged, not hidden.** Some postings carry unit errors, such as "$60,000 hourly". When the annualized midpoint falls outside $10,000 to $600,000, the row is marked `salaryQuality: "implausible"`, keeps its original numbers, and is left out of medians.
- **French requirement as a yes or no.** Postings that require French or bilingualism are `frenchRequired: true`. "English or French" is `false`, because either language is accepted. Useful for Quebec workforce planning.
- **Regional detail.** Filter and aggregate by Statistics Canada economic region (such as Montérégie, Montréal or Toronto), or by province.
- **Fast and light.** A focused query on one month takes a few seconds; a full year for all of Canada takes about two minutes.

### What data do you get?

| Field group | Fields |
|---|---|
| Job | `jobTitle`, `originalJobTitle`, `firstPostingDate`, `month`, `vacancies`, `employmentType`, `employmentTerm`, `education`, `experience` |
| Occupation and industry | `noc2021`, `noc2021Title`, `noc2016`, `noc2016Title`, `naicsSector` |
| Location | `province`, `provinceCode`, `economicRegion`, `city`, `postalCode` |
| Salary | `salaryMin`, `salaryMax`, `salaryPer`, `salaryText`, `annualSalaryMinCad`, `annualSalaryMaxCad`, `annualSalaryMidCad`, `salaryQuality` |
| Language | `officialLanguage`, `frenchRequired` |
| Other | `benefits` (dental, pension, RRSP and others), `placementAgency`, `fromPartnerSite` |

**What the source does not include:** employer names, job descriptions and links to the posting. ESDC leaves them out of the open data.

### How do I use it?

1. Add **NOC 2021 codes** (for example `21232` for software developers, or `2123` for that whole group) or **keywords**.
2. Optionally choose **provinces**, **economic regions** and a **French required** filter.
3. Set a **start and end month**, or leave both empty for the latest month.
4. Choose postings, aggregates or both, then click **Start**.

### How much does it cost?

| Event | Price |
|---|---|
| One job posting | $0.0008 |
| One aggregate row (one month, one occupation, one region) | $0.002 |
| Run start (Apify standard) | $0.00005 |

So 1,000 postings cost $0.80. Use **Maximum postings** (default 1,000) and Apify's maximum cost setting to cap any run. The Actor stops cleanly when a cap is reached.

### Input example

```json
{
    "nocCodes": ["21231", "21232"],
    "provinces": ["QC", "ON"],
    "startMonth": "2026-01",
    "endMonth": "2026-09",
    "frenchRequired": "any",
    "includePostings": true,
    "includeAggregates": true,
    "maxPostings": 1000
}
```

### Output example

A posting:

```json
{
    "type": "posting",
    "month": "2025-12",
    "firstPostingDate": "2025-12-17",
    "jobTitle": "Java programmer",
    "originalJobTitle": "Développeur junior intermédiaire Java Back-End",
    "noc2021": "21232",
    "noc2021Title": "Software developers and programmers",
    "province": "Québec",
    "provinceCode": "QC",
    "economicRegion": "Montérégie Region",
    "city": "Saint-Jean-sur-Richelieu",
    "vacancies": 1,
    "salaryMin": 70000,
    "salaryMax": 70000,
    "salaryPer": "Year",
    "annualSalaryMidCad": 70000,
    "salaryQuality": "ok",
    "officialLanguage": null,
    "frenchRequired": null,
    "fromPartnerSite": true,
    "attribution": "Contains information licensed under the Open Government Licence - Canada ...",
    "sourceUrl": "https://open.canada.ca/data/en/dataset/ea639e28-c0fc-48bf-b5dd-b8899bd43072"
}
```

An aggregate row:

```json
{
    "type": "aggregate",
    "regionLevel": "economicRegion",
    "month": "2026-09",
    "noc2021": "21232",
    "noc2021Title": "Software developers and programmers",
    "province": "Québec",
    "provinceCode": "QC",
    "economicRegion": "Montréal Region",
    "postings": 19,
    "vacancies": 23,
    "postingsWithSalary": 19,
    "medianAnnualSalaryCad": 104000,
    "postingsWithLanguage": 6,
    "frenchRequiredShare": 0.67
}
```

`frenchRequiredShare` is based on `postingsWithLanguage`, the postings that state a language (here 4 of 6). The run summary (months read, counts, skipped months and the licence attribution) is saved as `OUTPUT` in the run's key-value store.

### Things to know about the data

- **Coverage.** January 2023 to May 2024, and October 2024 to the latest month. ESDC published June to September 2024 in a different file layout; those months are skipped and free.
- **French requirement starts in January 2026.** Earlier files have no language field, so `frenchRequired` is `null` there, and the French filter excludes those months' postings.
- **Partner-site postings.** More than half of the postings (`fromPartnerSite: true`) are ads Job Bank republishes from partner job boards. They have a title, occupation, location and salary, but no education or experience data, and often no language data.
- **No telework filter.** The source has a telework column, but in every file checked it reads only "No" or blank, so a filter on it would mislead you.
- **Medians use the midpoint** of each posting's annual salary range and skip missing or implausible salaries. `postingsWithSalary` tells you how many postings each median is based on.
- **Months are by first posting date**, as ESDC publishes them.

### FAQ

**Is this data free to use commercially?**
Yes. ESDC publishes it under the Open Government Licence - Canada, which allows commercial use with attribution. Every row carries the attribution text, so it travels with the data.

**How fresh is it?**
ESDC usually publishes each month's file in the first days of the following month, though some months have come out later. The Actor reads the catalogue on every run, so a new month is available as soon as it is published.

**Can I use it from an AI agent?**
Yes. Call it through the Apify API or the Apify MCP server. Every row type has the fields documented above.

**Something broke or you need a feature?**
Open an issue on the Issues tab.

### Licence and attribution

Contains information licensed under the [Open Government Licence - Canada](https://open.canada.ca/en/open-government-licence-canada). Source: Employment and Social Development Canada, [Job Postings Advertised on Canada's National Job Bank Website](https://open.canada.ca/data/en/dataset/ea639e28-c0fc-48bf-b5dd-b8899bd43072).

This Actor is not affiliated with or endorsed by the Government of Canada, ESDC or Job Bank.

# Actor input Schema

## `startMonth` (type: `string`):

First month to include, as YYYY-MM (for example 2026-01). Leave empty for the latest published month only.

## `endMonth` (type: `string`):

Last month to include, as YYYY-MM. Leave empty for the latest published month. Up to 24 months per run.

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

Occupation codes from the National Occupational Classification 2021. Use full 5-digit codes (21231 = software engineers and designers) or prefixes for wider groups (2 = natural and applied sciences, 2123 = computer, software and web developers, programmers and designers). Leave empty for all occupations.

## `keywords` (type: `array`):

Match any of these words in the job title or occupation name. Accents and capitals are ignored. Combined with NOC codes, a posting must match both.

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

Two-letter codes: AB, BC, MB, NB, NL, NS, NT, NU, ON, PE, QC, SK, YT. Leave empty for all of Canada.

## `economicRegions` (type: `array`):

Statistics Canada economic region names, or part of a name (for example "Montréal", "Toronto", "Lower Mainland"). Accents and capitals are ignored.

## `frenchRequired` (type: `string`):

Filter on the posting's language requirement. "Yes" keeps postings that require French or bilingualism. This field exists only from January 2026; earlier months have no language data.

## `includePostings` (type: `boolean`):

One row per matching job posting, with salary normalized to annual CAD.

## `includeAggregates` (type: `boolean`):

One row per month, occupation and region: postings, vacancies, median annual salary and the share requiring French. Aggregates cover every matching posting, even past the posting limit.

## `aggregateBy` (type: `string`):

Group aggregates by economic region (finer) or by province.

## `maxPostings` (type: `integer`):

Stop delivering individual postings after this many. 0 means no limit. Aggregates still cover every match.

## Actor input object example

```json
{
  "nocCodes": [
    "21231",
    "21232"
  ],
  "provinces": [
    "QC",
    "ON"
  ],
  "frenchRequired": "any",
  "includePostings": true,
  "includeAggregates": true,
  "aggregateBy": "economicRegion",
  "maxPostings": 1000
}
```

# Actor output Schema

## `results` (type: `string`):

Job postings and aggregate rows as JSON.

## `summary` (type: `string`):

Months read, counts, skipped months and the Open Government Licence attribution.

# 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 = {
    "startMonth": "",
    "endMonth": "",
    "nocCodes": [
        "21231",
        "21232"
    ],
    "provinces": [
        "QC",
        "ON"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("maritimio/canada-job-postings-data").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 = {
    "startMonth": "",
    "endMonth": "",
    "nocCodes": [
        "21231",
        "21232",
    ],
    "provinces": [
        "QC",
        "ON",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("maritimio/canada-job-postings-data").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 '{
  "startMonth": "",
  "endMonth": "",
  "nocCodes": [
    "21231",
    "21232"
  ],
  "provinces": [
    "QC",
    "ON"
  ]
}' |
apify call maritimio/canada-job-postings-data --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,maritimio/canada-job-postings-data"
        }
    }
}
```

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/NsaimhA7IJyX5xh5C/builds/lXvCVGeqXmMH0lxx4/openapi.json
