Unified.to
All articles

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:

  • id
  • employment_status
  • employment_type
  • hired_at
  • terminated_at
  • updated_at
  • groups[]
  • locations[]
  • company_id

Important constraints:

  • Compensation is not required for headcount.
  • Payroll artifacts are not used to infer employment.
  • updated_at reflects changes to the employee record, not necessarily employment events.

Groups (HrisGroup)

Groups define organizational attribution.

Relevant fields:

  • id
  • name
  • parent_id
  • type
  • is_active
  • company_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:

  • id
  • name
  • address (region, country)
  • is_active
  • company_id

Locations support regional planning and geo-based hiring analysis.

Companies (HrisCompany)

Companies define scope.

Relevant fields:

  • id
  • name

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_at is 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.

Unified supports incremental refresh across all HR objects.

Typical workflow:

  1. Initial full load
  2. Persist updated_at watermark
  3. Re-fetch with updated_gte
  4. 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.

Start your 30-day free trial

Book a demo

All articles