Skip to content

How to Integrate with the BambooHR API (2026 Engineering Guide)

A practical engineering guide to BambooHR API integration covering authentication gotchas, cursor-based pagination, undocumented rate limits, hidden maintenance costs, and when a unified API makes more sense.

Sidharth Verma Sidharth Verma · · 13 min read
How to Integrate with the BambooHR API (2026 Engineering Guide)

Your engineer just said integrating with BambooHR will take a sprint, falling into the classic "just a few API calls" trap. They're looking at the REST endpoints, the clean JSON responses, and the straightforward Basic Auth header. They're right about the happy path — that part genuinely is simple. What they haven't priced in is the undocumented throttling, the 400-field ceiling you'll hit the moment you try to pull a full employee record, and the OAuth migration BambooHR started enforcing in 2025.

This guide breaks down the architectural realities of building a native BambooHR integration. We cover authentication mechanics, pagination constraints, rate limit handling, webhook architecture, and the hidden maintenance costs that drain engineering resources quarter after quarter.

Understanding the BambooHR API Architecture

The BambooHR API is a RESTful service where all requests route over HTTPS to a company-specific base URL:

https://api.bamboohr.com/api/gateway.php/{yourSubdomain}/v1/

Unlike centralized APIs where all traffic hits a single api.vendor.com host, BambooHR requires the customer's exact company subdomain to construct the base URL. This matters — your application must capture this information during the onboarding flow.

Each employee has an immutable employee ID that is unique within a single company that you can use to reference the employee. The API covers employees, time-off, benefits, time tracking, applicant tracking, webhooks, and custom reports. You can request specific fields by passing a comma-separated list in the fields query parameter:

curl -u "{API_KEY}:x" \
  "https://api.bamboohr.com/api/gateway.php/acme/v1/employees/123?fields=firstName,lastName,department,jobTitle"

Currently, the only version is "V1." However, BambooHR has added three new Datasets endpoints under /api/v1_2/ that provide the same dataset and field discovery capabilities as their /api/v1/ counterparts, with improved API consistency. For new integrations, it's worth evaluating the v1.2 endpoints alongside the standard v1 surface.

Core entities in the data model:

  • Employees: The central node. Returns demographic data, contact information, and organizational placement.
  • Employment Status: Historical and current records of an employee's lifecycle (hired, terminated, promoted).
  • Time Off: Balances, policies, and individual time-off requests.
  • Custom Tables: BambooHR allows administrators to define custom fields and tables, meaning the schema you expect will rarely match the schema your enterprise customers actually use.

BambooHR provides official SDKs to help you integrate with their API quickly and reliably. Their SDKs handle authentication, error handling, retries, and provide fully typed models. As of early 2026, official SDKs exist for PHP and Python, both supporting OAuth 2.0.

The Subdomain Routing Problem

Because the base URL requires a subdomain, your application must capture this information during onboarding. If a user inputs the wrong subdomain, or if their company rebrands and changes their BambooHR URL, your integration instantly breaks with a 404. Your database schema for storing connected accounts must treat the subdomain as a mutable configuration variable, not a static identifier.

Authentication: API Keys vs. OAuth 2.0

BambooHR supports two authentication methods, and the distinction matters more than it used to.

API Key via Basic Auth

The simplest path. BambooHR uses Basic Authentication. For the username, you use your API key and for the password, you can use anything (commonly 'x'). The API key inherits the permission set of the user who created it. If a user doesn't have access to certain fields, the API key they generate won't have access to those fields either.

// Formatting the BambooHR auth header in Node.js
const apiKey = process.env.BAMBOOHR_API_KEY;
const encodedCredentials = Buffer.from(`${apiKey}:x`).toString('base64');
 
const headers = {
  'Authorization': `Basic ${encodedCredentials}`,
  'Accept': 'application/json'
};
 
const response = await fetch(
  `https://api.bamboohr.com/api/gateway.php/${subdomain}/v1/employees/directory`,
  { method: 'GET', headers }
);

