BambooHR Jobs Scraper - Any Company's Open Jobs avatar

BambooHR Jobs Scraper - Any Company's Open Jobs

Pricing

from $2.19 / 1,000 job returneds

Go to Apify Store
BambooHR Jobs Scraper - Any Company's Open Jobs

BambooHR Jobs Scraper - Any Company's Open Jobs

Every open job of any company that hires through BambooHR, one row per job: title, department, employment type, city, state, country, link, and with one switch the posting date, compensation text and full description. A company ID that does not exist is reported, never returned as zero jobs.

Pricing

from $2.19 / 1,000 job returneds

Rating

0.0

(0)

Developer

NeverEmpty

NeverEmpty

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

21 hours ago

Last modified

Share

Pull every open job of any company that hires through BambooHR - the careers sites at <company>.bamboohr.com/careers - read straight from the JSON that BambooHR's own careers page loads. No login, no API key, no proxies, no HTML scraping.

One row per job: title, department, employment type, city, state, country and a link to the job page - plus, with one switch, the posting date, the compensation text, the minimum experience level, the postal code and the full job description.

Unofficial. This Actor is not affiliated with, endorsed by, or sponsored by BambooHR. All product names are trademarks of their respective owners.


A company that does not exist is never reported as "0 jobs"

BambooHR answers a company ID that does not exist with a redirect to www.bamboohr.com, and a company that exists but has no openings with totalCount: 0. Those are two different answers, and this Actor keeps them apart: a typo in the company ID gets a company-not-found row that says where to find the right ID, and a company with nothing open gets a no-openings row. Neither is charged, and neither is an empty dataset.

rowTypeokWhat it meansCharged?
jobtrueA real open jobYes
no-openingstrueThe company's BambooHR careers site exists and lists no open jobsNo
no-matching-jobstrueThe company has jobs, but none match your filters - the row says how many it hasNo
company-not-foundfalseNo BambooHR careers site exists for this ID (BambooHR redirects it to www.bamboohr.com). The row tells you where to find the right oneNo
posting-closedfalseA job that was in the list but whose own page says it is gone or no longer Open when its details were read - not returned as an open jobNo
incompletefalseBambooHR reported more openings than it returned; the jobs that were read are returnedNo
more-jobs-availabletruemaxResults cut the list; says how many were left outNo
not-looked-upfalseNot fetched because maxResults was already reached, or the run was close to its time limitNo
duplicate-inputfalseThe same company was given twice (IDs are not case-sensitive); its jobs are returned onceNo
bad-inputfalseNot a BambooHR company ID or careers linkNo
unavailablefalseBambooHR could not be reached after retries, or answered in a shape this Actor does not readNo
budget-reachedfalseThe run hit your maximum charge before or during this companyNo

You are charged only for job rows.


What BambooHR publishes where - measured, not estimated

Measured 2026-09-12 on 10 companies with open jobs (401auto, basdensteel, montroseholdings, kiln, flyio, cardinal, fbcs, beehiiv, avaindustries, posthog): 208 open jobs, every one of them also read from its own page.

The list (/careers/list) is one request per company - there is no paging - and it carries only part of each job:

ColumnFilled (208 jobs)Source
title, employmentType, jobUrl100%list
department97%list
city / state (location)95%list, from location or, when that is empty, atsLocation
country2% (4 of 208)list - only a few companies fill it there; 94% (195 of 208) once the job page is read
postalCode0%list - only on the job page (94%)
isRemote0%list - BambooHR sends the field but it was null for all 208 jobs, so it stays null here

The job page (/careers/<id>/detail) is where the rest lives. With includeDescription on, each job costs one more request and the row gains:

ColumnFilled (208 jobs)
postedAt (date only, as BambooHR gives it), descriptionText100%
minimumExperience, country, postalCode94%
compensationText66%

compensationText is whatever the company typed - $55,000 per year, $19/hr, TBD, Up to $20 DOE - and is left as text. No number is parsed out of it, and nothing is filled in when it is empty.

locationTypeCode is BambooHR's own code (0, 1 or 2). It was 0 for all 194 jobs that carry a street address and 1/2 for the 14 that do not, but BambooHR publishes no key for it, so it is passed through as-is rather than translated into "remote" or "on-site".


Filters

