# BambooHR Jobs API - Career Page Scraper for Small Employers (`starbright_overlap/bamboohr-jobs-scraper`) Actor

BambooHR jobs API and career page scraper. Read live postings from any BambooHR career site, or search thousands of known BambooHR employers in one run. Titles, locations, departments and direct apply links as JSON. Skews to small and mid-sized employers that large aggregators miss.

- **URL**: https://apify.com/starbright\_overlap/bamboohr-jobs-scraper.md
- **Developed by:** [Sulle H](https://apify.com/starbright_overlap) (community)
- **Categories:** Jobs, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## BambooHR Jobs API — 9,711 Company Career Pages

BambooHR is an HR platform used mostly by small and mid-sized employers, and its careers module
publishes an open JSON list for every company that turns it on.

Point it at a company, or leave the list empty and search a bundled registry of **9,711 live
BambooHR career pages** carrying **65,904 open postings**.

```json
{ "keywords": ["nurse"], "maxJobs": 200 }
```

```json
{ "companies": ["https://masalto.bamboohr.com"] }
```

No login, no cookies, no proxies, no API key.

### What you get

| Field | Description |
|---|---|
| `provider` | Always `bamboohr` — the same schema the multi-ATS Actors below emit |
| `company` / `companySlug` | Employer subdomain |
| `jobId` | BambooHR posting id — stable, so it works as a dedupe key across runs |
| `title` | Job title |
| `location` | City, state and country as the employer entered them |
| `department` | Department, from the employer's own org structure |
| `employmentType` | Full-time, part-time, internship, contract |
| `remote` | Inferred from the location string — the employer flag is never set. See below |
| `applyUrl` | Direct link to the posting |
| `scrapedAt` | When this row was read |

### What you do not get, and why

**No posting date, no description, no salary.** BambooHR splits its careers API in two: the list
endpoint returns titles and locations, and a separate per-job endpoint carries `datePosted`,
`description` and `compensation`.

Reading those would mean **one extra HTTP request per posting**. On a thousand-row run that is a
thousand extra requests against employers' servers, and you would pay for the time. So this Actor
reads the list endpoint only and returns `null` for the three fields rather than making the run
an order of magnitude slower and heavier.

`postedWithinDays` therefore keeps BambooHR rows rather than dropping them, the same as Workday
and Rippling. If you need dated rows with descriptions and pay, the platforms that publish all
three on the list endpoint are **Recruitee** and **Pinpoint** — both linked below.

### The remote flag is there, but nobody fills it

BambooHR gives employers an `isRemote` field and this Actor reports it directly when it is set.
In practice it never is: **across every posting measured in this registry, not one had it set.**
So `remote` comes back `null` — unknown, not "no" — unless the location string itself says remote,
in which case this Actor falls back to that.

If you need a work-arrangement field you can actually filter on, **Ashby** marks 85.1% of its rows
and **Recruitee** marks 100%. Both are linked below.

### Input

| Option | What it does |
|---|---|
| `keywords` | Keep only titles containing one of these. Case-insensitive. |
| `excludeKeywords` | Drop titles containing any of these, e.g. `senior`, `intern`. |
| `locations` | Keep only locations containing one of these. |
| `remoteOnly` | Falls back to the location string here, because the employer flag is never set. |
| `postedWithinDays` | Freshness filter. BambooHR rows have no date and are always kept. |
| `companies` | Subdomains or career page URLs. Leave empty to search all 9,711. |
| `maxBoards` | How many boards to scan, largest-first. |
| `maxJobs` | Hard cap on rows, so a run costs what you expect. Defaults to 150, which keeps a first run under a dollar. |

### Finding a company slug

The career page lives at `<slug>.bamboohr.com/careers`, so the slug is the subdomain. Paste the
whole URL and the Actor extracts it. Or run without `companies` and read the `companySlug` column.

### Recipes

**Small-employer coverage.** BambooHR skews smaller than Workday or Greenhouse, so this is where
roles at companies under a few hundred people show up — the ones large aggregators miss.

**Watch one company.** One slug, run on a schedule, diff `jobId` between runs.

**Feed a niche job board.** Every row carries the employer's own `applyUrl`, so your users apply at
the source rather than through a middleman.

### Pricing

Pay per result — you are charged per job row delivered. A board that fails or returns nothing costs
you nothing. Platform usage is included rather than billed on top.

### Beyond BambooHR

Companies move between applicant tracking systems, and most job-data projects need more than one:

- **[Career Site Job Feed](https://apify.com/starbright_overlap/ats-job-feed)** — the same engine
  across every platform in this family, in one run, with an identical output schema.
- **[New Job Alerts](https://apify.com/starbright_overlap/new-jobs-feed)** — the same coverage, but
  each run returns only what appeared since the previous run.
- **[Recruitee Jobs API](https://apify.com/starbright_overlap/recruitee-jobs-scraper)** — publish
  dates, structured pay and a real remote flag on every row.

### Notes

- **A dead slug never aborts the run.** Failures land in a `FAILED_BOARDS` record in the key-value
  store, with the reason for each.
- **`companySlug:jobId` is a stable key**, safe for detecting what opened and closed between runs.
- **No login, no proxies, no API key.** BambooHR publishes this endpoint openly. Companies that
  keep their careers module private are invisible to every scraper, including this one.

# Actor input Schema

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

Keep only jobs whose title contains at least one of these. Case-insensitive. Leave empty for every job.

## `excludeKeywords` (type: `array`):

Drop jobs whose title contains any of these, e.g. `senior`, `intern`.

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

Keep only jobs whose location contains one of these, e.g. `london`, `new york`, `germany`.

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

Keep only postings flagged remote by the ATS or with a remote-looking location.

## `postedWithinDays` (type: `integer`):

Freshness filter. Defaults to 90 days because some employers leave postings open for years — measured across the registry, 15% of Greenhouse and about half of the largest SmartRecruiters boards are over a year old. Set a large number such as 3650 to include everything. Postings with no publication date (most Workday rows) are always kept.

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

Board slugs, e.g. `lanesgroup`. Leave empty to search all 9,711 known bamboohr employers.

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

Boards are scanned largest-first, so a capped run still returns the most jobs. Raise it to sweep all 9,711 boards.

## `maxJobs` (type: `integer`):

Hard cap on results, so a run costs what you expect. Defaults to 150, which costs well under a dollar so you can try the feed without spending your free credit on it. Clearing the box falls back to that, so type a large number such as 500000 to sweep everything.

## `concurrency` (type: `integer`):

Higher is faster but more likely to hit ATS rate limits.

## `maxJobsPerCompany` (type: `integer`):

Keeps one huge employer from filling the whole run. Boards are scanned largest-first, so without this the first few boards use up the entire result budget. Raise it when you want depth on a few employers rather than breadth across many. Lower values spread a capped run across more employers rather than filling it from the largest board.

## Actor input object example

```json
{
  "keywords": [
    "engineer"
  ],
  "remoteOnly": false,
  "postedWithinDays": 90,
  "maxBoards": 300,
  "maxJobs": 150,
  "concurrency": 8,
  "maxJobsPerCompany": 10
}
```

# Actor output Schema

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

Every posting matching your filters, normalized across all six ATS platforms.

## `failedBoards` (type: `string`):

Written only when a board was unreachable or rate-limited. The run itself is unaffected.

# 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": [
        "engineer"
    ],
    "postedWithinDays": 90,
    "maxJobs": 150,
    "maxJobsPerCompany": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("starbright_overlap/bamboohr-jobs-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": ["engineer"],
    "postedWithinDays": 90,
    "maxJobs": 150,
    "maxJobsPerCompany": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("starbright_overlap/bamboohr-jobs-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": [
    "engineer"
  ],
  "postedWithinDays": 90,
  "maxJobs": 150,
  "maxJobsPerCompany": 10
}' |
apify call starbright_overlap/bamboohr-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,starbright_overlap/bamboohr-jobs-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/qnIqeUeYJiytayfBg/builds/Q3qxNd8be0JWdeoXu/openapi.json
