A reference guide for building AI agents: every method, how to authenticate, and the permissions each one needs.
The Greenhouse API is how an app or AI agent works with a recruiting organization: reading candidates and their applications, advancing or rejecting a candidate in the pipeline, creating jobs, and reading offers and interview scorecards. Access is granted through a Harvest API key, which a site admin grants endpoint by endpoint, and a granted endpoint returns all of its data rather than a narrowed subset. Greenhouse can also push recruiting events, like a candidate being hired, to a registered web hook.
How an app or AI agent connects to Greenhouse determines what it can reach. The Harvest API is the route into recruiting data, and each call is governed by the API key behind it and the endpoints that key has been granted.
The Harvest API is the route into recruiting data at https://harvest.greenhouse.io/v1. A call authenticates with a Harvest API key over HTTP Basic auth, sent as the username with a blank password. Write methods also require an On-Behalf-Of header naming the Greenhouse user the action is attributed to.
Greenhouse posts an event to a URL registered in the Greenhouse application when something happens in recruiting, like a candidate being hired or an application changing stage. Each delivery carries a signature, computed from the secret entered when the web hook was created and the request body, so the receiver can confirm it came from Greenhouse.
A Harvest API key authenticates over HTTP Basic auth, sent as the username with a blank password. The key is created in the Greenhouse Dev Center, where a site admin grants it access to specific endpoints one by one. A key created before 18 January 2017 carries full access to the endpoints that existed then.
The Harvest API is split into areas an agent can act on, like candidates, applications, jobs, offers, and scorecards. Each area has its own methods, and writes here change real hiring records or move a candidate through a pipeline.
List, read, create, and update candidates, and add notes, attachments, and other records to a candidate.
List, read, create, and update applications, and move a candidate through the pipeline by advancing, rejecting, or hiring.
List, read, create, and update jobs, and read or change a job's hiring team.
List and read the public posts for jobs, and update a post or change whether it is live.
List and read offers across the organization or for an application, and update the current offer on an application.
List and read the interview scorecards submitted across the organization or for a single application.
List and read scheduled interviews, and create, update, or remove an interview on an application.
List and read the Greenhouse users in an organization, create a user, and enable, disable, or change a user's permission level.
List, read, create, and update the departments and offices that organize jobs and candidates.
Filter by method, access, or permission, or search any path. Select a row for version detail, rate limits, the related webhook event, and the source.
| Method | Endpoint | What it does | Access | Permission | Version | |
|---|---|---|---|---|---|---|
CandidatesList, read, create, and update candidates, and add notes, attachments, and other records to a candidate.7 | ||||||
| GET | /v1/candidates | List all candidates in the organization. | read | GET: List Candidates | Current | |
Access in Harvest is per-endpoint and all-or-nothing: a key granted this permission can read every candidate, with no narrowing to a subset. Acts oncandidate Permission (capability) GET: List CandidatesVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /v1/candidates/{id} | Retrieve a single candidate by ID. | read | GET: Retrieve Candidate | Current | |
Returns a candidate's personal details, including contact information. Acts oncandidate Permission (capability) GET: Retrieve CandidateVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /v1/candidates | Create a new candidate. | write | POST: Add Candidate | Current | |
Write methods require an On-Behalf-Of header naming the Greenhouse user the action is attributed to, for the audit trail. Acts oncandidate Permission (capability) POST: Add CandidateVersionAvailable since the API’s base version Webhook event update_candidateRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| PATCH | /v1/candidates/{id} | Edit a candidate's details. | write | PATCH: Edit Candidate | Current | |
Requires the On-Behalf-Of header. Fires the update_candidate webhook. Acts oncandidate Permission (capability) PATCH: Edit CandidateVersionAvailable since the API’s base version Webhook event update_candidateRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /v1/candidates/{id}/notes | Add a note to a candidate's profile. | write | POST: Add Note | Current | |
Requires the On-Behalf-Of header naming the note's author. Acts oncandidate note Permission (capability) POST: Add NoteVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /v1/candidates/{id}/attachments | Add an attachment, such as a resume, to a candidate. | write | POST: Add Attachment | Current | |
Requires the On-Behalf-Of header. Acts oncandidate attachment Permission (capability) POST: Add AttachmentVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| PUT | /v1/candidates/{id}/anonymize | Anonymize a candidate's personal data for a privacy request. | write | PUT: Anonymize Candidate | Current | |
Irreversibly removes personal fields. Requires the On-Behalf-Of header. Fires the candidate_anonymized webhook. Acts oncandidate Permission (capability) PUT: Anonymize CandidateVersionAvailable since the API’s base version Webhook event candidate_anonymizedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
ApplicationsList, read, create, and update applications, and move a candidate through the pipeline by advancing, rejecting, or hiring.6 | ||||||
| GET | /v1/applications | List all applications in the organization. | read | GET: List Applications | Current | |
All-or-nothing access: the granted key reads every application across all jobs. Acts onapplication Permission (capability) GET: List ApplicationsVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /v1/applications/{id} | Retrieve a single application by ID. | read | GET: Retrieve Application | Current | |
Read-only. Acts onapplication Permission (capability) GET: Retrieve ApplicationVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /v1/candidates/{id}/applications | Create an application for a candidate or prospect on a job. | write | POST: Add Application | Current | |
Requires the On-Behalf-Of header. Fires the new_candidate_application webhook. Acts onapplication Permission (capability) POST: Add ApplicationVersionAvailable since the API’s base version Webhook event new_candidate_applicationRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /v1/applications/{id}/advance | Advance an application to the next interview stage. | write | POST: Advance Application | Current | |
Requires the On-Behalf-Of header. Fires the candidate_stage_change webhook. Acts onapplication Permission (capability) POST: Advance ApplicationVersionAvailable since the API’s base version Webhook event candidate_stage_changeRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /v1/applications/{id}/hire | Mark an application as hired. | write | POST: Hire Application | Current | |
Requires the On-Behalf-Of header. Fires the hire_candidate webhook. Acts onapplication Permission (capability) POST: Hire ApplicationVersionAvailable since the API’s base version Webhook event hire_candidateRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /v1/applications/{id}/reject | Reject an application, with a reason and optional notification. | write | POST: Reject Application | Current | |
Requires the On-Behalf-Of header. Fires the reject_candidate webhook. Acts onapplication Permission (capability) POST: Reject ApplicationVersionAvailable since the API’s base version Webhook event reject_candidateRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
JobsList, read, create, and update jobs, and read or change a job's hiring team.5 | ||||||
| GET | /v1/jobs | List all jobs in the organization. | read | GET: List Jobs | Current | |
Read-only. Acts onjob Permission (capability) GET: List JobsVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /v1/jobs/{id} | Retrieve a single job by ID. | read | GET: Retrieve Job | Current | |
Read-only. Acts onjob Permission (capability) GET: Retrieve JobVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /v1/jobs | Create a new job. | write | POST: Create Job | Current | |
Requires the On-Behalf-Of header. Fires the job_created webhook. Acts onjob Permission (capability) POST: Create JobVersionAvailable since the API’s base version Webhook event job_createdRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| PATCH | /v1/jobs/{id} | Update a job's details. | write | PATCH: Update Job | Current | |
Requires the On-Behalf-Of header. Fires the job_updated webhook. Acts onjob Permission (capability) PATCH: Update JobVersionAvailable since the API’s base version Webhook event job_updatedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /v1/jobs/{id}/hiring_team | Get the hiring team assigned to a job. | read | GET: Get Hiring Team | Current | |
Read-only. Acts onhiring team Permission (capability) GET: Get Hiring TeamVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
Job postsList and read the public posts for jobs, and update a post or change whether it is live.3 | ||||||
| GET | /v1/job_posts | List all job posts in the organization. | read | GET: List Job Posts | Current | |
Read-only. Acts onjob post Permission (capability) GET: List Job PostsVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /v1/job_posts/{id} | Retrieve a single job post by ID. | read | GET: Retrieve Job Post | Current | |
Read-only. Acts onjob post Permission (capability) GET: Retrieve Job PostVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| PATCH | /v1/job_posts/{id}/status | Update a job post's status, such as setting it live or offline. | write | PATCH: Update Status | Current | |
Requires the On-Behalf-Of header. Fires the job_post_updated webhook. Acts onjob post Permission (capability) PATCH: Update StatusVersionAvailable since the API’s base version Webhook event job_post_updatedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
OffersList and read offers across the organization or for an application, and update the current offer on an application.3 | ||||||
| GET | /v1/offers | List all offers in the organization. | read | GET: List Offers | Current | |
Offers include pay and terms; this key reads every offer in the organization. Acts onoffer Permission (capability) GET: List OffersVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /v1/offers/{id} | Retrieve a single offer by ID. | read | GET: Retrieve Offer | Current | |
Returns the offer's pay and terms. Acts onoffer Permission (capability) GET: Retrieve OfferVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| PATCH | /v1/applications/{id}/offers/current | Update the current offer on an application. | write | PATCH: Update Offer | Current | |
Changes a real offer's pay and terms. Requires the On-Behalf-Of header. Fires the offer_updated webhook. Acts onoffer Permission (capability) PATCH: Update OfferVersionAvailable since the API’s base version Webhook event offer_updatedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
ScorecardsList and read the interview scorecards submitted across the organization or for a single application.2 | ||||||
| GET | /v1/scorecards | List all submitted scorecards in the organization. | read | GET: List Scorecards | Current | |
Scorecards hold interviewers' written assessments and ratings of candidates. Acts onscorecard Permission (capability) GET: List ScorecardsVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /v1/scorecards/{id} | Retrieve a single scorecard by ID. | read | GET: Retrieve Scorecard | Current | |
Returns one interviewer's assessment. Some question fields may contain HTML. Acts onscorecard Permission (capability) GET: Retrieve ScorecardVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
Scheduled interviewsList and read scheduled interviews, and create, update, or remove an interview on an application.3 | ||||||
| GET | /v1/scheduled_interviews | List all scheduled interviews in the organization. | read | GET: List Interviews | Current | |
Read-only. Acts onscheduled interview Permission (capability) GET: List InterviewsVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /v1/scheduled_interviews | Schedule an interview on an application. | write | POST: Create Interview | Current | |
Requires the On-Behalf-Of header. This endpoint is served by the v2 implementation. Acts onscheduled interview Permission (capability) POST: Create InterviewVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| DELETE | /v1/scheduled_interviews/{id} | Remove a scheduled interview. | write | DELETE: Remove Interview | Current | |
Requires the On-Behalf-Of header. Fires the interview_deleted webhook. Acts onscheduled interview Permission (capability) DELETE: Remove InterviewVersionAvailable since the API’s base version Webhook event interview_deletedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
UsersList and read the Greenhouse users in an organization, create a user, and enable, disable, or change a user's permission level.3 | ||||||
| GET | /v1/users | List all Greenhouse users in the organization. | read | GET: List Users | Current | |
Read-only. Acts onuser Permission (capability) GET: List UsersVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /v1/users | Add a new Greenhouse user. | write | POST: Add User | Current | |
Creates a user who can sign in to Greenhouse. Requires the On-Behalf-Of header. Acts onuser Permission (capability) POST: Add UserVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| PATCH | /v1/users/{id}/permission_level | Change a user's permission level. | write | PATCH: Permission Level | Current | |
Changes what a user can do in Greenhouse. Requires the On-Behalf-Of header. Served by the v2 implementation. Acts onuser Permission (capability) PATCH: Permission LevelVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
Departments & officesList, read, create, and update the departments and offices that organize jobs and candidates.4 | ||||||
| GET | /v1/departments | List all departments in the organization. | read | GET: List Departments | Current | |
Read-only. Acts ondepartment Permission (capability) GET: List DepartmentsVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /v1/departments | Add a new department. | write | POST: Add Department | Current | |
Requires the On-Behalf-Of header. Acts ondepartment Permission (capability) POST: Add DepartmentVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /v1/offices | List all offices in the organization. | read | GET: List Offices | Current | |
Read-only. Acts onoffice Permission (capability) GET: List OfficesVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /v1/offices | Add a new office. | write | POST: Add Office | Current | |
Requires the On-Behalf-Of header. Acts onoffice Permission (capability) POST: Add OfficeVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
Greenhouse can notify an app or AI agent when something happens in recruiting, like a candidate being hired or an application changing stage, instead of the app repeatedly asking. Greenhouse posts the event to a URL registered for the chosen events.
| Event | What it signals | Triggered by |
|---|---|---|
new_candidate_application | Fires when a new candidate application is created on a job. | /v1/candidates/{id}/applications |
candidate_stage_change | Fires when an application moves to a new interview stage. | /v1/applications/{id}/advance |
hire_candidate | Fires when a candidate is hired, on an application's offer being accepted. | /v1/applications/{id}/hire |
reject_candidate | Fires when a candidate or prospect is rejected. | /v1/applications/{id}/reject |
update_candidate | Fires when a candidate's information is created or updated. | /v1/candidates/v1/candidates/{id} |
candidate_anonymized | Fires when a candidate's personal data is anonymized. | /v1/candidates/{id}/anonymize |
job_created | Fires when a new job is created. | /v1/jobs |
job_updated | Fires when a job's details change. | /v1/jobs/{id} |
job_post_updated | Fires when a job post is modified or its live status changes. | /v1/job_posts/{id}/status |
offer_updated | Fires when an offer's information changes. | /v1/applications/{id}/offers/current |
interview_deleted | Fires when a scheduled interview is cancelled or removed. | /v1/scheduled_interviews/{id} |
Greenhouse limits how fast an app or AI agent can call the Harvest API, through a request quota measured over a rolling ten-second window rather than per method.
Greenhouse meters the Harvest API by request rate over a rolling ten-second window, not by a per-method cost. The ceiling is 50 requests per 10 seconds for approved partners and custom integrations, reported in the X-RateLimit-Limit response header. Each response also carries X-RateLimit-Remaining, the calls left in the current window, and X-RateLimit-Reset, the time the window resets. Going over returns HTTP 429, and the request should be retried after the reset time.
List endpoints use the Link response header, following the RFC 5988 convention, with rel values of next, prev, and last where they apply. The page parameter selects a page and per_page sets the page size. The next link should be followed rather than building the URL by hand, and some endpoints support only the next relation.
The per_page parameter is an integer from 1 to 500 and defaults to 100. Requests and responses are JSON. A skip_count parameter can be passed on some list endpoints to omit the total count for faster responses.
The status codes an agent should handle, and what to do about each.
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 401 | Unauthorized | The Harvest API key is missing or invalid. | Send a valid key over HTTP Basic auth, as the username with a blank password. |
| 403 | Forbidden | The key is valid but has not been granted the endpoint being called, or the request is otherwise not permitted, such as sending a body on a GET. | Grant the endpoint to the key in the Dev Center, or correct the request. |
| 404 | Not Found | The requested resource does not exist. | Check the path and the resource ID. |
| 422 | Unprocessable Entity | Validation failed: the request was well-formed but a field is missing or invalid. | Read the returned errors, correct the named fields, and resend. |
| 429 | Too Many Requests | The rate limit was exceeded for the rolling ten-second window. | Wait until the X-RateLimit-Reset time, then retry. Slow the request rate to stay under the X-RateLimit-Limit ceiling. |
| 500 | Internal Server Error | An error on Greenhouse's side. | Retry after a short wait, and contact Greenhouse support if it persists. |
Greenhouse versions the Harvest API by URL, with the original methods under v1. The v1 and v2 Harvest API is being retired on 31 August 2026 in favour of a rebuilt Harvest v3.
The Harvest API is versioned by URL, with the original methods under v1 and a few later methods served by a v2 implementation under the same paths. The v1 and v2 Harvest API is deprecated and will be removed on 31 August 2026, replaced by a rebuilt Harvest v3. The dated entries below are recent changes from the Harvest API change log; they ship continuously rather than as new URL versions.
Added match_score_reasoning and identity_verification fields to the candidate anonymization response.
Documented that scorecard and job stage question data may contain HTML formatting, so a consumer should expect to handle markup.
Updated the 403 documentation to note that sending a body on a GET request is rejected.
Added recruiter_id and coordinator_id parameters to the application endpoints, so an integration can set those assignments.
Added the video_conferencing_url parameter to interview scheduling.
Standardized documentation terminology, replacing 'web hook' with 'webhook'.
An integration on v1 or v2 should plan its move to Harvest v3 before the retirement date.
Greenhouse Harvest API change log ↗Bollard AI sits between a team's AI agents and Greenhouse. Grant each agent exactly the access it needs, read or write, resource by resource, and every call is checked and logged.