BambooHR's list has no search of its own, so every filter is applied here, after reading the list, against the fields the list carries: titleContains, departmentContains, locationContains and keyword (title, department, location, employment type). Filters do not look into the description. When a filter can never match on a company - it publishes no department, or no location - the no-matching-jobs row says so instead of pretending no job qualifies.

Location needs one more word. The list carries only the city and the state (a country for 4 of the 208 measured jobs, never a postal code). So on its own, locationContains matches city and state - Kitchener, Ontario - and a country or postal code returns a no-matching-jobs row that says exactly this. With includeDescription: true every listed job's page is read before the filter is applied, and locationContains then also matches the country and the postal code: Canada returns 118 of 401 Auto's 124 jobs, N2C the jobs in that Kitchener postal district. Jobs whose page could not be read are judged on the list's city and state only.


Input

FieldTypeDefaultWhat it does
companiesarray(prefilled: 401auto)BambooHR company IDs or careers links, up to 200. If empty, 401auto is used and every row says so in input
keywordstring(empty)Keep jobs whose title, department, location or employment type contains this text
titleContainsstring(empty)Keep jobs whose title contains this text (case-insensitive)
departmentContainsstring(empty)Keep jobs whose department contains this text
locationContainsstring(empty)Matches city and state from the list (Kitchener, Ontario); with includeDescription also country and postal code (Canada, N2C)
includeDescriptionbooleanfalseRead each job's page: adds the description, posting date, compensation text, experience level, postal code and country (one extra request per job, 8 in parallel). With locationContains, every listed job's page is read before filtering
maxResultsinteger1000Job rows for the whole run; a more-jobs-available row counts the rest, later companies get not-looked-up
maxRetriesinteger3Attempts per request for temporary errors (HTTP 429 / 5xx)

Finding a company ID: open the company's careers page. The address looks like https://401auto.bamboohr.com/careers - 401auto is the ID. You can also paste that address, or a job link such as https://401auto.bamboohr.com/careers/560.

Descriptions

With includeDescription on, descriptionStatus says what happened for each job:

  • ok - the description is in descriptionText (empty if the page has none)
  • failed: ... - BambooHR did not return the page after retries; the job is still returned and charged, because everything from the list is real, but postedAt, compensationText and the other page-only columns stay null
  • not-fetched-time-limit - the run was close to its time limit, so pages were skipped to return the jobs in time (still charged); companies after it get a not-looked-up row
  • A job whose page answers 404 or is no longer marked Open is not returned as a job - it becomes a free posting-closed row

Without includeDescription, descriptionStatus and the page-only columns are null for every job.


Output columns

rowType, ok, note, input, companyId, jobId, title, department, departmentId, employmentType, city, state, country, postalCode, fullLocation, isRemote, locationTypeCode, postedAt, compensationText, minimumExperience, jobUrl, descriptionText, descriptionStatus, searchedKeyword, checkedAt

Every row - including note rows - has all 25 columns in the same order.

Things it refuses to invent or pass on

  • No people's names - but the application address stays. The list and the job page carry no recruiter name. Inside descriptions, an e-mail address whose local part looks like a person's name (jane.doe@, janedoe@) is replaced with [email removed]; role mailboxes such as careers@, hr@ or jobs+infra@ and phone numbers are the way to apply, so they are left as written (measured: all 125 addresses and 118 phone numbers in the 208 descriptions were role mailboxes and a company switchboard).
  • isRemote is only ever what BambooHR sends. A job without a location is not turned into "remote"; its fullLocation stays null.
  • postedAt is the date BambooHR publishes (YYYY-MM-DD). No time of day is added.
  • employmentType is the company's own label (Full-Time, Part-Time, Full-Time - Weekly Paid (Ocean Hotels)), not normalised into a guess.
  • Empty values are null, never an empty string or a 0.

How it is checked

  • 71 automated checks, most of which run the real list, page, retry, filter and charging code against recorded BambooHR responses (a company that does not exist, an empty company, a list shorter than its count, closed postings, budget and time limits)
  • 72 deliberate defects injected into the source, plus 23 into the charging path; every one was caught by those checks (re-run 2026-09-12)
  • The rows this Actor returns for the 10 measured companies were compared against the companies' lists fetched directly: 208 of 208 jobs, same IDs, same order
  • Only public careers pages are read. Nothing needs a login
  • Every request has a 45-second timeout; temporary errors are retried with a growing pause

Support

Found a company that behaves oddly? Open an issue on the Issues tab with the company ID - it will be reproduced against the live careers site before anything is changed.