Two gotchas that bite people in production:

  1. Key lockout. If an unknown API key is used repeatedly, the API will disable access for a period of time. Users will still be able to log in to the BambooHR website during this time. When the API is disabled, it will send back an HTTP 403 Forbidden response to any requests it receives. If you're debugging with the wrong key in a tight loop, you'll lock yourself out.

  2. Double round-trips. When an API request is made without credentials, BambooHR returns a 401 response with a WWW-Authenticate: Basic realm="..." header. Some HTTP clients use this as a signal to retry the request with credentials — a pattern known as HTTP authentication negotiation. This doubles the number of HTTP round trips for every API call. BambooHR strongly recommends configuring your integration to include credentials on every API request from the start rather than relying on the challenge-response cycle.

Warning

Always send the Authorization header preemptively. Relying on the 401 challenge-response pattern wastes your rate limit budget on failed requests and doubles latency.

Storing Credentials Securely

API keys grant broad access to sensitive HR data — compensation, social security numbers, home addresses. Storing these in plain text in your database is a massive liability. Your infrastructure must encrypt keys at rest using AES-256-GCM or equivalent, decrypting them only in memory immediately before the HTTP request is dispatched.

OAuth 2.0 (Authorization Code Flow)

For multi-tenant integrations or marketplace apps, OAuth 2.0 is now the required path. BambooHR deprecated its OpenID Connect Login API as of April 14, 2025. Applications created after that date must use OAuth 2.0 tokens for authentication instead of User API Keys.

The flow follows a standard authorization code grant:

  1. Redirect the user to https://{companyDomain}.bamboohr.com/authorize.php with your client_id, redirect_uri, requested scopes, and response_type=code
  2. BambooHR redirects back with a temporary code
  3. Exchange that code at the token endpoint for an access_token and refresh_token
sequenceDiagram
    participant App as Your App
    participant Browser as User Browser
    participant BHR as BambooHR
    App->>Browser: Redirect to authorize.php
    Browser->>BHR: User logs in, grants consent
    BHR->>Browser: Redirect to callback with ?code=
    Browser->>App: Callback with auth code
    App->>BHR: POST /token.php (code + client_secret)
    BHR->>App: access_token + refresh_token
    App->>BHR: API requests with Bearer token

The access token expires in 3600 seconds. You'll only receive a refresh_token if you include the offline_access scope in your initial authorization request — miss that scope and you're stuck re-authorizing users every hour.

The Redirect URI is the URL you registered when creating your app in the BambooHR Developer Portal. During the OAuth flow, BambooHR redirects users to this address along with a temporary authorization code. Make sure the Redirect URI you use in your requests exactly matches the one you registered — even small differences (such as an extra slash or capitalization change) can cause authentication errors.

Tip

Which should you pick? API keys are fine for internal tooling where a single BambooHR admin owns the integration. For a B2B SaaS product connecting to your customers' BambooHR accounts, OAuth 2.0 is required. BambooHR is actively deprecating legacy auth paths — build on OAuth from day one.

Handling BambooHR API Pagination and Rate Limits

This is where BambooHR's "simple API" reputation starts to crack.

The New Cursor-Based Pagination Endpoint

BambooHR added a new public API endpoint, GET /api/v1/employees, which provides a more flexible and efficient way to retrieve employee data. This endpoint supports filtering, sorting, and pagination, allowing developers to request only the employees and fields they need.

Before this endpoint (added in late 2025), you had two options for bulk employee retrieval: the /employees/directory endpoint (which dumps everyone at once with limited fields) or custom reports (which require you to pre-define the fields you want). Neither supported proper cursor-based pagination.

The existing directory endpoint and custom reports remain available and unchanged. No action is required for current integrations; however, developers may review the new endpoint to determine whether its capabilities better align with their use cases.

The 400-Field Limit

BambooHR set a limit of up to 400 fields that can be requested for a single request to the Get Employee endpoint. This limit is being applied to help ensure the stability and performance of their systems for everyone.

