Human Resource Information System (HRIS) platforms are software services that integrate HR functions and streamline HR activities. These activities may include employee data management, recruitment, onboarding and offboarding, performance management, leave and vacation tracking, processing employee feedback, analytics, and reporting. HRIS platforms also often include self-service portals for employees to access important information and track attendance.
BambooHR is a popular cloud-based HRIS platform that's pretty powerful by itself, but it also serves as a common target with which other services can integrate. To enable this, BambooHR provides 125-plus prebuilt integrations on its Marketplace. If there's no existing integration for a given service, BambooHR also provides a REST API to access and update employee data programmatically.
This article explains one possible way of using the BambooHR API to retrieve employee data using Python. Having done that, you'll be able to use your existing knowledge to route the data to a different system with which you're integrating BambooHR.
What is the BambooHR API
The BambooHR API is a RESTful API for integrating other applications into BambooHR. It focuses on synchronizing employee data and generating employee reports. The API provides a variety of endpoints that can retrieve and update employee records and files, company-wide data, benefits, job descriptions and applications, employee training types, and records.
You can access the API using a specific API key if you're developing an integration on behalf of a single customer or use OpenID Connect for authorization if you're serving multiple BambooHR customers.
BambooHR API is particularly useful when your company is a power user of BambooHR, allowing you to automate workflows and customize the platform to perfectly meet your specific needs, such as payroll or employee onboarding. You may want to use state-of-the-art payroll processing or onboarding software as a service (SaaS) that better suits your needs. In this case, if the BambooHR Marketplace doesn't provide an existing integration, your best bet is to use the BambooHR API to synchronize employee data with the other service that you're using.
What if you're working with a SaaS vendor that's not using but developing a state-of-the-art SaaS for onboarding or payroll? If so, you absolutely need to provide integrations with BambooHR and other HRIS platforms using their respective APIs.
How to Extract Employee Data with the BambooHR API
Whatever motivates you to explore the BambooHR API, chances are that your integration efforts start with getting employee information from BambooHR. Let's see how exactly you can retrieve employee data from your BambooHR instance using the BambooHR API and Python.
To follow along, you need two things:
- The latest Python 3 installed on your machine.
- A BambooHR account.
Signing Up for a BambooHR Account
If you don't have a BambooHR account, sign up for a free trial.
After filling out the free trial form, create a name for your BambooHR domain (it's going to be used for your instance's subdomain, as in
https://your-bamboohr-domain.bamboohr.com/) and then log in.
Your trial BambooHR account is pre-populated with sample employee records, so you don't need to worry about coming up with data to follow this tutorial.
Getting Your API Key
To make calls to the BambooHR API, you need to create and copy an API key from your BambooHR account. Here's what you should do:
- In BambooHR's navigation menu, click the user icon on the far right. In the pop-up menu that appears, click API Keys:
- In the My API Keys view, click Add New Key.
- In the Add New API Key view, enter
bamboohr-python
as the key name and then click Generate Key. - When BambooHR displays your newly generated API key, click Copy Key and then click Done.
- When the API key is shown, copy and paste it to a safe place as BambooHR won't show it again.
Setting Up a Python Application
You need to set up some boilerplate for the Python application that grabs data from the BambooHR API.
Open the terminal and then create and go to a directory called
bamboohrthat hosts your application:
mkdir bamboohr && cd bamboohr
Inside the
bamboohrdirectory, create a new Python virtual environment that is hosted in the
envsubdirectory. If you're on macOS or Linux, use the following command:
python3 -m venv env
On Windows, run this instead:
python -m venv env
Activate the new virtual environment in your terminal.
To do this on macOS or Linux, run the following:
source env/bin/activate
If you're on Windows, run the following activation script instead:
env\Scripts\activate.bat
You need Python's
requestspackage to send API requests. Install it by running the following command:
pip install requests
Finally, create a Python file to host the source code that you'll write shortly:
touch main.py
Getting Employee Data from the BambooHR API
Open your newly created
main.pyfile in your favorite code editor.
Once inside the file, start by adding your needed
importstatements:
from pathlib import Path import requests import json
These statements import the following:
- The
requests
package that you've recently installed to make requests to the BambooHR API. - The built-in
json
module to parse the data you are getting from the API. Write it into a file. - The
Path
class from the built-inpathlib
module that helps you create a directory to save the file to.
Next, let's define the base URL that you'll be reusing in your API calls. Paste the following code into your Python file:
domain = 'your-bamboohr-domain' base_url = f'https://api.bamboohr.com/api/gateway.php/{domain}/v1'
Make sure to replace
your-bamboohr-domainwith the actual BambooHR domain that you specified when creating your BambooHR account. It's used as the subdomain in the URL of your BambooHR instance:
https://your-bamboohr-domain.bamboohr.com/.
The
base_urluses your domain name to form your individual base BambooHR API URL that you need to make specific API requests later on.
Let's now save your BambooHR API key to the
api_keyconstant. You also define another constant for authentication in the format that the
requestspackage needs it to be in:
api_key = 'your-api-key' auth = (api_key, '')
Remember to replace
your-api-keywith your actual BambooHR API key that you generated and stashed earlier. Note that in the
authtuple, the second value is an empty string: the BambooHR API uses basic HTTP authentication where the API key is used as the username and the password can be any string, including an empty string.
The last thing you do to prepare for sending API requests is to declare a
headersobject that sets the
AcceptHTTP header to JSON. If you don't do this, BambooHR will return the results as XML by default:
headers = { 'Accept': 'application/json' }
Now, let's add stubs for the three functions that perform the main steps of your program:
def get_all_employees(): pass def print_all_employees(employees_json): pass def save_all_employees_to_file(employees_json): pass all_employees = get_all_employees()
get_all_employees()makes an API call and returns text data.
print_all_employees()displays formatted data in the console output.
save_all_employees_to_file()saves the data to a file.
You're also calling the first of these functions and saving whatever it returns to the
all_employeesvariable.
Let's now fill the stub of the
get_all_employees()function with code that makes an API request for the list of employees in the BambooHR directory. Update
get_all_employees()to read the following:
def get_all_employees(): try: response_all_employees = requests.request('GET', f'{base_url}/employees/directory', headers=headers, auth=auth) if response_all_employees.status_code == 200: return response_all_employees.text else: print( f'Something went wrong when trying to get the requested info from the API server. Status code: {response_all_employees.status_code}. Message: {response_all_employees.reason}') except Exception as e: print("An error occurred while performing the API call: ", e)
Here's what happens inside the updated function:
- The
request
function from therequests
package makes a GET HTTP request to the BambooHR API's Get Employee Directory endpoint. It uses string interpolation to combine the base URL that you've defined earlier with the path of the endpoint. Theheaders
andauth
constants are passed as respective arguments. The result of the call is saved into theresponse_all_employees
variable. - The
response_all_employees
is aResponse
object. Therequest
function returning this object may throw several exceptions, so the code in the function is wrapped into atry…except
block to provide basic handling for these. - The check for status code
200
verifies if your request has been successfully fulfilled, provided that no exceptions occur. If it's successful, then you return the textual employee data that arrived from the API, which is available through thetext
property of theresponse_all_employees
object. - The status code and error message that the API has returned are simply printed if the status code of the response is anything other than
200
.
The employee directory data that the API returns looks something like this:
{ "fields": [ { "id": "displayName", "type": "text", "name": "Display name" }, { "id": "firstName", "type": "text", "name": "First name" }, // more field definitions ], "employees": [ { "id": "4", "displayName": "Charlotte Abbott", "firstName": "Charlotte", "lastName": "Abbott", "preferredName": null, "jobTitle": "Sr. HR Administrator", "workPhone": "801-724-6600", "mobilePhone": "801-724-6600", "workEmail": "cabbott@efficientoffice.com", "department": "Human Resources", "location": "Lindon, Utah", "division": "North America", "linkedIn": "www.linkedin.com", "instagram": "@instagram", "pronouns": null, "workPhoneExtension": "1272", "supervisor": "Jennifer Caldwell", "photoUploaded": true, "photoUrl": "https:\/\/images7.bamboohr.com\/619596\/photos\/4-6-4.jpg?Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cHM6Ly9pbWFnZXM3LmJhbWJvb2hyLmNvbS82MTk1OTYvKiIsIkNvbmRpdGlvbiI6eyJEYXRlR3JlYXRlclRoYW4iOnsiQVdTOkVwb2NoVGltZSI6MTcyMDAxMTM2MH0sIkRhdGVMZXNzVGhhbiI6eyJBV1M6RXBvY2hUaW1lIjoxNzIyNjAzMzcwfX19XX0_&Signature=Ewi31wP5h7aoUEQSEmWEj7vAvo7GTVNXIjk4iJQ9XKGiczzwlUR~EJLrtaClvCxxsvxq0~cdVQ1yJO1fLe0loSP1t5BE48bOZQ0biOqawfMToqTmCBEV7ADxdvqs0rMnGVm9cV6F~p7LewXfp51lgW7u6A2TTudkWCbJN5btsJVp~1UaVyMBsZFwujYvFgrXxXLYxfb4WsvW3bVsnfnmMO8eHq5SIZw~joaII7sEJdzHKaMdsNaMP9GCUY-Mrs~~Gt9JcV3rt8M-YxXH8Rxmo18xmqCqePDML8ETWDWkhzjrDSNv6shU9PjuxhujxDJ6e0BVZ8XYKMn5sTUf9tlltQ__&Key-Pair-Id=APKAIZ7QQNDH4DJY7K4Q", "canUploadPhoto": 1 }, { "id": "5", "displayName": "Ashley Adams", "firstName": "Ashley", "lastName": "Adams", "preferredName": null, "jobTitle": "HR Administrator", "workPhone": "+44 207 555 4730", "mobilePhone": "+44 207 555 6671", "workEmail": "aadams@efficientoffice.com", "department": "Human Resources", "location": "London, UK", "division": "Europe", "linkedIn": "www.linkedin.com", "instagram": "@instagram", "pronouns": null, "workPhoneExtension": "130", "supervisor": "Jennifer Caldwell", "photoUploaded": true, "photoUrl": "https:\/\/images7.bamboohr.com\/619596\/photos\/5-6-4.jpg?Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cHM6Ly9pbWFnZXM3LmJhbWJvb2hyLmNvbS82MTk1OTYvKiIsIkNvbmRpdGlvbiI6eyJEYXRlR3JlYXRlclRoYW4iOnsiQVdTOkVwb2NoVGltZSI6MTcyMDAxMTM2MH0sIkRhdGVMZXNzVGhhbiI6eyJBV1M6RXBvY2hUaW1lIjoxNzIyNjAzMzcwfX19XX0_&Signature=Ewi31wP5h7aoUEQSEmWEj7vAvo7GTVNXIjk4iJQ9XKGiczzwlUR~EJLrtaClvCxxsvxq0~cdVQ1yJO1fLe0loSP1t5BE48bOZQ0biOqawfMToqTmCBEV7ADxdvqs0rMnGVm9cV6F~p7LewXfp51lgW7u6A2TTudkWCbJN5btsJVp~1UaVyMBsZFwujYvFgrXxXLYxfb4WsvW3bVsnfnmMO8eHq5SIZw~joaII7sEJdzHKaMdsNaMP9GCUY-Mrs~~Gt9JcV3rt8M-YxXH8Rxmo18xmqCqePDML8ETWDWkhzjrDSNv6shU9PjuxhujxDJ6e0BVZ8XYKMn5sTUf9tlltQ__&Key-Pair-Id=APKAIZ7QQNDH4DJY7K4Q", "canUploadPhoto": 1 }, // more employee records ] }
The response data consists of two arrays:
fieldsand
employees. The former lists the fields that the response data contains, along with their types. Fields are important as you can use them in API calls to exactly define what kind of data you want to be returned. However, in this simple scenario, you don't need them: what you're after is the
employeesarray.
Printing and Saving Employee Data
You'll save the entire
employeesarray to a file later on. For now, let's display basic information about each employee in the console.
Start by replacing the stub of the
print_all_employees()function with the following:
def print_all_employees(employees_json): for entry in employees_json: print( f'{entry["displayName"]}: {entry["jobTitle"]} at {entry["department"]}, based in {entry["location"]} ({entry["division"]})')
This function now goes over all employee entries and prints a quick summary that includes each employee's full name, job title, department, location, and division.
Now, at the end of the file, following the
all_employeesdeclaration, add this piece of code:
if all_employees is not None: all_employees_json = json.loads(all_employees)['employees'] print_all_employees(all_employees_json)
Initially, you're checking whether
all_employeesis null, which is possible if the prior API call has returned an error. If it's not null, you parse employee data as JSON and save the
employeesarray from the resulting JSON object, thus stripping away the
fieldsarray. You then pass the JSON object to the
print_all_employees()function.
If you call your program now by running
python main.pyin the terminal, you'll see something like this:
Charlotte Abbott: Sr. HR Administrator at Human Resources, based in Lindon, Utah (North America) Ashley Adams: HR Administrator at Human Resources, based in London, UK (Europe) Christina Agluinda: HR Administrator at Human Resources, based in Sydney, Australia (Asia-Pacific) Shannon Anderson: HR Administrator at Human Resources, based in Vancouver, Canada (North America) Maja Andev: VP of Product at Product, based in Lindon, Utah (North America) Eric Asture: VP of IT at IT, based in Lindon, Utah (North America) Cheryl Barnet: VP of Customer Success at Customer Success, based in Lindon, Utah (North America) Jake Bryan: VP Learning and Development at Operations, based in Lindon, Utah (North America)
Finally, let's save the full set of employee data to a JSON file. At the end of
main.py, following the call to
print_all_employees, add a call to
save_all_employees_to_file:
save_all_employees_to_file(all_employees_json)
Then replace the stub of the
save_all_employees_to_filefunction with the following:
def save_all_employees_to_file(employees_json): destination_dir = 'data' Path(destination_dir).mkdir(parents=True, exist_ok=True) json_file = f'{destination_dir}/all_employees.json' with open(json_file, 'w', encoding='utf-8') as target_file: json.dump(employees_json, target_file, ensure_ascii=False, indent=4) print(f"Data saved to {json_file}.")
This function now creates the
datadirectory if it doesn't exist, writes employee data to a JSON file in this directory, and reports a status update to the console.
If you want to see what your
main.pyfile should look like by the end of this exercise, check out this "Extracting Employee Data with the BambooHR API" example.
If you're developing a product that relies on integrations with many different HRIS systems, you need to create and maintain multiple integrations that are going to be more sophisticated than the one shown earlier. The product would probably include authentication and authorization that are more advanced than those that use a single API key, handle error codes and retries, have a database layer instead of employee data dumped into JSON files, and more. After working through a few, chances are you'll realize that's probably not what you want to spend your development resources on.
Instead, consider using a unified HRIS API like the one that Apideck provides. The unified HRIS API is a single API that enables pulling and pushing employee data from and to more than fifty HRIS platforms in real time. You can save valuable development time and resources instead of spending them on developing and maintaining all the integrations in-house.
Summary
After reading this article, you now know how to write a proof-of-concept Python application that gets basic employee information from a BambooHR instance using the BambooHR API. See the "Extracting Employee Data with the BambooHR API" example for the source code of
main.pythat results from performing the instructions given earlier.
In a real-life integration, you need to worry about many other things, such as managing authentication and authorization, handling client- and server-side error codes, implementing retry policies, and monitoring API versions to prevent service interruptions.
Doing this for one HRIS service is fine, but if you're integrating with dozens of them, your development team can get overwhelmed quickly. If you don't want this to happen, start simplifying your HRIS integrations today by signing up to Apideck.