How to Forecast Headcount and Hiring Needs with Unified's HR & Directory API
February 2, 2026
Headcount forecasting fails when teams don't agree on what 'headcount' actually means.
Across HR systems, workforce size is modeled inconsistently. Some platforms treat contractors as employees. Others exclude interns or seasonal workers. Employment start dates, termination timing, and organizational attribution vary widely. In many systems, historical changes are reconstructed indirectly or inferred from payroll or provisioning data—often incorrectly.
For a PM or FP&A team, this creates immediate risk:
- Who counts as headcount today?
- When did growth or attrition actually occur?
- Are changes tied to departments, locations, or companies consistently?
- Can forecasts be explained and defended when numbers are questioned?
Many products attempt to solve this by inventing 'employment history' models or extrapolating from adjacent data like payroll or benefits. That works until customers audit the numbers or compare results across HR systems.
Unified's HR & Directory API takes a stricter approach. It does not predict hiring outcomes or infer workforce changes. Instead, it exposes observable employment state and change—employees, employment status, start and end dates, and organizational attribution—through a normalized, real-time interface.
This guide shows how to build a defensible headcount baseline and hiring trend analysis on top of that model. The output isn't a prediction engine. It's a clean, auditable foundation that forecasting tools depend on.
What 'forecasting' means in this context
This article focuses on enabling forecasting, not performing it.
We will:
- Establish current headcount accurately
- Measure observed changes over time (hires and exits)
- Segment headcount by department, location, and employment type
We will not:
- Predict future hires automatically
- Infer compensation or capacity
- Reconstruct undocumented employment history
Those decisions belong in your planning models, not the integration layer.
The mental model: headcount is time-bound
Headcount is not a static count of records. It is a time-bound property of employees.
At any point in time, an employee is either:
- Employed
- Not employed
Changes occur at discrete moments:
- Hiring (
hired_at) - Termination (
terminated_at) - Status transitions (
employment_status)
Unified exposes those moments explicitly. Your forecasting logic should build on them, not guess around them.
Objects you'll use
Employees (HrisEmployee)
Employees are the source of truth for headcount state and change.
Relevant fields:
idemployment_statusemployment_typehired_atterminated_atupdated_atgroups[]locations[]company_id
Important constraints:
- Compensation is not required for headcount.
- Payroll artifacts are not used to infer employment.
updated_atreflects changes to the employee record, not necessarily employment events.
Groups (HrisGroup)
Groups define organizational attribution.
Relevant fields:
idnameparent_idtypeis_activecompany_id
Groups enable rollups by department, division, or business unit. Their semantics are customer-defined and should not be hardcoded.
Locations (HrisLocation)
Locations define geographic attribution.
Relevant fields:
idnameaddress(region, country)is_activecompany_id
Locations support regional planning and geo-based hiring analysis.
Companies (HrisCompany)
Companies define scope.
Relevant fields:
idname
This allows headcount planning across subsidiaries or legal entities without assuming a single global org.
Step 1: Fetch employees incrementally
Employee lists are paginated and support incremental refresh via updated_gte.
import { UnifiedTo } from '@unified-api/typescript-sdk';
const sdk = new UnifiedTo({
security: { jwt: process.env.UNIFIED_API_KEY! },
});
async function fetchEmployees(connectionId: string) {
const out = [];
let offset = 0;
const limit = 100;
while (true) {
const page = await sdk.hris.listHrisEmployees({
connectionId,
limit,
offset,
sort: 'updated_at',
order: 'asc',
fields: [
'id',
'employment_status',
'employment_type',
'hired_at',
'terminated_at',
'updated_at',
'groups',
'locations',
'company_id',
].join(','),
});
if (!page || page.length === 0) break;
out.push(...page);
offset += limit;
}
return out;
}
This is sufficient for both current state and change detection.
Step 2: Define 'current headcount'
Headcount definitions vary by product. Unified leaves this policy to you.
A common baseline:
employment_status === 'ACTIVE'terminated_atis null
function currentHeadcount(employees: any[]) {
return employees.filter(
(e) => e.employment_status === 'ACTIVE' && !e.terminated_at
);
}
You may choose to:
- Exclude contractors
- Include interns
- Handle seasonal workers differently
Those rules are explicit and configurable.
Step 3: Segment headcount by org and location
Once you have the current population, segmentation is a join problem.
Examples:
- Headcount by department:
employee.groups[].id - Headcount by location:
employee.locations[].id - Headcount by company:
employee.company_id - Headcount by employment type:
employee.employment_type
Because groups and locations are normalized objects, rollups are consistent across HRISs.
Step 4: Observe hiring and attrition over time
Observed change is derived from dates, not inferred history.
Hires
function hiresBetween(employees: any[], start: Date, end: Date) {
return employees.filter((e) => {
if (!e.hired_at) return false;
const hired = new Date(e.hired_at);
return hired >= start && hired <= end;
});
}
Terminations
function terminationsBetween(employees: any[], start: Date, end: Date) {
return employees.filter((e) => {
if (!e.terminated_at) return false;
const terminated = new Date(e.terminated_at);
return terminated >= start && terminated <= end;
});
}
These counts can be grouped by department, location, or company using the same joins as current headcount.
Step 5: Track trends with incremental updates
Unified supports incremental refresh across all HR objects.
Typical workflow:
- Initial full load
- Persist
updated_atwatermark - Re-fetch with
updated_gte - Recompute affected aggregates only
This allows near-real-time planning dashboards without rebuilding your entire dataset.
What Unified does not infer
Unified intentionally does not:
- Predict future hires
- Infer employment from payroll or benefits
- Reconstruct undocumented job history
- Normalize customer-specific policies
This keeps headcount analysis explainable and auditable.
How this supports forecasting
With a correct baseline and observed trends, your product can:
- Model growth scenarios
- Compare hiring velocity across teams
- Plan regional expansion
- Benchmark attrition rates
The forecasting logic lives in your application. Unified ensures the data feeding it is consistent and defensible.
Closing thoughts
Headcount forecasting doesn't start with prediction. It starts with a correct definition of who is employed, when changes occurred, and how people are attributed across the organization.
Unified's HR & Directory API gives you that foundation: explicit employment state, observable change, and normalized organizational context—without guessing, reconstructing history, or embedding vendor-specific logic.
From there, forecasting becomes a planning problem, not an integration problem.