BambooHR is also researching a reasonable limit to the number of fields that can be requested in a single custom report. 90% of uses of the Request Custom Report endpoint request 30 or less fields. However there are some requests that attempt to get 1000 or more fields in a single request.

If your integration needs more than 400 fields per employee (common in companies with many custom fields), you'll need to batch your field requests across multiple API calls and merge the results client-side. For a complex analytics product requiring 600 fields, that means executing multiple paginated passes and stitching the JSON payloads in memory before writing to your database.

Rate Limiting: The Undocumented Problem

Here's where things get frustrating. BambooHR does have rate limits, but specific details are not easily found in the public documentation. API requests can be throttled if BambooHR deems them to be too frequent. Implementations should always be prepared for a 503 Service Unavailable response.

When rate limiting occurs, a "Retry-After" header may be available in the response, indicating when it's appropriate to retry the request. That "may be" is doing a lot of heavy lifting — you can't rely on it being there.

When you hit their limits, the API throws specific HTTP status codes:

  • 429 Limit Exceeded: You're making too many requests per second.
  • 503 Service Unavailable: The BambooHR gateway is overwhelmed and dropping connections.

If your integration relies on a naive while loop to fetch data, a 429 error will crash your sync job. Here's a production-grade retry pattern:

import time
import random
import requests
from requests.auth import HTTPBasicAuth
 
def bamboo_request(url, api_key, max_retries=5):
    for attempt in range(max_retries):
        response = requests.get(
            url,
            auth=HTTPBasicAuth(api_key, "x"),
            headers={"Accept": "application/json"}
        )
        
        if response.status_code == 200:
            return response.json()
        
        if response.status_code in (429, 503):
            retry_after = response.headers.get("Retry-After")
            if retry_after:
                wait = int(retry_after)
            else:
                # Exponential backoff with jitter
                wait = min(2 ** attempt + random.uniform(0, 1), 60)
            
            time.sleep(wait)
            continue
        
        response.raise_for_status()
    
    raise Exception(f"Max retries exceeded for {url}")

Here's what a typical sync flow looks like when backoff kicks in:

sequenceDiagram
    participant SaaS as Your Application
    participant Worker as Background Job
    participant Bamboo as BambooHR API
    
    SaaS->>Worker: Trigger Directory Sync
    Worker->>Bamboo: GET /v1/employees?cursor=null
    Bamboo-->>Worker: HTTP 200 (Page 1)
    Worker->>Bamboo: GET /v1/employees?cursor=xyz123
    Bamboo-->>Worker: HTTP 429 Limit Exceeded
    Note over Worker: Sleep for 2 seconds<br>Retry request
    Worker->>Bamboo: GET /v1/employees?cursor=xyz123
    Bamboo-->>Worker: HTTP 503 Service Unavailable
    Note over Worker: Sleep for 4 seconds<br>Retry request
    Worker->>Bamboo: GET /v1/employees?cursor=xyz123
    Bamboo-->>Worker: HTTP 200 (Page 2)
Info

BambooHR's rate limits are intentionally undocumented and subject to change at their discretion. BambooHR may, in its sole discretion, limit, modify, suspend, or discontinue access to any Developer Tools or specific API endpoints at any time, including by imposing or adjusting rate limits. Build your integration assuming limits will tighten over time.

For a deeper look at handling rate limits across multiple HR platforms, see our guide to API rate limit best practices.

Webhooks vs. Polling for Real-Time Sync

To keep your application updated when an employee is hired or terminated, you have two choices: poll the API on a cron schedule, or listen for webhooks.

BambooHR has made meaningful improvements here. They transitioned from a cron-based system to a real-time event-driven architecture. With this transition, they removed webhook scheduling and rate limiting features from the user interface as they are no longer needed for real-time delivery. If your webhook configuration used scheduling or rate limiting features, your webhooks automatically transitioned to real-time delivery with no action required.

They also expanded webhook functionality to include support for custom fields, allowing customers and partners to monitor and receive real-time notifications when their own custom data changes.

