# Multi Job Board Scraper | LinkedIn + Career Sites, $1.50/1K (`aurelix/multi-job-board-scraper`) Actor

Scrape multiple job boards at once: LinkedIn and ~50 company career sites (Greenhouse, Lever, Ashby, SmartRecruiters) together, which the job scrapers we compared do not, at 25% under their price: $1.50 per 1,000 jobs. Plus Remote OK and WWR; duplicates merged and charged once; new-jobs mode.

- **URL**: https://apify.com/aurelix/multi-job-board-scraper.md
- **Developed by:** [Aurelix](https://apify.com/aurelix) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.90 / 1,000 job results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 is Multi Job Board Scraper?

**Search LinkedIn, about 50 company career sites, Remote OK and We Work Remotely at once, duplicates merged and charged once, for $1.50 per 1,000 jobs, no login.**

For job boards, recruiters and newsletters. One input (keywords, country, city, remote, date posted) runs on every source, and every job comes back in one schema with ISO dates and numeric pay. Your first run with the example input costs about $0.011 (7 jobs in our test run).

[![Multi Job Board Scraper: example output from a real run](https://api.apify.com/v2/key-value-stores/twkCCtTWzsi8fVbx5/records/multi-job-board-scraper-card-v2.png)](https://console.apify.com/actors/1vRso7C485nO3hsrY?addFromActorId=1vRso7C485nO3hsrY)

[![Multi Job Board Scraper: 75-second demo video](https://api.apify.com/v2/key-value-stores/twkCCtTWzsi8fVbx5/records/multi-job-board-scraper-demo-thumb-v1.png)](https://api.apify.com/v2/key-value-stores/twkCCtTWzsi8fVbx5/records/multi-job-board-scraper-demo-v1.mp4)

**[▶ Watch the 75-second demo](https://api.apify.com/v2/key-value-stores/twkCCtTWzsi8fVbx5/records/multi-job-board-scraper-demo-v1.mp4)**

| | |
|---|---|
| **Search by** | Keywords + country, optional city |
| **Sources** | LinkedIn, ~50 bundled company career sites plus your own, Remote OK, We Work Remotely |
| **Filters** | Remote only, date posted, sources, max jobs per source, new jobs only |
| **Output** | 30 fields per job; JSON, CSV, Excel, API |
| **Automate** | Schedules, webhooks, Zapier, Make, n8n, Google Sheets, MCP for AI agents |
| **Price** | $1.50 per 1,000 jobs ($0.90 per 1,000 jobs on paid Apify plans) |
| **Login needed** | No |

### Daily monitor: only what's new, every morning

Set it once, get only the new jobs every morning. With **Only new jobs since last run** on, the first run returns everything that matches; each later run skips jobs that earlier runs already returned, so you pay only for new ones.

```json
{
  "keywords": [
    "data engineer"
  ],
  "country": "CA",
  "location": "Toronto, ON",
  "datePosted": "past24h",
  "maxResultsPerSource": 50,
  "incremental": true,
  "stateKey": "toronto-data-daily"
}
```

1. Paste this input into the **Input** tab (JSON view) and click **Save as a new task**; it appears under **Saved tasks**.
2. Open the task from **Saved tasks** and click **Schedule**.
3. Choose **Daily**, pick the hour, and save.

**Cost example:** 1 search across boards, about 25 new jobs a day ≈ $1.16/month on the free plan ($0.71 on paid plans): one run a day for 30 days, start fee included. The first run returns everything that matches, so it costs more.

Add a Slack incoming-webhook URL in `slackWebhookUrl` (or any URL in `webhookUrl`) to get a short digest of each run's new jobs; nothing is sent when a run finds nothing.

### Why Multi Job Board Scraper users pick this one

- **Many boards, one run.** LinkedIn plus employers' own career sites, Remote OK and WWR.
- **One row per job.** Cross-source duplicates merged with 'also_listed_on' links, charged once.
- **Gaps filled.** A LinkedIn row gains the career site's pay range and apply link.
- **25% under the alternatives.** $1.50 per 1,000 jobs against $2.00.
- **Throttling contained.** If one source slows down, the others still deliver.

### Get started in 3 steps

1. Type job keywords (e.g. 'data engineer') and pick a country; optionally a city.
2. Pick the sources (LinkedIn, career sites and remote boards by default), remote-only, date posted and how many jobs to take from each source; optionally add company job boards.
3. Click Start, then download one deduplicated list as JSON, CSV or Excel, or schedule it as a daily feed to Google Sheets, Zapier, Make or n8n.

**[▶ Try it with the example input](https://console.apify.com/actors/1vRso7C485nO3hsrY?addFromActorId=1vRso7C485nO3hsrY)**

The form opens with an example input already filled in: that run costs about $0.011 on the free plan.

### How much does it cost to scrape job boards?

Pay per event: you pay only for what the run returns, plus a small start fee. Bronze, Silver, Gold and higher Apify plans pay less per result.

| Jobs | Free plan | Paid plans (Bronze, Silver, Gold) |
|---|---|---|
| 100 | $0.15 | $0.091 |
| 1,000 | $1.50 | $0.90 |
| 10,000 | $15.00 | $9.00 |

Totals include the $0.001 start fee per run, rounded to the cent (amounts under $0.10 to four decimals). Apify's free plan includes $5 of credit a month, enough for about 3,300 jobs.

Proxy included. A job found on several sources is one row and is charged once. Each run's start fee covers searching every source even when nothing matches.

#### Billing events

| Event | Free plan | Paid plans (Bronze, Silver, Gold) |
|---|---|---|
| `job-result` | $1.50 per 1,000 jobs | $0.90 per 1,000 jobs |
| `apify-actor-start` | $0.001 per run | $0.001 per run |

### Multi Job Board Scraper vs alternatives

| | **This Actor** | curious_coder/linkedin-jobs-scraper | charliemorrisondev/remote-jobs-aggregator |
|---|---|---|---|
| **Price per 1,000 jobs, free plan** | $1.50 | $2.00 | $2.00 |
| **Price per 1,000 jobs, paid plans** | $0.90 | $1.00 | $2.00 |
| **Start fee per run** | $0.001 (at 512 MB) | $0.00005 | $0.00005 |
| **Platform usage billed on top** | No | No | No |
| **Sources** | LinkedIn, ~50 career sites, 2 remote boards | LinkedIn | 8 remote boards |
| **LinkedIn jobs** | ✓ | ✓ | ✗ |
| **Company career sites (Greenhouse, Lever, Ashby, SmartRecr...** | ✓ | ✗ | ✗ |
| **Same job across sources merged and charged once** | ✓ | One source | not stated |
| **Only-new-jobs mode for scheduled runs** | ✓ | not stated | not stated |

Prices checked regularly on each Actor's Store page.

### Benchmarks: how we compare

| Test | Input | Result | Run time |
|---|---|---|---|
| First run of the prefilled input | 'data engineer', country United States, 5 jobs per source, 10 bundled boards | 7 jobs, about $0.011 charged in total | not recorded |
| Cross-source dedupe | 'engineer', Canada, Toronto, past month, LinkedIn + career sites, 150 per source, no descriptions | 171 jobs (LinkedIn 149, career sites 22); 2 jobs found on both merged into one row each, no same company + title pair missed | 36 s |
| Mixed sources | 'data engineer', Canada, Toronto, past month, 10 per source | 18 jobs (LinkedIn 10, career sites 1, We Work Remotely 7) | 30 s |
| New-jobs mode, two runs in a row | 'software engineer', Canada, Toronto, past month, 15 per source | Run 1: 36 jobs. Run 2: 8 jobs, none of them repeats from run 1 | 70 s, then 47 s |
| Keyword that matches nothing | A made-up keyword, all sources | 0 jobs, run succeeded, start fee only | 8 s (cloud) |
| Run stopped by its timeout | Cloud run with a 150 s timeout | 591 jobs kept, all unique | 150 s |
| One source rate-limited (simulated) | LinkedIn forced to fail | 5 jobs from the other sources kept and charged; status 'Partial' names LinkedIn | 4 s |
| Every source down (simulated) | All requests fail | 0 jobs, nothing charged per job; status says to try again later | 1 s |
| Accuracy against live job pages | 17 rows from LinkedIn, career sites and We Work Remotely | 17 of 17 matched on title, company and city; one We Work Remotely region label comes from the feed and is not printed on the page | - |
| Removal list | One email, one domain and one company name on the removal list | Exactly the matching rows withheld (1, 1 and 2); no other row lost; charges equal rows saved | 17-69 s |

Each row is one run on our own account. Rows marked cloud ran on Apify; the others ran the same code on our test machine without a proxy, with faults injected where it says simulated. Accuracy was checked by opening each job's own page and comparing the fields by hand. Job counts are what the sources showed at the time of each run.

### What data you get

**Multi Job Board Scraper** runs one job search across LinkedIn's public job search, a bundled list of about 50 company career sites (25 searched per run by default) on Greenhouse, Lever, Ashby and SmartRecruiters (plus any boards you add), and the Remote OK and We Work Remotely feeds, and returns one deduplicated list. Every row has the same fields: title, company, location, ISO country, workplace type, job types, pay where published, posted date, job and apply links and description, plus the sources it was found on. Give keywords and a country once; the same job on LinkedIn and the employer's own site becomes one row, charged once.

- source, sources and also_listed_on (source, id, link)
- Title, company, location, city, region, ISO country
- Remote/hybrid/on-site, job types, seniority (LinkedIn)
- Pay minimum, maximum, currency, period, or pay text where published
- Posted date where the source gives one
- Job link, employer apply link where known, description text and HTML

| title | company | location | source | sources | salary_min | posted_date |
|---|---|---|---|---|---|---|
| Senior Data Engineer (Databricks Focused) | BDO Canada | Oakville, Ontario, Canada | linkedin | \["linkedin"] |  | 2026-11-03 |
| Senior Staff Data Engineer | Cohere | Toronto | careersite | \["careersite"] | 275000 | 2026-10-22 |
| Senior Data Engineer, Ads | Discord | Anywhere in the World | weworkremotely | \["weworkremotely"] |  | 2026-11-01 |

One full job record, exactly as a real run returned it (long text and lists shortened here). From a real run:

```json
{
  "job_id": "careersite:lever:pointclickcare:6ec25484-b204-48a6-ab91-7b0b1625aa02",
  "source": "careersite",
  "source_job_id": "lever:pointclickcare:6ec25484-b204-48a6-ab91-7b0b1625aa02",
  "title": "Intermediate Software Engineer",
  "company": "PointClickCare",
  "company_url": "https://www.linkedin.com/company/pointclickcare",
  "location": "Mississauga/Toronto/Kitchener/Waterloo/Cambridge",
  "city": "Mississauga",
  "region": "ON",
  "country": "CA",
  "workplace_type": "hybrid",
  "job_types": [
    "fullTime"
  ],
  "seniority": null,
  "salary_min": 102600,
  "salary_max": 128300,
  "salary_currency": "CAD",
  "salary_interval": "year",
  "salary_text": "At PointClickCare, base salary is one of the many components that make up our total rewards package. The CAD base salary... [247 more characters]",
  "posted_at": "2026-10-21T20:57:35Z",
  "posted_date": "2026-10-21",
  "job_url": "https://jobs.lever.co/pointclickcare/6ec25484-b204-48a6-ab91-7b0b1625aa02",
  "apply_url": "https://jobs.lever.co/pointclickcare/6ec25484-b204-48a6-ab91-7b0b1625aa02/apply",
  "description_text": null,
  "description_html": null,
  "search_keyword": "intermediate software engineer",
  "sources": [
    "careersite",
    "linkedin"
  ],
  "also_listed_on": [
    {
      "source": "linkedin",
      "job_id": "linkedin:4481154739",
      "job_url": "https://www.linkedin.com/jobs/view/4481154739/"
    }
  ],
  "dedupe_key": "pointclickcare|intermediate software engineer|mississauga toronto kitchener waterloo cambridge",
  "source_url": "https://jobs.lever.co/pointclickcare/6ec25484-b204-48a6-ab91-7b0b1625aa02",
  "scraped_at": "2026-11-04T06:18:09Z"
}
```

### Use cases

- Job seekers and career coaches running one daily search instead of three
- Job boards and newsletters backfilling deduplicated postings with the new-jobs-only mode
- Recruiters comparing a role's pay and posting date across LinkedIn and the employer's own site
- Market researchers counting hiring demand without double-counting cross-posted jobs

### Use-case recipes

#### Job seeker: a daily Slack feed of new jobs in one city

Type the job titles you want, your country and city, and turn on 'Only new jobs since last run'. Schedule it daily with a Slack webhook: the digest lists how many new jobs were saved and the top 10 with links. Open `apply_url` (or `job_url`) to apply; `salary_min` and `salary_max` show pay where it is published.

```json
{
  "keywords": [
    "data engineer",
    "analytics engineer"
  ],
  "country": "CA",
  "location": "Toronto, ON",
  "datePosted": "past24h",
  "maxResultsPerSource": 25,
  "incremental": true,
  "stateKey": "toronto-data",
  "slackWebhookUrl": "https://hooks.slack.com/services/T000/B000/XXXX"
}
```

#### Remote job board or newsletter: deduplicated remote postings

Turn on 'Remote jobs only' and pick the country your readers live in: remote-board jobs are kept when they are open to that country or worldwide. Each row has the same fields whatever its source, so `title`, `company`, `salary_min` and `job_url` go straight into your posting template. Turn on new-jobs mode for the scheduled version so a job already published is not sent again.

```json
{
  "keywords": [
    "product manager",
    "product designer"
  ],
  "country": "US",
  "remoteOnly": true,
  "datePosted": "pastWeek",
  "maxResultsPerSource": 20
}
```

#### Recruiter: compare pay and posting date on LinkedIn and the employer's site

Run LinkedIn and the career sites together on the Canadian bundled list. Filter the rows whose `sources` has two entries: they are the same job on both. The kept row carries the career site's `salary_min`, `salary_max` and `apply_url` when LinkedIn lacks them, and `also_listed_on` gives the LinkedIn link.

```json
{
  "keywords": [
    "software engineer"
  ],
  "country": "CA",
  "sources": [
    "linkedin",
    "careersites"
  ],
  "starterList": "canada",
  "maxBoards": 25,
  "datePosted": "pastMonth",
  "maxResultsPerSource": 50
}
```

#### Market research: hiring demand without double counting

Run a set of role keywords for one country over the past month with descriptions off for speed. Count rows by `company` and `search_keyword`: a job posted on several sources is one row, so counts are not inflated by cross-posting. Use `workplace_type` to split remote from on-site demand.

```json
{
  "keywords": [
    "nurse",
    "pharmacist"
  ],
  "country": "CA",
  "datePosted": "pastMonth",
  "includeDescription": false,
  "maxResultsPerSource": 50
}
```

### Input

| Field | Type | What it does | Default |
|---|---|---|---|
| `keywords` (Job keywords) | array | Job title words, e.g. \['data engineer', 'product manager']. |  |
| `maxBoards` (Max bundled company boards) | integer | Upper limit of bundled company boards searched per run (home-country employers first); boards you list in 'companies' are always searched.... | 25 |
| `maxResultsPerSource` (Max jobs per source) | integer | Upper limit of jobs returned from each source (LinkedIn, career sites, remote boards), after cross-source dedupe, newest first within each... | 25 |
| `proxyConfiguration` (Proxy (LinkedIn)) | object | Proxy for LinkedIn. | {"useApifyProxy": true} |
| `country` (Country) | string | Country to search, e.g. 'CA'. | "US" |
| `location` (City or region (optional)) | string | City inside the country, e.g. 'Toronto, ON'. |  |

All 16 fields, with examples: see the **Input** tab.

### Switching from curious_coder/linkedin-jobs-scraper

Already running `curious_coder/linkedin-jobs-scraper`? It searches LinkedIn only; this Actor runs the same search on LinkedIn, company career sites and two remote boards and merges the copies. Map its input fields like this:

| `curious_coder/linkedin-jobs-scraper` input | This Actor's input | Notes |
|---|---|---|
| `urls` | no equivalent | Search URLs are not accepted. Give `keywords`, `country` and, if you want, a city in `location`. |
| `keywords` | `keywords` | Theirs is one text; ours is a list. LinkedIn runs one search per entry, and the same words filter career-site and remote-board titles. |
| `location` | `country` + `location` | Pick one of 30 countries from the list and type the city in `location`, e.g. 'Toronto, ON'. For 'Remote', turn on `remoteOnly` instead. |
| `geoId` / `distance` | no equivalent | Location is the city text plus the country. LinkedIn rows from another country are dropped. |
| `datePosted` | `datePosted` | Values `any`, `past24h`, `pastWeek`, `pastMonth`. Our default is the past week, theirs is any time. Career-site and remote-board jobs without a posted date are kept. |
| `companyIds` | no equivalent | Our `companies` field adds company career-site boards (Greenhouse, Lever, Ashby, SmartRecruiters), e.g. `greenhouse:stripe`, not LinkedIn company IDs. |
| `under10Applicants` | no equivalent | Applicant counts are not returned. |
| `scrapeCompany` | no equivalent | LinkedIn rows carry `company_url` only; no company details are fetched. |
| `limitPerSource` | `maxResultsPerSource` | Up to 500 per source, counted after cross-source dedupe. LinkedIn, career sites and the two remote boards (together) are the three sources. |
| `splitByLocation` / `splitCountry` | no equivalent | No automatic split. To get past LinkedIn's limit of about 1,000 results per search, run one search per city in `location`. |

- For LinkedIn-only rows like theirs, set `sources` to `["linkedin"]`. With all sources on, `source` says where a row came from and `also_listed_on` lists the other copies.
- A job found on LinkedIn and on the employer's career site is one row and is charged once.
- Field names differ: dates are `posted_date` (YYYY-MM-DD), the country is an ISO code in `country`, and pay is numeric in `salary_min` / `salary_max` where the source publishes it.
- Price per 1,000 jobs from the comparison table: $1.50 against their $2.00 on the free plan and $0.90 against $1.00 on paid plans; our start fee is higher ($0.001 against $0.00005 per run).

### Use with AI agents (Apify MCP server, Claude, ChatGPT, n8n AI agent)

Multi Job Board Scraper is a ready-made tool for AI agents. Through the Apify MCP server an agent reads this Actor's input schema, fills in the input from your request, starts the run and reads the jobs back. You pay the same per-result price as a run started by hand, from your own Apify account.

#### Example prompts

- "Find data engineer jobs in Toronto posted in the past week on LinkedIn and company career sites, merge duplicates and give me a table with title, company, pay and apply link."
- "Search remote product manager jobs open to the United States on LinkedIn, Remote OK and We Work Remotely and list the 30 newest with their links."
- "Every morning, give me only the new software engineer jobs in Vancouver BC from LinkedIn and the bundled Canadian company career sites, with pay where published."

#### Claude (claude.ai and Claude Desktop)

Open **Settings > Connectors**, choose **Add custom connector**, paste `https://mcp.apify.com/?tools=aurelix/multi-job-board-scraper` and sign in to Apify when asked. Then ask Claude one of the prompts above.

#### Claude Code, Cursor, VS Code and other MCP clients

```bash
claude mcp add --transport http apify "https://mcp.apify.com/?tools=aurelix/multi-job-board-scraper"
```

Clients configured with JSON take the same URL:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com/?tools=aurelix/multi-job-board-scraper"
    }
  }
}
```

A client without Apify sign-in sends the header `Authorization: Bearer <YOUR_APIFY_TOKEN>`; to run the server locally instead, use `npx -y @apify/actors-mcp-server --actors aurelix/multi-job-board-scraper` with `APIFY_TOKEN` set.

#### ChatGPT

Where your ChatGPT plan offers custom connectors (developer mode), add `https://mcp.apify.com/?tools=aurelix/multi-job-board-scraper` as a connector, sign in to Apify, and ask for the data in plain words.

#### n8n AI agent

Give an **AI Agent** node an **MCP Client Tool** with the endpoint `https://mcp.apify.com/?tools=aurelix/multi-job-board-scraper` and a Bearer credential holding your Apify token. The agent can then call Multi Job Board Scraper whenever a workflow step needs jobs; for a fixed, non-agent workflow use Apify's n8n node with **Run Actor** instead.

### Use it from AI agents (MCP), the API or no-code

#### Copy this to your AI assistant

```text
Use the Apify Actor aurelix/multi-job-board-scraper (Multi Job Board Scraper) through Apify's MCP server or API. Main input: keywords (Job keywords); maxBoards (Max bundled company boards). Example input: {"keywords": ["data engineer"], "country": "CA", "location": "Toronto, ON", "datePosted": "pastWeek", "sources": ["linkedin", "careersites", "remoteboards"], "companies": ["greenhouse:stripe"], "maxResultsPerSource": 50}. Each result is one job with fields such as job_id, source, source_job_id, title, company, company_url, location, city. It costs $1.50 per 1,000 jobs on Apify's free plan plus a $0.001 start fee per run, and needs no login.
```

MCP server URL for this Actor: `https://mcp.apify.com/?tools=aurelix/multi-job-board-scraper` (setup for each client in the section above).

#### API

Start a run and read the dataset with Apify's REST API or the Python and JavaScript clients:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("aurelix/multi-job-board-scraper").call(run_input={'keywords': ['data engineer'], 'country': 'CA', 'location': 'Toronto, ON', 'datePosted': 'pastWeek', 'sources': ['linkedin', 'careersites', 'remoteboards'], 'companies': ['greenhouse:stripe'], 'maxResultsPerSource': 50})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
```

#### No-code: schedule it in 3 clicks

Send the results to Google Sheets, Zapier, Make, n8n, Slack or any webhook URL.

1. Fill in the input, then click **Save as a new task**.
2. In the task, click **Schedule** and pick daily or weekly (Apify Schedules).
3. Turn on **Only new jobs since last run** so each run returns only what is new, and paste a Slack or webhook URL into **Slack webhook URL** / **Webhook URL** to get a digest of the new items (count plus the top 10) after every run.

Prefer n8n or Make? Ask in the **Issues** tab and we will share a ready-made workflow for it.

### FAQ

#### Can I scrape job boards without login?

No. Everything is read logged-out: LinkedIn's public job search, the employers' public job-board APIs, and the remote boards' public feeds.

#### Is there a job board API?

You can use this Actor as one. Start it with Apify's REST API or the Python and JavaScript clients and read the results as JSON (see the code above), or call it from an AI agent through MCP.

#### How much does it cost to scrape job boards?

About $1.50 for 1,000 jobs on Apify's free plan and $0.90 on paid plans, start fee included. The free $5 monthly credit covers about 3,300 jobs.

#### Can I export job boards data to Excel or Google Sheets?

Yes. Download the results as Excel, CSV, JSON, XML or HTML from the run's **Storage** tab, or send them to Google Sheets with Apify's Google Sheets integration or the API.

#### Is it legal to scrape job boards?

It reads only public data any logged-out visitor can see: job postings on LinkedIn, the Greenhouse, Lever, Ashby and SmartRecruiters job-board APIs, Remote OK's API and We Work Remotely's RSS feed. It goes at a polite pace, never logs in and never solves CAPTCHAs. It returns job posting fields (title, company, location, pay, dates, description, link); no member or candidate profiles; descriptions can name a recruiter or hiring contact (business contact information, B2B use only, see Lawful use). To have a record removed, open an issue on this Actor's Issues tab; if a source formally asks us to stop, this Actor will stop. You are responsible for using the data lawfully, incl. anti-spam and privacy laws; this is not legal advice.

#### How are duplicates matched?

By normalised company name (legal suffixes like Inc. or Ltd removed), job title and an overlapping location: any shared city (a career site's 'Mississauga/Toronto' matches LinkedIn's 'Mississauga'), both remote, or one side without a location. Jobs are sorted newest posting first before merging, so the copy with the latest posting time keeps the row (a career-site copy with a full timestamp usually wins over a LinkedIn copy from the same day); in a long run, a copy saved in an earlier batch keeps it. The others are listed in 'also_listed_on', and their pay, apply link or description fill any gaps.

#### Which career sites are searched?

About 50 bundled public job boards on Greenhouse, Lever, Ashby and SmartRecruiters (Canadian employers such as Geotab, Wealthsimple and Cohere, plus global employers such as Stripe and GitLab), plus any you add in 'Extra companies'. By default 25 of them are searched per run, home-country employers first; set 'Max bundled company boards' to 0 for all, or the bundled list to 'canada' or 'none' to narrow it.

#### What happens if a source is rate-limited?

The other sources still run. Jobs already found are kept and charged; the run ends with a 'Partial' status naming the source that stopped. Retry later for the rest.

#### Why not Indeed, Glassdoor or ZipRecruiter?

Indeed is not included. Glassdoor and ZipRecruiter block shared datacenter addresses, so they are not included either.

#### What am I charged when nothing matches, or only part of the search works?

A run that finds nothing pays only its start fee. Jobs are charged one by one as they are saved, so a run where one source is rate-limited still pays only for the jobs the other sources delivered. Duplicates merged into another row, jobs dropped by your filters and, in new-jobs mode, jobs already returned are never charged.

#### How many jobs can one run return?

Up to 'Max jobs per source' from each of the three sources (LinkedIn, career sites, and the two remote boards together), at most 500 each, so at most 1,500 rows. The limit counts rows after dedupe, newest first. LinkedIn also stops at about 1,000 results per search; the Actor runs one LinkedIn search per keyword.

#### How long does a run take?

In our tests a search with no matches took 8 s, 171 jobs from LinkedIn and career sites took 36 s with descriptions off, and 18 jobs from all sources took 30 s. Full descriptions need one extra LinkedIn request per job, so turn 'Include full description' off when you only need the list. Searching the whole bundled list (Max bundled company boards = 0) is slower than the default 25.

#### How does 'Only new jobs since last run' work?

The Actor remembers each job by its company, title and first city (or 'remote'), the 'dedupe_key' field, so a job reposted on another source is not new. The memory is shared by runs with the same keywords, country, city, remote, date, sources and company settings, or by every run with the same 'Incremental state name'. Changing 'Max jobs per source' or descriptions keeps the memory. A job is forgotten 60 days after it was last seen.

#### Which countries can I search?

The 30 countries in the Country list, including the US, Canada, the UK, most of Western Europe, India, Singapore, Japan, Australia and Brazil. LinkedIn searches that country (or the city you give). Career-site jobs are kept when their location is in that country; remote-board jobs when they are open to that country or worldwide.

#### What do 'source', 'sources' and 'also_listed_on' mean?

'source' is where the saved row came from: linkedin, careersite, remoteok or weworkremotely. 'sources' lists every source the same job was found on, and 'also_listed_on' gives the other copies' source, job_id and job_url. 'search_keyword' is the keyword that matched, and 'job_id' is the source plus that source's own ID.

#### Why do I get only a few career-site jobs?

Career-site jobs are matched by keyword in the title, so 'data engineer' does not find 'Data Platform Engineer'. Only the bundled employers (25 per run by default, 10 in the prefilled input) and the boards you add are searched, and a job must be in the chosen country and mention your city unless it is remote. Add more keywords, raise 'Max bundled company boards', or add employers in 'Extra companies'.

#### Which proxy does it need?

Only the LinkedIn search uses a proxy, and Apify's datacenter proxy (the default) works; it is included in the price. Career sites and the remote boards are public APIs and feeds and are read without a proxy.

### Lawful use

- B2B use only: descriptions are kept as the employer published them and can include a recruiter or hiring contact. That is business contact information; use it only for business purposes such as job search, hiring-market research or a relevant business enquiry, never for consumer marketing.
- No spam: contact people only with relevant, individual business messages, identify yourself, and honour every opt-out at once. Bulk unsolicited email built from this data is not allowed.
- You are responsible for complying with the anti-spam and privacy laws that apply to you and to the people you contact, including Canada's CASL, the US CAN-SPAM Act and the EU/UK GDPR (lawful basis, transparency, the right to object).
- Removal requests: open an issue on this Actor's Issues tab naming the person, email address or domain to remove. It is added to our suppression list and honoured within 7 days; from then on matching records are not returned.

### Limits (stated honestly)

- LinkedIn caps results per search at about 1,000; split broad searches.
- Career sites cover only the bundled employers plus the boards you add, not every company in the country.
- Career-site and remote-board jobs are matched by keyword in the title (and company, for remote boards), not by full-text relevance.
- Dedupe is by company, title and overlapping location: two genuinely different openings with the same title in a shared city are merged. Rows are saved in batches during long runs (after the first minute); a duplicate found after its first copy was saved is dropped, but is not added to that row's 'also_listed_on'.
- LinkedIn shows a posted date without a time; posted_at is null for LinkedIn rows.
- Pay and remote flags are only as complete as each source publishes them.
- Sources can change their pages; the daily health run catches breakage within 24 hours.

### Related Aurelix tools

Public Aurelix Actors, grouped by the data they collect. All are pay-per-result and need no login.

#### Jobs and hiring data

- **[Greenhouse Jobs Scraper](https://apify.com/aurelix/greenhouse-jobs-scraper)**: Live jobs from a company's Greenhouse, Lever, Ashby or SmartRecruiters board, with salary and Canadian province tags.
- **[LinkedIn Jobs Country Filter Scraper](https://apify.com/aurelix/linkedin-jobs)**: Public LinkedIn job postings by keyword and country: title, company, location, seniority, job type, salary text, description.

#### E-commerce and product data

- **[AliExpress Scraper API](https://apify.com/aurelix/aliexpress-product-search)**: AliExpress search results by keyword: price, discount, rating, units sold, ship-from country and sponsored flag.
- **[Amazon New Releases & Best Sellers](https://apify.com/aurelix/amazon-best-sellers)**: Amazon Best Sellers and New Releases top 100 on 10 marketplaces: rank, ASIN, price, rating, rank change.

#### Leads and company data

- **[Funded Companies Hiring](https://apify.com/aurelix/funded-companies-hiring)**: Companies that just raised money (SEC Form D), with their open roles and business contacts, as a new-only monitor.
- **[Google Maps Phone Scraper](https://apify.com/aurelix/google-maps-scraper)**: Local business lists from Google Maps: phones, websites, ratings, hours, coordinates; optional website emails and socials.
- **[Recently Funded Companies API](https://apify.com/aurelix/recently-funded-companies)**: US companies that just filed an SEC Form D: amount raised, industry, state, executives, confirmed website.

#### Ad intelligence

- **[Competitor Watch](https://apify.com/aurelix/competitor-watch)**: One report per competitor with only what is new: Meta and TikTok ads, page changes, Shopify products and prices, jobs.
- **[Facebook Ad Library Search & API](https://apify.com/aurelix/facebook-ad-library-scraper)**: Facebook and Instagram business ads from the Meta Ad Library: copy, creatives, landing pages, dates, optional EU reach.

#### Canada and government data

- **[Canada Building Permits](https://apify.com/aurelix/canada-building-permits-scraper)**: Building permits and development applications from 7 Canadian cities: address, type, status, dates, value, contractor company.

#### AI, content and SEO

- **[Google Trends by Region](https://apify.com/aurelix/google-trends-scraper)**: Google Trends interest over time, by region and rising queries for any number of keywords, plus trending searches.
- **[PDF to Markdown OCR & Table Extraction](https://apify.com/aurelix/pdf-to-markdown)**: Turns linked PDF and Word files into Markdown per page, with tables as JSON rows, RAG chunks and OCR.
- **[Website to Markdown API](https://apify.com/aurelix/website-to-markdown)**: Crawls a site and returns one row of clean main-content Markdown per page, with title, headings and links.

### Recent changes

- **0.1**: Price cut to $1.50 per 1,000 jobs ($0.90 on paid plans) from $2.00 / $1.40; retitled; comparison with the leading LinkedIn and multi-board job scrapers added.

The full history is on the **Changelog** tab.

### Tested daily

An automated health check runs this Actor every day on a fixed input and checks that at least 7 jobs come back with `job_id`, `source`, `title`, `company`, `job_url`, `posted_date` filled in; a failed check alerts the maintainer straight away.

### Your feedback

Missing a field, found a bug or need another filter? Open an issue on the **Issues** tab; we aim to answer within a day. If this Actor saved you time, a short review helps other people find it.

# Changelog

This Actor's version history is a separate document: https://apify.com/aurelix/multi-job-board-scraper/changelog.md

# Actor input Schema

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

Job title words, e.g. \['data engineer', 'product manager']. LinkedIn runs one search per entry; career sites and remote boards keep jobs whose title (or company, for remote boards) contains an entry.

## `country` (type: `string`):

Country to search, e.g. 'CA'. LinkedIn and career-site jobs located in another country are dropped; remote-board jobs must be open to this country or worldwide.

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

City inside the country, e.g. 'Toronto, ON'. LinkedIn searches there; career-site jobs must mention the city (remote jobs are kept). Empty = the whole country.

## `remoteOnly` (type: `boolean`):

Only remote jobs, e.g. true. Remote-board jobs are always remote.

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

Only jobs posted within this window. Jobs whose posting date is not published are kept.

## `sources` (type: `array`):

Where to search: 'linkedin' (LinkedIn public job search), 'careersites' (public Greenhouse, Lever, Ashby and SmartRecruiters boards: the bundled company list plus 'companies'), 'remoteboards' (Remote OK and We Work Remotely). Default = all three.

## `companies` (type: `array`):

Company job boards to add to the career-site search: a board URL, 'greenhouse:stripe', or just a slug like 'shopify' (the ATS is detected). Empty = only the bundled list.

## `starterList` (type: `string`):

Bundled list of verified company boards to search: 'all' (about 50 Canadian and global employers), 'canada' (Canadian-headquartered only) or 'none' (only 'companies').

## `maxBoards` (type: `integer`):

Upper limit of bundled company boards searched per run (home-country employers first); boards you list in 'companies' are always searched. 0 = the whole bundled list (about 50 boards, slower), e.g. 25.

## `maxResultsPerSource` (type: `integer`):

Upper limit of jobs returned from each source (LinkedIn, career sites, remote boards), after cross-source dedupe, newest first within each batch, e.g. 50. Jobs are saved in batches while the run is going, so a stopped run keeps what it found.

## `includeDescription` (type: `boolean`):

Full job description as text and HTML. Off = faster, smaller rows.

## `incremental` (type: `boolean`):

Return only jobs not returned by a previous run with the same search, matched across boards. Use with a schedule for a daily feed.

## `stateKey` (type: `string`):

Name for the 'new since last run' memory, e.g. 'toronto-data'. Leave empty to derive it from the search.

## `proxyConfiguration` (type: `object`):

Proxy for LinkedIn. Apify's datacenter proxy works. Career sites and remote boards are public APIs and feeds and need none.

## `slackWebhookUrl` (type: `string`):

After each run, post a short digest to Slack: how many results were saved (new ones only when 'Only new jobs since last run' is on) and the top 10 with links, e.g. https://hooks.slack.com/services/T000/B000/XXXX. Nothing is posted when the run saved nothing. Leave empty to skip.

## `webhookUrl` (type: `string`):

After each run, POST the same digest as JSON (actor, newOnly, count, items = top 10 results, runUrl, text) to any URL, e.g. a Zapier, Make or n8n webhook. Nothing is sent when the run saved nothing. Leave empty to skip.

## Actor input object example

```json
{
  "keywords": [
    "data engineer"
  ],
  "country": "US",
  "location": "",
  "remoteOnly": false,
  "datePosted": "pastWeek",
  "sources": [
    "linkedin",
    "careersites",
    "remoteboards"
  ],
  "companies": [],
  "starterList": "all",
  "maxBoards": 10,
  "maxResultsPerSource": 5,
  "includeDescription": true,
  "incremental": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

Deduplicated jobs from every selected board, one item per job.

# 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 = {
    "keywords": [
        "data engineer"
    ],
    "maxBoards": 10,
    "maxResultsPerSource": 5,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("aurelix/multi-job-board-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 = {
    "keywords": ["data engineer"],
    "maxBoards": 10,
    "maxResultsPerSource": 5,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("aurelix/multi-job-board-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 '{
  "keywords": [
    "data engineer"
  ],
  "maxBoards": 10,
  "maxResultsPerSource": 5,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call aurelix/multi-job-board-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,aurelix/multi-job-board-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/1vRso7C485nO3hsrY/builds/eFFVVjOkHKacwlkYD/openapi.json
