The hiring-signal hiding in plain sight
BambooHR runs the careers page for a huge slice of the SMB and mid-market world — everything from regional healthcare networks to ballet companies. If you've ever needed to answer "who is this company hiring right now, and for what," BambooHR's public {company}.bamboohr.com/careers page is usually where the answer lives. The catch is that there's no dashboard for pulling that data across dozens of companies at once, no bulk export, and no official API key you can just request.
I build small, focused scrapers for a living (under the Devil Scrapes banner), and BambooHR kept coming up as a gap: recruiters, SDRs, and labor-market researchers all wanted "every open req at this employer" without hand-copying a careers page. So I built a BambooHR jobs scraper that talks to BambooHR's own careers JSON endpoints directly and normalizes the output into one schema across every tenant.
What the data looks like
Here's a single row from a real run — this is what lands in your dataset per posting:
{
"company": "adobe",
"job_id": 15,
"job_title": "IT Security Engineer",
"job_url": "https://adobe.bamboohr.com/careers/15",
"department": "IT",
"employment_type": "Full-Time",
"job_status": "Open",
"location_city": "Mayfair",
"location_state": "London, City of",
"location_country": "United Kingdom",
"is_remote": null,
"date_posted": "2025-11-29",
"description_html": "<p><strong>About Us</strong></p>...",
"description_text": "About Us Our mission is simple...",
"scraped_at": "2026-07-26T12:00:00Z"
}
Note is_remote is a tri-state boolean, not a forced true/false — when the employer never said, we leave it null instead of guessing "on-site." That distinction matters if you're building any kind of remote-hiring filter downstream.
The naive approach, and where it falls apart
If you open devtools on any BambooHR careers page, you'll find the /careers/list endpoint returning JSON almost immediately — it looks trivial at first glance. The friction shows up once you try to do this at any real scale:
- Company-by-company inconsistency. Some subdomains have zero open jobs, some 404 because the company deactivated its careers page, some reject a custom-domain front-end. Your script needs to treat all three as normal outcomes, not exceptions that kill the whole batch.
-
Full descriptions are a second endpoint. The list feed gives you titles and locations; the actual HTML job description body lives behind a separate
/careers/{id}/detailcall per posting, which means orchestrating N+1 requests cleanly. -
Client fingerprinting. A bare
requests.get()from a script looks nothing like a browser at the TLS/HTTP2 layer, and once you're doing this against dozens of company subdomains in a loop, that difference starts mattering.
None of this is exotic, but it's exactly the kind of "boring at small scale, annoying at real scale" work that eats an afternoon you didn't plan to spend.
The Actor
This is where BambooHR Jobs Scraper comes in. We rotate Chrome/Firefox TLS fingerprints via curl-cffi on every request, retry 408/429/5xx with exponential backoff up to 5 attempts, and isolate failures per company so one bad subdomain never sinks the rest of your batch. You get back clean, Pydantic-validated rows — no half-parsed HTML, no silently-empty datasets.
Run it via the Apify Python SDK:
from apify_client import ApifyClient
client = ApifyClient("APIFY_TOKEN")
run = client.actor("DevilScrapes/bamboohr-jobs-scraper").call(run_input={
"companies": ["adobe", "nycballet"],
"maxJobsPerCompany": 50,
"fetchFullDescription": True,
"proxyConfiguration": {"useApifyProxy": True},
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item["job_title"], item["location_city"])
Or fire it directly from the Apify Console with the same JSON as input — no code required if you just want a one-off pull.
What you'd actually use this for
- Recruiting pipelines — build sourcing lists against every BambooHR-hosted employer in a target industry, normalized into one schema.
- Hiring-intent signal for sales/BD — a sudden burst of open reqs at a target account is a decent proxy for headcount growth or a new-market push, before it shows up anywhere else.
- Job-board aggregation — BambooHR coverage slots next to our Teamtailor, Workday, SmartRecruiters, Workable, and Multi-ATS scrapers to build one hiring-intel feed across the whole ATS landscape.
- Labor-market research — sample hiring demand by industry, region, or company size band with a structured, machine-readable dataset.
- Competitive intelligence — watch a specific competitor's open reqs over time to infer where they're actually growing.
Pricing, honestly
The Actor charges $0.005 per run (a flat warm-up fee) plus $0.0015 per job posting written to your dataset. A 1,000-posting pull costs $1.51 total. A company with zero current openings costs nothing beyond the warm-up fee — you're not billed for rows that never existed. Apify gives every new account $5 of free credit, no card required, which covers roughly 3,300 postings before you'd spend a cent of your own money.
Why this is more interesting than it looks
The genuinely fiddly part wasn't the HTTP calls — it was normalizing BambooHR's own inconsistencies across tenants. Some companies fill in department, some leave it null; some populate location_state with a full region name, others with an abbreviation; is_remote is sometimes explicit and usually isn't. Building one stable schema that holds up across hundreds of independently-configured BambooHR instances, without silently inventing values the source data doesn't actually contain, is most of the real engineering here.
Honest limitations
This Actor only resolves {subdomain}.bamboohr.com addresses — if a company fronts its BambooHR careers page with a custom domain, you'll need the underlying BambooHR subdomain instead. There's no compensation field either, because BambooHR's public feed doesn't expose structured salary data on any company we've checked — we don't invent one. And it's a point-in-time snapshot: schedule recurring runs if you want to track how a board changes over time.
Try it
BambooHR Jobs Scraper is live on the Apify Store now, priced at $1.50 per 1,000 results with Apify's standard $5 free trial credit (no credit card needed to start). It's one of several ATS-coverage Actors we run under Devil Scrapes — if you need Recruitee, Ashby, Personio, or Breezy HR coverage too, those are live as well.
What ATS is your target company running that isn't covered yet? Drop it in the comments — that's usually how the next Actor in this fleet gets picked.
Top comments (0)