# Job Board Scraper & API — LinkedIn, Indeed, Glassdoor + 29 More (`spool/job-board-api`) Actor

Job board API across 33 sources: LinkedIn, Indeed, Glassdoor, Naukri, Amazon, Workday, Welcome to the Jungle, remote job feeds, SWE internships and new grad jobs, 17 employer ATS platforms. Deduplicated, salaries in USD, 190 technologies, a hiring profile per company, new-jobs monitor.

- **URL**: https://apify.com/spool/job-board-api.md
- **Developed by:** [Spool](https://apify.com/spool) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 19 total users, 5 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $3.50 / 1,000 jobs

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

**A job board scraper and API in one: one search across LinkedIn, Indeed, Glassdoor and 30 more sources, with the
employer's own posting attached to every row it can find it for.** Full
descriptions where the boards withhold them, salaries annualised in USD, 190
technologies read from the text, job category, employment type, years of experience, visa sponsorship and education on every row, duplicates merged across boards, and one hiring
profile per company. Rows arrive in waves while the slower boards are still
answering.

```
Software Development Engineer II   Amazon       $143,700–$213,800   Engineering · mid     indeed  +linkedin
Embedded Software Engineer         General Dyn. $115,252–$121,554   Engineering · mid     indeed
Staff Platform Engineer            Stripe       $190,000–$260,000   Engineering · lead    linkedin → greenhouse
```

**What a run costs.** $0.005 per job row and $0.005 per company profile, so
**1,000 jobs are $5**, less on paid plans. The default run below — 57 jobs, every
one with a description, 54 with a salary, plus 43 company profiles — costs about
**$0.50** and finishes in 20 to 40 seconds. Duplicates, empty boards and
companies without a public board cost nothing.

**Try it in one minute**

1. Press **Try for free** and keep the prefilled input, or paste this:
   ```json
   { "searchTerms": ["software engineer"], "location": "New York, NY" }
   ```
2. Press **Start**. Indeed, LinkedIn and Glassdoor are searched, every company found is looked up on its own job board, and the descriptions LinkedIn and Glassdoor withhold are read from the boards' own job details.
3. Open the **Output** tab: the *Jobs*, *Companies* and *Salaries* views each show their own columns. Export as JSON, CSV or Excel, or read the dataset from the API.

Measured on the platform, 7 October 2026, build 1.6: 57 jobs, 57 with a
description, 54 with a salary, 43 company profiles, $0.50; 20.8 seconds run
alone, 38.5 seconds with three runs sharing the proxy.

***

### What you get

Every job row carries the posting as the board gave it and what the Actor added:

- **The posting** — title, company, location, direct and board URLs, `applyUrl` (the employer's own page when found), description with `descriptionSource` (board, ats, page, linkedin-page, glassdoor-page), posting date and `postedDaysAgo`, job type, remote flag.
- **Salary, three ways** — as stated, annualised in its currency, annualised in USD. `salarySource` says whether it came from the board, the employer's board, the employer's job page or the text of the posting.
- **Classification** — `department`, `seniority`, `tech` (190 technologies) and `techCategories`, `city`, `region`, `country` (ISO code), `locationType`.
- **The employer's posting** — `atsProvider`, `atsBoardUrl`, `atsDepartment`, `atsPostedAt` when the company's own board was found.
- **Bookkeeping** — `site` and `alsoOn` (every board the posting appeared on), `searchTerm`, `dedupeKey`, `firstSeenAt` in monitor mode.

Company rows (`recordType: company`) hold open roles found, boards posted on, the ATS and its total open roles, department and seniority mix, top technologies, salary range in USD, remote share. Notice rows say in plain words why a run returned nothing. The full field list, with types, is on the Output tab and in the dataset schema the Actor ships with.

***

### Where it reads: 33 sources

**Job boards** — Indeed (salary and description on every row), LinkedIn (global), Glassdoor (salaries, company ratings), Naukri (India), Bayt (Middle East), BDJobs (Bangladesh). Indeed, LinkedIn and Glassdoor go through Apify's residential proxy because they refuse datacenter addresses; the others go direct.

**Free job APIs** — Remotive, RemoteOK, Arbeitnow (Europe), Jobicy, Himalayas, Working Nomads, We Work Remotely, the monthly *Ask HN: Who is hiring?* thread, and **Welcome to the Jungle** (Europe, 10,000+ postings with salary fields). No key, no proxy. The remote ones join a remote search automatically.

**Early-career list** — the daily-updated [speedyapply/2027-SWE-College-Jobs](https://github.com/speedyapply/2027-SWE-College-Jobs) list of software-engineering internships and new-grad roles: 2,717 roles on 4 October 2026, 238 with a stated salary, every one with a direct apply link. Select `collegejobs`.

**Employers' own boards, 17 platforms** — Greenhouse, Ashby, Lever, SmartRecruiters, Rippling, Breezy, Personio, Teamtailor and Pinpoint are found by company name; **Workday**, Oracle Cloud HCM, SAP SuccessFactors, Phenom career sites, Eightfold career sites, BambooHR and Recruitee from a careers URL (Indeed hands back the Workday URL on most enterprise postings, so those boards get read too); **Amazon's** careers site by name. Workday is where the large enterprises live: Nvidia's board holds 1,719 roles, Salesforce's 833.

**A verified employer list** — 1,129 well-known employers' boards on 17 platforms, from Greenhouse and Ashby start-ups to the Workday, Oracle, SuccessFactors, Phenom and Eightfold sites of Lockheed Martin, Capital One, JPMorgan Chase and Nvidia (68,177 open roles on 9 October 2026), that `searchEmployerBoards` can ask for your terms directly.

Checked and left out, so you know: ZipRecruiter (HTTP 403) and Google Jobs (no data) were dropped from the options in 1.6; Workable, The Muse, Jobvite, iCIMS and Dover refuse plain requests.

***

### The employer's posting, attached to the board's row

LinkedIn returns a title, a company and a location, and no description. Glassdoor the same. Every scraper built on them either leaves the description empty or pays for a second request per job, which is the request those boards rate-limit.

This Actor fills the gap twice over. For every company a search turns up it finds the company's own board, by name or from the direct URL the aggregator already gave, and attaches the employer's posting: the full description, a direct apply URL, the department as the employer labels it, the real posting date. For rows still without text it reads the board's own job detail, LinkedIn's public job fragment or Glassdoor's detail query, through paced residential sessions. Measured on the default run: 21 of 42 companies had a public board, 13 rows received the employer's posting, and 57 of 57 rows ended with a description (LinkedIn 19 of 19, Glassdoor 20 of 20 from the boards' details).

A company with no public board stays as the aggregator gave it, and `descriptionSource` tells you which case you have.

***

### Salaries you can compare

Boards report "$45/hour", "₹12L/year" and "€4,200/month" as bare numbers with an interval and a currency. Every salary here is reported as stated, **annualised** in its currency (hourly × 2,080, monthly × 12), and **annualised in USD**; when no exchange rate is available the USD fields are null, never a guess. `minSalaryUsd` and the per-company range both use the USD figure, so a London role and an Austin role are judged on one scale.

**Pay ranges stated in the text.** US pay-transparency laws make employers print the range in the description, and the ATS platforms pass that text through without a field. The Actor reads it back out conservatively: a figure counts only when it carries a currency, sits in a plausible band for its interval and either forms a range or stands next to a pay word; years, requisition IDs, percentages, stipends and company finances are rejected. Dot-thousands ("60.000 €"), k-suffixes ("$150–200k") and lakhs ("12–18 LPA") are understood. Measured: 13 of 57 default-run rows gained a range this way; 48 of 50 on a snippet audit. Every run writes `SALARY_TEXT_SAMPLE` to its key-value store so you can check the extractor's work.

***

### Deduplicated, located, classified

The same posting appears on LinkedIn, Indeed and Glassdoor at once. Rows are matched on normalised title + company + city; the richest copy is kept, missing fields are filled from the others, and `alsoOn` lists every board it appeared on. Title variants ("Sr. Software Engineer II (Remote)" and "Senior Software Engineer 2"), requisition IDs, company suffixes (Inc, Ltd, GmbH) and city spellings ("NYC", "New York, United States") all collapse to one key. Each run writes `MERGE_SAMPLE` with the first sixty kept/dropped pairs; 30 of 30 inspected merges were the same posting.

Every row gets `city`, `region` (US state or Canadian province code, or the region as written), `country` (ISO 3166-1 alpha-2) and `locationType` (onsite, hybrid, remote), so you filter by country without regexes. Department and seniority come from the title, the employer's own department label winning where the ATS supplies one; `tech` lists the technologies named in the posting, from 190 recognised, and `requireTech` turns a search into a shortlist.

***

### One hiring profile per company

A new job posting is a buying signal: a company posting five SDR roles needs sales tooling, one hiring three Snowflake engineers is standing up a data platform. Each company found gets one row:

```json
{
  "recordType": "company", "company": "Stripe", "openRolesFound": 7,
  "atsProvider": "greenhouse", "atsOpenRoles": 716, "boards": ["linkedin", "indeed", "greenhouse"],
  "departments": { "Engineering": 5, "Product": 2 }, "seniorityMix": { "senior": 4, "lead": 2, "mid": 1 },
  "topTech": ["Kubernetes", "Go", "Kafka", "Snowflake"], "salaryMinUsd": 165000, "salaryMaxUsd": 290000, "remoteShare": 0.43
}
```

`atsOpenRoles` is the whole board, not just what matched your search: the honest size of their hiring, from the employer's own count.

***

### Rows arrive in waves

Since 1.6 the run delivers rows as soon as nothing more can be added to them, instead of everything at the end. Employer boards and free feeds arrive complete and go out first, usually within a few seconds; the board rows follow once the employer-board match and the description reads are done, and those three stages now run at the same time instead of one after another. Measured on the platform: a remote search delivered its first 15 rows at 3 seconds and all 66 at 40; 150 employer boards delivered 243 postings at 15 seconds (32 before); the default run finished in 20.8 seconds alone and 38.5 with three runs sharing the proxy (67 before). A copy of a row that already went out is dropped rather than merged, and its board is still counted on the company's profile. `RUN_SUMMARY.waves` lists each wave with its row count and the second it landed.

***

### Monitor mode: only what is new since the last run

Turn on `onlyNew` and the search remembers every posting it has delivered. On a schedule, each run returns only postings that were not there before: a feed of new jobs, nothing delivered twice, nothing charged twice.

- Memory is one record per search in a key-value store named `job-board-api-monitor` **in your own account**. Nothing leaves your storage.
- The record is named from the search itself, so the same search always shares one memory. Set `monitorName` to share memory between searches or to start over.
- Only rows actually delivered are remembered; rows held back by a spending limit stay new. Postings not seen for 60 days are forgotten, so a role closed and reposted later shows up again.

```json
{ "searchTerms": ["machine learning engineer"], "location": "Remote", "onlyNew": true }
```

Measured: first run 6 jobs from two feeds in 1.5 seconds; the same input again minutes later, 0 new, 6 already seen, one notice row, nothing charged but the $0.002 Actor start. Boards rotate their top results, so a repeat an hour later usually finds a handful of genuinely new rows.

**Run it on a schedule in three steps.** Open the public task [daily-new-jobs-monitor](https://apify.com/spool/job-board-api/tasks/daily-new-jobs-monitor), press **Save as task** and set your terms and location. In the task, open **Schedules** and pick *every day* or *every hour*. Then open **Integrations** and connect Slack, Google Sheets, Airtable or a webhook: each run delivers exactly the new postings, and a day with nothing new costs the Actor start.

***

### Employer-direct search

`searchEmployerBoards: 100` asks the hundred largest boards in the bundled, verified list of 1,129 well-known employers on 17 platforms (Lockheed Martin, Capital One, JPMorgan Chase, Nvidia, Databricks, OpenAI, Stripe, Anthropic, Datadog, Airbnb…) for your search terms directly; `searchEmployerBoards: 800` covers the whole list. No aggregator in between: the full description, the employer's own department label, the real posting date, the direct apply URL and a hiring profile per company. A role matches when the whole term appears in its title or every word of a multi-word term does.

```json
{ "searchTerms": ["machine learning engineer"], "boards": [], "searchEmployerBoards": 150 }
```

Measured: 150 boards asked, 243 postings delivered at 15 seconds, 241 with a description, 184 with a salary, 55 company profiles.

***

### Deep mode

| knob | default | deep |
|---|---|---|
| `locations` | one | up to **10 locations** in one run, deduplicated together |
| `resultsPerBoard` | 20 | up to **500** per board per term per location |
| `searchEmployerBoards` | 0 | up to **400**, the whole verified employer list |
| `maxDescriptionsPerBoard` | 60 | up to **500** descriptions from one Workday or Eightfold board |
| `maxJobPageFetches` | 40 | up to **300** employer pages and board details read per board |
| `maxAtsLookups` | 50 | up to **200** companies resolved to their boards |

**A national sweep**, measured with 1.5: one term, three cities, three boards, 100 per board: 736 deduplicated jobs (61 duplicates merged), 676 with a description, 631 with a salary, 240 companies profiled, in 341 seconds.

```json
{ "searchTerms": ["software engineer"], "locations": ["San Francisco, CA", "Austin, TX", "Seattle, WA"], "resultsPerBoard": 100, "maxAtsLookups": 200, "maxJobPageFetches": 300 }
```

**A whole enterprise board**, measured: Nvidia's Workday board, 300 descriptions requested: 267 roles with full descriptions in 196 seconds.

```json
{ "companies": ["https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite"], "boards": [], "maxDescriptionsPerBoard": 500 }
```

Deep runs take minutes; you pay per deduplicated row delivered, so a sweep that finds the same posting in five cities bills it once.

***

### Recipes

**Remote senior data roles posted this week, with salaries over $150k**

```json
{ "searchTerms": ["senior data engineer", "staff data engineer"], "location": "Remote", "remoteOnly": true, "postedWithinHours": 168, "requireSalary": true, "minSalaryUsd": 150000 }
```

**Who is hiring for Snowflake in London**

```json
{ "searchTerms": ["data engineer", "analytics engineer"], "location": "London", "indeedCountry": "uk", "requireTech": ["Snowflake"] }
```

**A named company's whole board, straight from the employer**

```json
{ "companies": ["stripe.com", "https://jobs.ashbyhq.com/ramp", "Datadog"] }
```

**An enterprise on Workday**: 173 matching Nvidia roles in 42 seconds, measured.

```json
{ "companies": ["https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite"], "searchTerms": ["software engineer"], "boards": [] }
```

**Amazon, by name**: 59 matching roles with descriptions in 8 seconds, measured.

```json
{ "companies": ["Amazon"], "searchTerms": ["software development engineer"], "location": "New York, NY", "boards": [] }
```

**Internships and new-grad roles with salaries**: 99 roles in 7 seconds, every one with a stated salary, measured.

```json
{ "searchTerms": ["software engineer intern"], "boards": ["collegejobs"], "resultsPerBoard": 100 }
```

**Remote roles from every free feed at once**

```json
{ "searchTerms": ["backend engineer"], "location": "Remote", "remoteOnly": true, "boards": ["remotive", "remoteok", "jobicy", "himalayas", "workingnomads", "weworkremotely", "hackernews"] }
```

**Cheapest possible daily feed**

```json
{ "searchTerms": ["product manager"], "location": "Austin, TX", "boards": ["indeed"], "postedWithinHours": 24, "enrichFromAts": false, "companyRollup": false }
```

***

### Every option

The form shows five fields; everything else sits in collapsed sections.

| Option | Default | What it does |
|---|---|---|
| **`searchTerms`** | — | Up to 5 queries, merged; each row records which one found it |
| **`location`** | — | City, region, country or `Remote` |
| **`boards`** | indeed, linkedin, glassdoor | Also naukri, bayt, bdjobs and the free APIs remotive, remoteok, arbeitnow, jobicy, himalayas, workingnomads, weworkremotely, hackernews, wttj, collegejobs |
| **`companies`** | — | Employers to read directly from their own board: names, domains or board URLs |
| `resultsPerBoard` | `20` | 1–500 per board per term per location, before deduplication |
| *Enrichment* | | |
| `enrichFromAts` | `true` | Match aggregator rows to the employer's own posting |
| `fetchAggregatorPages` | `true` | Read LinkedIn's and Glassdoor's own job details for rows still without a description |
| `fetchJobPages` | `true` | Read the employer's job page (JobPosting JSON-LD) for rows still without a description |
| `companyRollup` | `true` | One hiring-profile row per company |
| *Filters* | | |
| `remoteOnly` / `linkedinEasyApply` | `false` | Board filters |
| `postedWithinHours` | — | Posted within N hours |
| `jobType` | — | fulltime, parttime, contract, internship, temporary |
| `requireSalary` | `false` | Keep only rows with a salary, including salaries that came from the employer's board or the text |
| `minSalaryUsd` | — | Annualised USD floor |
| `requireTech` | — | Keep only rows naming these technologies |
| `indeedCountry` | `usa` | National Indeed and Glassdoor site |
| *Monitor mode* | | |
| `onlyNew` | `false` | Deliver and charge only postings this search has not delivered before |
| `monitorName` | derived | Name of the memory record; set it to share or reset (`monitorKey` still accepted) |
| *Deep mode* | | |
| `locations` | — | Up to 10 more locations in the same run |
| `searchEmployerBoards` | `0` | Ask the N largest boards in the bundled employer list for the terms (0–400) |
| `maxAtsLookups` | `50` | Companies to look up per run, up to 200 |
| `maxJobPageFetches` | `40` | Cap on page and board-detail reads, per board, up to 300 |
| `maxDescriptionsPerBoard` | `60` | Descriptions fetched per Workday or Eightfold board, up to 500 |
| *Advanced* | | |
| `autoRemoteFeeds` | `true` | Remote searches also pull the free remote feeds unless you picked feeds yourself |
| `linkedinDescriptions` | `false` | LinkedIn's own per-job fetch; unnecessary since the board details are read |
| `linkedinCompanyIds` | — | Restrict LinkedIn to company IDs |
| `descriptionFormat` | `markdown` | or `html`, for board descriptions |
| `radiusMiles` | `50` | Miles around the location |
| `offset` | `0` | Pagination |
| `useApifyProxy` | `true` | Apify Proxy, residential by default, for Indeed, LinkedIn and Glassdoor |
| `proxiedBoards` | indeed, linkedin, glassdoor | Which boards go through the proxy |
| `proxyConfiguration` | residential | Another proxy group, or your own proxy URLs |

***

### How much does it cost?

Pay per event: per deduplicated job row delivered and, with `companyRollup` on, per company profile. Rows merged as duplicates, boards that return nothing and companies without a public board cost nothing. Actor start is $0.002.

| Plan | per job | per company profile |
|---|---|---|
| Free | $0.005 | $0.005 |
| Bronze | $0.0045 | $0.0045 |
| Silver | $0.004 | $0.004 |
| Gold | $0.0035 | $0.0035 |

- The default run, measured: 57 jobs + 43 profiles = **$0.50** (Gold: $0.35)
- **1,000 jobs** = **$5.00**; with profiles at the measured ratio of about seven companies per ten jobs, $8.50 (Gold: $5.95)
- Indeed only with `companyRollup: false`, 100 jobs = **$0.50**
- Monitor mode on a daily schedule: only new postings are charged; a day with nothing new costs the $0.002 start
- The free plan's $5 monthly credit covers roughly **1,000 jobs** without profiles

Platform usage (compute, proxy) is billed by Apify separately and is small: a three-board run measured $0.0002 of proxy traffic, because the boards answer with compact JSON.

***

### Use it from an AI agent, the API or an integration

**MCP.** Add the Apify MCP server to Claude, Cursor or any MCP client and the agent can call this Actor as a tool: *"Find remote staff engineer roles posted this week that mention Kubernetes, and tell me which companies are hiring the most."*

```
https://mcp.apify.com/?tools=spool/job-board-api
```

The server exposes a tool named `spool-slash-job-board-api` whose arguments are exactly this Actor's input; the generic `call-actor` form works too:

```json
{ "tool": "call-actor", "arguments": { "actor": "spool/job-board-api", "input": { "searchTerms": ["staff engineer"], "location": "Remote", "postedWithinHours": 168, "requireTech": ["Kubernetes"] } } }
```

**API.** `POST https://api.apify.com/v2/acts/spool~job-board-api/run-sync-get-dataset-items?token=…` with the input as JSON returns the rows in one call. **Python and JavaScript:** `client.actor("spool/job-board-api").call(run_input={...})` with `apify-client`. **Schedules and integrations:** run it daily with `onlyNew` and push the rows to Google Sheets, Airtable, Slack, HubSpot or a webhook from the Integrations tab; filter on `recordType` to route jobs and companies separately.

***

### What it covers, honestly

- **Boards fail, the run does not.** Each source runs isolated with its own timeout; one that errors or returns nothing is reported in the log and in `RUN_SUMMARY.boardHealth` while the others deliver.
- **Three boards will not talk to Apify's own addresses.** Indeed refuses the connection, Glassdoor answers 403 through the datacenter group, LinkedIn rate-limits by IP. Through the residential proxy all three answer, for about $0.0002 of traffic per run. Without residential access the Actor falls back to the default group, then to direct, and the log says what happened.
- **Filter combinations the boards reject.** LinkedIn cannot combine `postedWithinHours` with `linkedinEasyApply`; Indeed cannot combine `postedWithinHours` with `jobType`, `remoteOnly` or `linkedinEasyApply`. The board silently drops one.
- **Descriptions depend on the source.** Indeed: in the listing. LinkedIn and Glassdoor: from the employer's board or the board's own job detail, within the per-board cap; one of the eight proxy sessions is usually rate-limited by Glassdoor after a dozen requests and retired. Bayt, Naukri, BDJobs: none. Measured coverage: default run 57 of 57; deep run 676 of 736.
- **ATS matching is by title and city.** A posting whose title differs from the aggregator's ("SWE II" vs "Software Engineer II") is left unmatched rather than guessed. Rippling's and Breezy's list APIs carry no text, so a match there adds the apply URL and department only.
- **The free APIs are remote-first and English-first**; excellent for remote and tech roles, thin for on-site roles in Lyon.
- **Salary coverage is the board's.** Indeed and Glassdoor state salaries on most US rows; LinkedIn rarely; Bayt almost never.
- **If your spending limit cuts a run short**, rows beyond it are not delivered and not charged, a notice row says how many were held back, and in monitor mode they stay new for the next run.
- **Tested.** 63 offline tests (row typing against the dataset schema, salary extractor, location parser, dedupe keys, wave delivery) and 7 live smoke tests ship in `tests/`; Apify runs the default input daily.

***

### Is this legal?

The job boards collect public job postings only: the same search results any visitor sees, no login, no candidate or recruiter profiles, no paywall bypass. The employer boards are different in kind: Greenhouse, Ashby, Lever and SmartRecruiters publish public JSON APIs intended for exactly this use, which is why that part needs no proxy. Job listings are business information published to be found; handle any personal data a description happens to contain according to the law that applies to you.

***

### FAQ

**Is there a LinkedIn jobs API, an Indeed API or a Glassdoor API?** Not a public one: LinkedIn's is partner-only, Indeed retired its Publisher API, Glassdoor's is closed. This is a job board API over their public listings plus the employers' own ATS APIs, which are public.

**How is this different from other job scrapers?** The employer's own posting attached to aggregator rows, descriptions on every LinkedIn and Glassdoor row without their rate-limited per-job fetch, salaries annualised in USD, a hiring profile per company, monitor mode for schedules, and rows delivered in waves rather than at the end.

**How fast is it?** Free feeds and employer boards land in seconds; the default three-board run finishes in 20 to 40 seconds; deep runs take minutes. `RUN_SUMMARY.waves` shows when each wave landed.

**How many jobs can one run return?** Up to 500 per board per term per location across ten locations, plus whole employer boards (Nvidia's alone is 1,719 roles). Paginate with `offset` for more.

**Which countries?** LinkedIn is global. Indeed and Glassdoor take `indeedCountry` (usa, uk, canada, australia, germany, france, india, singapore, uae and most others). Naukri covers India, Bayt the Middle East, BDJobs Bangladesh, Welcome to the Jungle Europe. The employer boards are wherever the employer is.

**Why fewer rows than I asked for?** Fewer matches, duplicates merged, or a filter combination a board rejects. The log shows per-board counts and `RUN_SUMMARY.duplicatesRemoved` the merges.

**Can I use it for lead generation?** Yes: the company rows are built for it. A company posting several roles in one department, with its tech stack and salary band, is a qualified signal, and monitor mode turns it into a daily feed.

***

### Changelog

**1.8.0 — 2026-10-11.** Every row now carries `jobCategory` (30 categories), `employmentType`, `yearsExperienceMin` and `yearsExperienceMax`, `visaSponsorship` (true, false or not stated) and `educationLevel`, read from the title, the board's own labels and the posting text; a new *Experience, visa, education* view. Plain input names: `boards`, `postedWithinHours`, `radiusMiles`, `remoteOnly`, `linkedinEasyApply`, `linkedinDescriptions`, `indeedCountry`, `proxiedBoards` (the old names are still read). The verified employer list grows to 1,129 boards with 68,000 open roles; employment type comes from the platform's own field on Ashby, Lever, Workday and SmartRecruiters. Title now says what it is: a job board scraper and API.

**1.7.0 — 2026-10-10.** Companies named in `companies` that have no board under their name are now found through their careers page (website → careers link → the system behind it, six requests and ten seconds at most) and read; Oracle Cloud HCM, SAP SuccessFactors (all three layouts) and Phenom career sites readable, plus Eightfold's personalised and custom-domain sites; the login-only SuccessFactors portal recognised. The verified employer list is rebuilt from a 1,000-company benchmark (478 found, 48%): 1,129 boards on 17 platforms, 68,177 open roles (was 307 boards on 4 platforms); `searchEmployerBoards` goes to 800 and reads every platform in the list. Slug guesses run on every platform at once; generic words (careers, jobs…) are never guessed as slugs.

**1.6.0 — 2026-10-07.** Rows arrive in waves: employer boards and feeds first, board rows once enrichment is done; ATS lookups start as each board returns and run alongside the page reads. Default run 21–39s (was 67s), 150 employer boards 15s (was 32s), remote search first rows at 3s. Input form reorganised into five visible fields plus collapsible sections; ZipRecruiter and Google Jobs, which returned nothing, removed from the options; `monitorKey` renamed `monitorName` (old name still read). README rewritten around what a run returns and costs.

**1.5.0 — 2026-10-05.** Board pages: LinkedIn and Glassdoor descriptions read from the boards' own job details through paced residential sessions. Pay ranges read from posting text. `city`, `region`, `country`, `locationType` on every row. Deduplication hardened. Test suite. Default run 59/59 descriptions (was 31), 52/59 salaries (was 41).

**1.4.0 — 2026-10-05.** Deep mode: `locations`, `resultsPerBoard` to 500, `searchEmployerBoards` to 400, `maxDescriptionsPerBoard` to 500. Employer pages (JobPosting JSON-LD). Welcome to the Jungle.

**1.3.0 — 2026-10-05.** Monitor mode (`onlyNew`). Employer-direct search over 307 verified boards. `dedupeKey` and `firstSeenAt` on every row.

**1.2.0 — 2026-10-04.** College jobs list, Amazon careers API, Eightfold career sites; Workday, Amazon and Eightfold asked for exact titles.

**1.1.0 — 2026-10-04.** Eight free job APIs; eight more ATS platforms including Workday from Indeed's direct URLs.

**1.0.0 — 2026-10-04.** Six boards via JobSpy; Greenhouse, Ashby, Lever and SmartRecruiters as source and as enrichment; deduplication; USD salaries; 190-technology detection; company profiles.

***

### Support

Open an issue on the **Issues** tab and you will get a reply, usually the same day. If a board changes and breaks, say so there; the per-board health in your run's `RUN_SUMMARY` is the fastest way to show it. If the Actor saved you time, a review on this page helps other people find it.

### More from spool

- **[Career Page Scraper & ATS Jobs API — 17 ATS Platforms, No Proxy](https://apify.com/spool/career-page-jobs-api)** — every open role straight from employers' own boards on 17 ATS platforms, by company name, domain or careers URL, on this engine
- **[Wellfound Jobs Scraper (AngelList) — Salary & Equity Data](https://apify.com/spool/angellist-jobs-scraper)** — startup roles from Wellfound with salary and equity
- **[Company Tech Stack API — Detected from Job Postings](https://apify.com/spool/company-tech-stack-api)** — a company's technologies read from its own job board
- **[YouTube Transcript Scraper — Subtitles, SRT/VTT & RAG Chunks](https://apify.com/spool/youtube-transcript-api)** — YouTube transcripts chunked for RAG, with timestamps
- **[SEC Filings API — Who Mentions What in EDGAR](https://apify.com/spool/sec-filings-api)** — SEC filings searched by term, with company and mention counts
- **[Shopify Stores API — Velocity, Pricing & Brand Model](https://apify.com/spool/shopify-stores-api)** — Shopify store analytics: product velocity, pricing bands, publish cadence
- **[Shopify Product Scraper — GTIN & Collections](https://apify.com/spool/shopify-product-feed)** — Shopify catalogs as a merchant feed with GTIN barcodes
- **[Shopify Scraper — Stores & Products](https://apify.com/spool/shopify-store-products-scraper)** — store analytics plus full catalogs with GTIN barcodes

# Actor input Schema

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

Up to five queries, one per line, run together and merged — `software engineer`, `data analyst`, `account executive`. Every row says which term found it.

Leave empty if you only want the **Companies** list below.

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

City, region or country — `New York, NY`, `London`, `Germany`, or `Remote`. LinkedIn is global; set **Indeed/Glassdoor country** below for those two.

## `boards` (type: `array`):

**Boards** — Indeed (salary and description on every row; via proxy from Apify's network), LinkedIn (global; descriptions filled from the employer's board or LinkedIn's own job detail), Glassdoor (salaries, company ratings), Naukri (India), Bayt (Middle East), BDJobs (Bangladesh).

**Free job APIs** — Remotive, RemoteOK, Arbeitnow (Europe), Jobicy, Himalayas, Working Nomads, We Work Remotely, the monthly *Ask HN: Who is hiring?* thread, **Welcome to the Jungle** (Europe, salaries) and **College jobs**, the daily-updated speedyapply list of software-engineering internships and new-grad roles (2,700+ rows, salaries on many). All answer a plain request with no key and no proxy; the remote ones join automatically when you search for remote roles.

Employers' own ATS boards are a separate layer — see **Companies** and *Enrichment*.

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

Employers to read **directly from their own board**: a name (`Stripe`), a domain (`ramp.com`), or a board URL. Greenhouse, Ashby, Lever, SmartRecruiters, Rippling, Breezy, Personio, Teamtailor and Pinpoint boards are found by name; **Workday** (most large enterprises), Eightfold career sites, BambooHR and Recruitee are found from a careers URL; **Amazon** is found by name — paste one like `https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite`.

These rows carry the full description, the department and the real posting date, and never hit a rate limit. If search terms are set, only matching roles are kept; otherwise the whole board is returned.

## `resultsPerBoard` (type: `integer`):

1–500. Total before deduplication = this × boards × search terms × locations. You pay only for rows actually delivered after deduplication. Deep runs: 500 per board on three boards across ten locations is up to 15,000 rows before merging — LinkedIn paginates to about 1,000 per query, Indeed and Glassdoor deeper.

## `enrichFromAts` (type: `boolean`):

**The reason to use this Actor.** For each company found on the boards, find its own ATS board and attach the employer's posting: a direct apply URL, the department, the real posting date, and — for LinkedIn rows, which arrive with no description — the full description, without paying for LinkedIn's per-job fetch.

Seventeen ATS platforms and career-site systems are recognised. Nine are found by company name; Workday, Oracle Cloud HCM, Eightfold, BambooHR and Recruitee are found from the job's own direct URL, and a named company's careers page is read for the system behind it, which Indeed and Glassdoor usually supply — so the large Workday enterprises are covered too. Measured on the platform's default run (4 Oct 2026): 9 of 34 companies had a board on the first four platforms and 12 LinkedIn rows received their description from it; the figure with twelve platforms is in the README.

## `fetchAggregatorPages` (type: `boolean`):

LinkedIn and Glassdoor list jobs without descriptions. With this on, rows from those two boards that still have none after the employer-board match get the board's own job detail read — LinkedIn's public job fragment, Glassdoor's detail query — through paced residential proxy sessions (one request every two seconds per session, a session retired at its first rate limit), capped by *Max employer pages per run* for each board. LinkedIn also yields seniority, employment type, job function and industry.

## `fetchJobPages` (type: `boolean`):

After the boards and the ATS layer, rows that still have no description but have a direct employer URL get that page fetched and its schema.org **JobPosting** data read — the block career sites publish for Google for Jobs: full description, posting date, employment type, remote flag and, when the employer states it, the salary (`salarySource: page`). One request per row, aggregator domains skipped.

## `companyRollup` (type: `boolean`):

One extra row per company: open roles found, which boards it posts on, its ATS, department mix, seniority mix, top technologies, salary range in USD, remote share. This is the lead-generation view — a company posting five SDR roles is a buying signal, and this is where you see it.

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

Passed to each board's own remote filter. Every row also carries an `remoteOnly` flag derived from the title and location, which catches postings the boards mislabel.

## `postedWithinHours` (type: `integer`):

24 = last day, 168 = last week. **Board quirks:** LinkedIn cannot combine this with Easy Apply; Indeed cannot combine it with job type, remote or Easy Apply — the board silently ignores one of them.

## `jobType` (type: `string`):

Employment type filter.

## `linkedinEasyApply` (type: `boolean`):

Quick-apply postings hosted on the board. See the LinkedIn/Indeed quirks under *Posted within*.

## `requireSalary` (type: `boolean`):

Keep rows with a salary from the board or the employer's ATS. Applied after deduplication and enrichment, so a LinkedIn row whose salary came from Greenhouse is kept.

## `minSalaryUsd` (type: `integer`):

Compared against the annualised USD figure, so an hourly US wage and a monthly EUR salary are judged on the same scale.

## `requireTech` (type: `array`):

Keep rows whose text names any of these — `Kubernetes`, `Snowflake`, `Salesforce`, `React`… Spelled as in the output's `tech` field. 190 technologies are recognised.

## `indeedCountry` (type: `string`):

Which national Indeed and Glassdoor site to search: `usa`, `uk`, `canada`, `australia`, `germany`, `france`, `india`, `singapore`, `uae`, `netherlands`, `spain`, `italy`, `brazil`, `mexico`, `japan`… LinkedIn ignores this.

## `onlyNew` (type: `boolean`):

Remember every posting this search has delivered and return only the ones not seen before. Put the run on a schedule — daily, hourly — and you get a feed of new postings; nothing delivered earlier is charged again. The memory is a record per search in a key-value store named `job-board-api-monitor` in **your** account (see *Monitor name*). Postings not seen for 60 days are forgotten, so a role closed and reposted later shows up again.

## `monitorName` (type: `string`):

Name of the memory record for this search. Leave empty and it is derived from the search itself (terms, location, sites, companies), so the same search always shares one memory. Set a name to share one memory between different searches, or change it to start fresh. It is a label, not a credential — it appears in your key-value store as the record key.

## `locations` (type: `array`):

Up to ten locations searched in the same run, one per line — `San Francisco, CA`, `Austin, TX`, `Seattle, WA`… Every board is asked for every term in every location and the results are deduplicated together. Combine with a higher *Results per board* for a full national sweep.

## `searchEmployerBoards` (type: `integer`):

Ask this many well-known employers' own boards for your search terms, straight from the source — no aggregator in between. The bundled, verified list of 1,129 employers covers 17 platforms (Greenhouse, Ashby, Lever, Workday, Oracle, SuccessFactors, Phenom, Eightfold…) with companies like Stripe, Airbnb, Anthropic, Databricks, Nvidia, Lockheed Martin and Capital One, largest board first. Rows carry the full description, the department and the real posting date. 0 = off; 100 boards take about a minute, 800 covers the whole list.

## `maxAtsLookups` (type: `integer`):

Each company costs one to four requests to find its board. 50 covers a typical run.

## `maxJobPageFetches` (type: `integer`):

Cap on the employer-page reads and on the LinkedIn page reads above, each. 40 covers a default run; raise it for deep runs.

## `maxDescriptionsPerBoard` (type: `integer`):

Workday and Eightfold boards need one request per posting for its description. 60 keeps a board under a minute; set 500 to read a whole enterprise board (Nvidia's holds 1,700+ roles) with every description.

## `autoRemoteFeeds` (type: `boolean`):

When the search is remote (Remote only is on, or the location says remote/anywhere/worldwide) and you have not picked any free feed yourself, Remotive, RemoteOK, Jobicy, Himalayas, Working Nomads and We Work Remotely join the run. Turn off to search the boards alone.

## `linkedinDescriptions` (type: `boolean`):

One extra LinkedIn request per job, which is what LinkedIn rate-limits. Usually unnecessary: the employer-board enrichment fills the description from the company's own ATS. Turn this on only for companies with no public ATS.

## `linkedinCompanyIds` (type: `array`):

Numeric LinkedIn company IDs, e.g. `1441` (Google).

## `descriptionFormat` (type: `string`):

How board descriptions are returned. Employer-board descriptions are plain text.

## `radiusMiles` (type: `integer`):

Around the location, where the board supports it.

## `offset` (type: `integer`):

Pagination: run again with an offset to go deeper on a board.

## `useApifyProxy` (type: `boolean`):

Indeed refuses connections from Apify's own addresses, Glassdoor rejects datacenter proxies, LinkedIn rate-limits by IP. With this on, those three boards go through Apify Proxy — **residential by default**, the only group all three accept — with eight sticky sessions. Measured proxy traffic for a three-board run: about $0.0002. Naukri, Bayt, BDJobs, the free APIs and the employer boards go direct; they do not need it.

## `proxiedBoards` (type: `array`):

Which boards use the proxy when it is on. The default is the three that need it.

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

Optional. Override the proxy group (datacenter is cheaper but Glassdoor rejects it) or supply your own proxy URLs.

## Actor input object example

```json
{
  "searchTerms": [
    "data engineer",
    "analytics engineer"
  ],
  "location": "New York, NY",
  "boards": [
    "indeed",
    "linkedin",
    "glassdoor"
  ],
  "companies": [
    "stripe.com",
    "https://boards.greenhouse.io/airbnb"
  ],
  "resultsPerBoard": 20,
  "enrichFromAts": true,
  "fetchAggregatorPages": true,
  "fetchJobPages": true,
  "companyRollup": true,
  "remoteOnly": false,
  "jobType": "",
  "linkedinEasyApply": false,
  "requireSalary": false,
  "indeedCountry": "usa",
  "onlyNew": false,
  "locations": [
    "San Francisco, CA",
    "Austin, TX",
    "Seattle, WA"
  ],
  "searchEmployerBoards": 0,
  "maxAtsLookups": 50,
  "maxJobPageFetches": 40,
  "maxDescriptionsPerBoard": 60,
  "autoRemoteFeeds": true,
  "linkedinDescriptions": false,
  "descriptionFormat": "markdown",
  "radiusMiles": 50,
  "offset": 0,
  "useApifyProxy": true,
  "proxiedBoards": [
    "indeed",
    "linkedin",
    "glassdoor"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

Job rows with enrichment (the `jobs` view: title, company, location, seniority, department, salary in USD, boards, apply link).

## `companies` (type: `string`):

One row per company: open roles, boards, ATS, department and seniority mix, top technologies, salary range.

## `salaries` (type: `string`):

Jobs with a stated salary, annualised and converted.

## `runSummary` (type: `string`):

Per-board rows returned, time and errors; ATS lookups; duplicates removed.

# 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": [
        "software engineer"
    ],
    "location": "New York, NY",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("spool/job-board-api").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": ["software engineer"],
    "location": "New York, NY",
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("spool/job-board-api").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": [
    "software engineer"
  ],
  "location": "New York, NY",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call spool/job-board-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,spool/job-board-api"
        }
    }
}
```

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/4bmbVU1p27yl1okXO/builds/Xt9mrjJIbmSCLeNT7/openapi.json