That said, relying purely on webhooks introduces operational overhead:

  1. Delivery guarantees. If your receiving endpoint goes down for maintenance, payloads may be dropped. You need a highly available queue immediately behind your webhook receiver to ensure no data is lost.
  2. Idempotency. Duplicate events happen. Your processing logic needs to handle receiving the same event multiple times without corrupting state.
  3. Manual configuration friction. BambooHR webhooks often require the customer's HR admin to log into their dashboard and manually configure the endpoint URL and select trigger fields. This creates friction during onboarding.

Because of these realities, most engineering teams implement a hybrid approach: webhooks for real-time updates, plus a nightly polling job to catch missed events and ensure absolute data consistency.

The Hidden Costs of Building a Native BambooHR Integration

Let's do the math that sprint planning never does.

Week 1-2: Your engineer builds the happy path. Basic auth, pull employees, map fields. Demo looks great. Ship it.

Month 2: A customer reports missing custom fields. Turns out the API key they generated doesn't have admin permissions. Debugging takes a day because the API just silently omits fields the key can't access — no error, no warning.

Month 4: BambooHR pushes a change to their webhook system. Some fields your integration relied on are removed. BambooHR removed some webhook fields — some no longer exist in their database, while others had extremely low usage. They reached out directly to impacted customers, but your customer didn't forward the notice to your engineering team.

Month 8: Your second enterprise customer uses BambooHR with 2,000 employees and 200 custom fields. Your integration hits the 400-field limit and starts silently dropping data. The pagination approach that worked for a 50-person company now triggers rate limits.

This pattern repeats across every HRIS vendor, not just BambooHR—and we see the exact same maintenance burden when teams build accounting integrations for QuickBooks, FreshBooks, or Xero. Organizations need $50,000 to $150,000 yearly to cover staff and partnership fees for ongoing API integration maintenance, according to research by Netguru. That's not the cost of building — that's the cost of keeping it running.

Cost Category Estimated Annual Cost
Initial build (amortized) $15,000 – $30,000
Ongoing maintenance & monitoring $20,000 – $50,000
Edge case debugging $10,000 – $25,000
Auth changes & API deprecation handling $5,000 – $15,000
Customer support escalations $5,000 – $15,000
Total (single vendor) $55,000 – $135,000

And that's for one HRIS provider. Most B2B SaaS products need to support BambooHR, Workday, ADP, Gusto, Rippling, and a half-dozen others. Multiply accordingly.

Every hour spent babysitting third-party HR APIs is an hour not spent on the core product your customers actually pay for. For a detailed breakdown, read our post on building native HRIS integrations without draining engineering.

How Truto Handles BambooHR (and 50+ Other HRIS Providers)

Let's be honest about trade-offs. A unified API adds an abstraction layer between you and the vendor API. That means less raw control, and there will be edge cases where you need data outside the unified schema. Any vendor who tells you their unified API covers 100% of every provider's surface area is lying to you.

That said, here's what Truto's architecture actually does for BambooHR integrations:

Zero integration-specific code. You don't write code specific to BambooHR — you write code against the Truto Unified Schema. When you request an employee directory, you hit the Truto /unified/hris/employees endpoint. The platform automatically translates that request into BambooHR's native format, handles the 400-field batching, manages cursor-based pagination, and returns a clean, standardized JSON array.

If the customer uses Workday instead of BambooHR, your code doesn't change. Truto routes the request to the correct provider using the integrated_account_id parameter.

curl -H "Authorization: Bearer {TRUTO_TOKEN}" \
  "https://api.truto.one/unified/hris/employees?integrated_account_id={ACCOUNT_ID}"

The unified data model. Instead of BambooHR's field-name-based schema, you work with standardized entities:

