---
title: "How to Retrieve Candidate Education Data from Bullhorn API using Python"
description: "Step-by-step guide to retrieving candidate education data from the Bullhorn API, including authentication, endpoints, common pitfalls, and FAQs."
source_url: "https://www.getknit.dev/blog/how-to-retrieve-candidate-education-data-from-bullhorn-api-using-python"
page_type: "blog"
---

_This is an educational blog post from Knit's blog: “How to Retrieve Candidate Education Data from Bullhorn API using Python”._

# How to Retrieve Candidate Education Data from Bullhorn API using Python

## Introduction

This article is part of a broader series covering the [Bullhorn API](https://developers.getknit.dev/docs/bullhorn-usecases) in depth. It focuses specifically on retrieving candidate education data using the Bullhorn API.

If you're building recruitment workflows, enriching candidate profiles, or standardizing talent data, accessing structured education records is a foundational requirement. This guide walks through how to do that efficiently, both for individual candidates and at scale.

For a complete overview of authentication, rate limits, and other Bullhorn API use cases, refer to the full guide [here](https://www.getknit.dev/blog/bullhorn-api-directory-DlpbVe).

### Prerequisites

Before getting started, ensure the following are in place:

*   Access to the Bullhorn API with valid credentials
*   BhRestToken obtained from the login process
*   Python environment set up with required libraries (e.g., `requests`)

### API Endpoints

*   `GET /entity/CandidateEducation/{id}`: Retrieve education data for a specific candidate
*   `GET /search/CandidateEducation`: Retrieve education data across all candidates

#### 1\. Obtain Access Token

```
import requests

login_url = 'https://auth.bullhornstaffing.com/oauth/token'
params = {
    'grant_type': 'password',
    'client_id': 'YOUR_CLIENT_ID',
    'client_secret': 'YOUR_CLIENT_SECRET',
    'username': 'YOUR_USERNAME',
    'password': 'YOUR_PASSWORD'
}
response = requests.post(login_url, data=params)
access_token = response.json()['access_token']
```

#### 2\. Get Candidate Education Data for One Candidate

```
candidate_id = 'CANDIDATE_ID'
education_url = f'https://rest.bullhorn.com/rest-services/{corpToken}/entity/CandidateEducation/{candidate_id}'
headers = {'BhRestToken': access_token}
response = requests.get(education_url, headers=headers)
education_data = response.json()
```

#### 3\. Get Candidate Education Data for All Candidates

```
search_url = f'https://rest.bullhorn.com/rest-services/{corpToken}/search/CandidateEducation'
params = {'query': '*', 'fields': 'id,candidate,degree,school,major'}
response = requests.get(search_url, headers=headers, params=params)
all_education_data = response.json()
```

## Common Pitfalls

Execution typically fails not because of complexity, but due to operational gaps. Watch for these:

1.  **Token lifecycle mismanagement**  
    BhRestToken expires. Not handling refresh flows will break production pipelines.
2.  **Incorrect endpoint construction**  
    Missing or incorrect `corpToken` in the base URL leads to silent failures.
3.  **Incomplete query parameters**  
    Skipping required fields or malformed queries results in partial or empty responses.
4.  **No response validation layer**  
    Ignoring HTTP status codes leads to bad data entering downstream systems.
5.  **Rate limit blind spots**  
    High-frequency calls without throttling can trigger API restrictions.
6.  **Improper JSON handling**  
    Poor parsing logic causes data inconsistencies, especially in nested structures.
7.  **Version drift**  
    Bullhorn API updates can break existing integrations if not monitored proactively.

## Frequently Asked Questions

1.  **How do I refresh the BhRestToken?**  
    Re-authenticate using the login process to generate a new token.
2.  **What does a 401 error indicate?**  
    Typically invalid credentials or an expired BhRestToken.
3.  **Can I filter education data?**  
    Yes. Use query parameters in the search endpoint to refine results.
4.  **Is there a limit to records returned?**  
    Yes. Pagination applies, refer to API limits and use `start` and `count`.
5.  **How do I handle large datasets?**  
    Implement pagination and batch processing to avoid timeouts and rate limits.
6.  **What format does the API return?**  
    JSON is the standard response format.
7.  **Can I update candidate education data?**  
    Yes. Use the `PUT /entity/CandidateEducation/{id}` endpoint.

## Knit for Bullhorn API Integration

If your objective is speed and reliability, manual integration is a bottleneck.

[Knit](https://www.getknit.dev/integration/bullhorn) abstracts the entire Bullhorn API layer into a single integration. Authentication, authorization, and maintenance are handled out of the box.


## Related pages

- [How Knit works](https://md.getknit.dev/how-knit-works)
- [Unified API product](https://md.getknit.dev/products/unified-api)