erDiagram
    COMPANY ||--o{ LOCATION : has
    COMPANY ||--o{ GROUP : has
    COMPANY ||--o{ JOB_ROLE : defines
    EMPLOYEE }o--|| COMPANY : belongs_to
    EMPLOYEE ||--o{ EMPLOYMENT : has
    EMPLOYEE }o--o{ GROUP : member_of
    EMPLOYEE }o--o| JOB_ROLE : assigned
    EMPLOYEE ||--o{ TIMEOFF_REQUEST : submits
    EMPLOYEE ||--o{ EMPLOYEE_COMPENSATION : receives

This schema holds whether you're pulling from BambooHR, Workday, or Personio.

Automatic auth lifecycle management. Truto manages both API key storage (encrypted at rest) and OAuth 2.0 token lifecycles. For OAuth-connected accounts, Truto refreshes tokens shortly before they expire. If a refresh fails, the account is flagged as needing re-authorization and your application is notified via webhook — no silent failures.

Pagination and rate limit handling. Truto's pagination system adapts to each provider's approach. For BambooHR, that means using cursor-based pagination on the new employees endpoint and handling the 400-field batching automatically. Rate limit responses trigger exponential backoff within the platform, so your application never sees a 429 or 503.

What Truto doesn't solve. If you need BambooHR-specific features outside the unified schema — like time tracking break policies or applicant tracking modules — you can use Truto's Proxy API to make direct calls to BambooHR's native endpoints. The Proxy API still gives you managed auth and rate limiting, but you're back to provider-specific response formats.

For companies that need to handle custom field mappings that differ between customers, Truto uses a three-level override hierarchy: platform defaults, environment-level overrides for your product, and per-account overrides for individual customers with unusual configurations.

What Should You Actually Do?

Here's the decision framework:

Build it yourself if:

  • BambooHR is the only HRIS you'll ever support (and you're certain about that)
  • You need deep access to BambooHR-specific features like time tracking break policies or benefits administration
  • Your team has spare capacity and you want full control over the data pipeline

Use a unified API if:

  • Your sales team is already asking for Workday, ADP, and Gusto support alongside BambooHR
  • You'd rather spend engineering cycles on your core product
  • You need to move fast — integrating through a unified API takes days, not quarters

BambooHR's API is one of the friendlier HRIS APIs out there. The docs are decent (by HR vendor standards), the data model is flat, and the REST conventions are mostly standard. But "friendlier" doesn't mean "free to maintain." The auth migration alone — from API keys to OAuth 2.0, with the OpenID Connect deprecation thrown in — is a reminder that even the simplest vendor APIs change underneath you.

You have to handle Basic Auth with API keys securely, navigate the strict 400-field limit, build cursor-based pagination loops, and implement exponential backoff for undocumented rate limits. While building this in-house is entirely possible, the ongoing maintenance burden makes it a poor use of highly paid engineering talent the moment you need more than one provider. And in HRIS, you always need more than one provider.

If you're weighing the build vs. buy decision for integrations, the math usually tips toward buy somewhere around provider number two. To evaluate how different platforms handle this architecture, review our comparison of alternatives for HRIS integrations.

FAQ

How does BambooHR API authentication work?
BambooHR supports two auth methods: API keys via HTTP Basic Auth (key as username, any string as password) and OAuth 2.0 authorization code flow. BambooHR deprecated its OpenID Connect Login API in April 2025, making OAuth 2.0 the required path for new marketplace integrations.
What are the BambooHR API rate limits?
BambooHR does not publish specific rate limit numbers. Requests deemed too frequent are throttled with 429 or 503 responses. A Retry-After header may or may not be present. Integrations must implement exponential backoff with jitter and assume limits will tighten over time.
Does the BambooHR API support pagination?
Yes. The new GET /api/v1/employees endpoint, added in October 2025, supports cursor-based pagination along with filtering and sorting. It enforces a strict limit of 400 fields per request. Older endpoints like the directory and custom reports use simpler patterns.
How much does it cost to build a custom BambooHR integration?
According to Netguru, annual maintenance costs for custom API integrations range from $50,000 to $150,000 covering staffing, vendor changes, debugging, and customer support. The initial build is typically the smallest portion of total cost.
Is there a field limit on BambooHR API requests?
Yes. BambooHR enforces a limit of 400 fields per request on the Get Employee endpoint. If you need more, you must batch field requests across multiple API calls and merge the results client-side.

More from our Blog