# Company Org Size by Department - Headcount by Domain (`b2bdata/headcount-by-department`) Actor

Headcount by department for company domains: total headcount, people per department and seniority, and the top department. Counts are people on file, not the company's own HR numbers. Free plan: 100 domains per run. Pay per domain with data, empty lookups free. No login or cookies.

- **URL**: https://apify.com/b2bdata/headcount-by-department.md
- **Developed by:** [Open Enrich](https://apify.com/b2bdata) (community)
- **Categories:** Lead generation, Marketing
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.70 / 1,000 headcount rows

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Headcount by Department - Company Org Size by Domain

Headcount by department for any company domain: give a list of domains, get one row per company with its total headcount, people per department, people per seniority level and the top department. Counts are what the data holds: the people on file as currently working at the company. No login, no cookies, no start fee. You pay only for a domain that returns a headcount.

**No login | Domains, URLs or emails in | Total, departments, seniority, top department out | One row per domain | Up to 1,000 domains per run | No data = free | JSON/CSV export**

> **Not a headcount history.** This actor reports the count today, per run. It does not report growth, hiring or change over time, and it does not list the people. For the people themselves use [linkedin-employee-search](https://apify.com/b2bdata/linkedin-employee-search).

[See all actors by b2bdata](https://apify.com/b2bdata)

***

### 💳 Pricing and plans

One event, `headcount`, charged once per domain that returns a result row. Prices per 1,000 domains (set on the Pricing tab):

| Event | When it is charged | Free plan | Gold plan |
|---|---|---|---|
| `headcount` | A domain has people on file and its headcount row is delivered | $0.963 | $0.696 |

Bronze and Silver prices are on the Pricing tab. Higher Apify plans only lower the price. Empty runs, domains with fewer than 5 people on file, failed lookups, unreadable entries and notices are free, and there is no start fee.

**Plan-credit math.** The free plan's $5 monthly credit covers about 5,190 domains at the Free price.

#### Success-only billing

You are charged only for a result row you receive. A domain with fewer than 5 people on file gets one free notice row (`reason: "no_data"`), because a match that small is not a company headcount. An entry that is not a domain, a repeat, or a lookup that fails is listed in `MISSES` with its position and a reason and costs nothing.

#### The free plan

Free Apify accounts get up to 100 domains looked up per run, and as many runs as their Apify credit covers. Nothing is stored on our side. When a list is longer, the first 100 valid domains are looked up and one free notice row (`reason: "free_plan_limit"`) says how many were not looked up; they are not charged. Run again with the rest, or use a paid Apify plan for no per-run limit.

#### Keep a run small while you test

Use `limit` to cap the number of domains looked up. The default is 100, the maximum is 1,000, `0` means all valid domains up to 1,000. Try 1 to 3 domains first, read the row, then scale up.

***

### ⚡ Why this headcount by department actor?

- **Domain in, org size out.** One company domain gives one row: total headcount, departments, seniority levels and the top department. No search to build, no pages to walk.
- **Takes the input you have.** A bare domain, a website URL (`https://www.stripe.com/about`) or an email address (`jane@stripe.com`) all become `stripe.com`.
- **Counts, not guesses.** Every number is a count from the data. Nothing is estimated, and there is no growth or trend figure.
- **Success-only, no start fee.** A domain with no data, a bad entry or a failed lookup is free.
- **Deterministic.** The same domain returns the same keys in the same order: departments and seniority levels sort by count, then name.

***

### 🎯 Use cases: size a company by team

- **Account scoring.** Rank a target list by engineering or sales headcount before outreach.
- **Territory and ICP fit.** Keep accounts with a real sales or product team, drop the rest.
- **Competitor mapping.** See where a rival puts its people: engineering heavy, sales heavy or balanced.
- **Agent workflows.** An AI agent holds a table of company domains and needs org size for all of them in one call.

***

### 🚀 Quick start: headcount for a list of domains

#### 1. One domain

```json
{ "items": ["stripe.com"], "limit": 1 }
```

#### 2. A mixed list

```json
{ "items": ["openai.com", "https://www.microsoft.com/en-us/about", "jane.doe@shopify.com"], "limit": 0 }
```

Each entry becomes its bare domain before the lookup.

***

### ⚙️ Inputs

| Key | Type | Default | What it does |
|---|---|---|---|
| `items` | array of strings | required | Company domains, website URLs or email addresses. A company name is not a domain and is listed in `MISSES` as `invalid_domain`. The same domain written twice is looked up once. |
| `limit` | integer | 100 | Maximum domains looked up, which also caps the domains charged. `0` = all valid domains. Maximum 1,000. |
| `includeAudit` | boolean | false | Adds an `audit` object to each result. |

Any other field stops the run before a lookup with `remove unsupported fields: ...`. Nothing is charged.

***

### 📤 Output

One result row per domain that has people on file. Results keep input order. Empty values are left out.

| Key | Type | Meaning, and when it is missing |
|---|---|---|
| `domain` | string | The domain from your entry, cleaned |
| `company` | string | The company name with the most people on file for this domain. Missing when none is reported |
| `totalHeadcount` | integer | People on file as currently working at the companies that declare this domain |
| `departments` | object | People per department: `{ "Engineering": 1200, "Sales": 400 }`, largest first. Covers people whose job title falls in the company's most common titles, see below |
| `classifiedHeadcount` | integer | The sum of `departments`. At most `totalHeadcount` |
| `seniority` | object | People per seniority level (`cxo`, `vp`, `director`, `manager`, `founder`, `other`), largest first |
| `topDepartment` | string | The department with the most people in `departments`. Missing when no title maps to a department |
| `asOf` | string | The date of the run (UTC) |
| `audit` | object | Only with `includeAudit`: `requestId`, `latencyMs`, `inputRaw` |

Example output (the shape is real, the numbers are illustrative):

```json
{
  "domain": "stripe.com",
  "company": "Stripe",
  "totalHeadcount": 8421,
  "departments": { "Engineering": 1200, "Sales": 400, "Product": 150, "People": 120, "Design": 80, "Data": 60 },
  "classifiedHeadcount": 2010,
  "seniority": { "other": 7000, "manager": 900, "director": 300, "vp": 150, "cxo": 40, "founder": 31 },
  "topDepartment": "Engineering",
  "asOf": "2026-11-02"
}
```

#### How to read the counts

- **What `totalHeadcount` is.** The number of people the data holds as currently employed at the companies that declare the domain. It is not the company's own employee figure, and people without a profile are not in it.
- **How departments are made.** The data has no department field. Departments are grouped from the company's most common job titles (up to the top 100): Sales, Customer Success, Marketing, Design, Finance, Data, Engineering, Product, People, Legal and Operations. The first matching rule wins, so a "Sales Engineer" counts as Sales and a "Product Designer" as Design. A title that matches no rule, or that is not among the most common titles, is not in `departments`. For a large company the departments cover part of `totalHeadcount`; `classifiedHeadcount` says how much. The title counts behind this are close, not exact.
- **Seniority** counts current positions, so a person with two current roles counts twice. Its sum can be above `totalHeadcount`.

#### Other records in the run

- **`MISSES`** (key-value store): `[{index, input, reason}]` for every entry without a result row. Reasons: `no_data`, `invalid_domain`, `duplicate_input` (the same domain as an earlier entry; `duplicateOf` names it), or `Upstream HTTP <code>`.
- **`errors-headcount-by-department`** (key-value store): the lookups that failed for a technical reason, with their position, so you can re-run only those domains.
- **`RUN_SUMMARY`**: counts (requested, valid, looked up, rows delivered, no data, not looked up because of `limit` or the free plan) and the stop reason.
- **Notice rows** (`type: "notice"`) appear for a domain with no data (`no_data`), when a free-plan list is longer than 100 (`free_plan_limit`), when the spend limit of your run is reached (`charge_limit_reached`), or when the input is invalid (`invalid-input`). Notices are never charged.

#### What is NOT returned

- The people behind the counts (use [linkedin-employee-search](https://apify.com/b2bdata/linkedin-employee-search))
- Headcount growth or any change over time
- Email addresses or phone numbers
- Company details such as industry or funding (use [company-firmographics-by-domain](https://apify.com/b2bdata/company-firmographics-by-domain))

***

### 🛡️ Reliable results

**Our data promise.** We work to keep the data current, and we stand behind it. If a count looks wrong or a company is missing, open the Issues tab with your run link. We review every report and sort it out with you, case by case.

- **No data is free and visible.** Every domain without a row is in `MISSES` with its position and a reason and is never charged.
- **Repeats are looked up once.** The same domain written twice is looked up and charged once; the repeat is in `MISSES` as `duplicate_input`.
- **Each row is saved as it is delivered.** A run that stops keeps what it already produced.
- **Bounded pace.** Lookups run at 60 per minute, so 1,000 domains need roughly 17 minutes.
- **Typos fail early.** An unsupported input field or a limit above 1,000 stops the run before any charge.

***

### 🤖 For AI agents (MCP)

This actor runs through the Apify MCP server. Prompt to copy:

> "Use b2bdata/headcount-by-department with items=\["stripe.com"] and limit=1. Return domain, totalHeadcount, topDepartment, departments and seniority."

**When to use it.** You hold company domains and need how many people work there and in which teams.
**When not to.** You need the people (use b2bdata/linkedin-employee-search), company facts (use b2bdata/company-firmographics-by-domain) or headcount over time (not offered).
**Input contract.** `items` array of domains, URLs or emails; `limit` 0-1,000 (default 100, 0 = all).
**Output contract.** One row per domain with `domain`, `totalHeadcount`, `departments`, `seniority`, `topDepartment`. Output is deterministic and schema-stable, rows keep input order. Domains without data are a free notice row and listed in `MISSES`.
**Smoke test.** `limit: 1` on a well-known domain returns one row.
**Error cases.** `No valid domains` means no entry was a domain. `remove unsupported fields` or a bad `limit` ends the run SUCCEEDED with one free `invalid-input` notice row and nothing charged.

***

### 🔌 Integrations: send results anywhere

1. **API.** Fetch rows from the run's default dataset: `GET /v2/datasets/{datasetId}/items?format=csv`.
2. **Webhook** on run success to push rows to your own endpoint.
3. **n8n, Make, Zapier.** Use the native Apify nodes: run the actor, then read the dataset.
4. **Google Sheets, Airtable, HubSpot, Salesforce, Clay.** Join on `domain` and map `totalHeadcount` and `topDepartment` to account fields.

***

### 🕐 Schedule examples

- **Monthly account refresh.** A schedule with a saved task that points at your target-account list.
- **New-account scoring.** A webhook from your CRM starts a run with the new domains.

***

### 🔗 Related actors

- `b2bdata/linkedin-employee-search`: the people at a company, not just the counts. [Open](https://apify.com/b2bdata/linkedin-employee-search)
- `b2bdata/company-firmographics-by-domain`: industry, size and location for a domain. [Open](https://apify.com/b2bdata/company-firmographics-by-domain)
- `b2bdata/linkedin-company-search`: find companies to size. [Open](https://apify.com/b2bdata/linkedin-company-search)
- `b2bdata/executive-contact-finder-by-domain`: the decision makers at a domain. [Open](https://apify.com/b2bdata/executive-contact-finder-by-domain)

[See all actors by b2bdata](https://apify.com/b2bdata)

***

### ❓ FAQ

#### Data and coverage

**How do I get headcount by department for a company?**
Put the company's domain in `items` and run. You get one row with the total, the people per department, the people per seniority level and the top department.

**Where do the department numbers come from?**
The data has no department field. Departments are grouped from the company's most common job titles by fixed rules, listed in the Output section. A title outside the most common ones, or one that matches no rule, is not counted in a department.

**Why is the sum of the departments lower than the total?**
`departments` covers only the people whose title falls in the most common titles and in a department rule. `classifiedHeadcount` is that sum. The rest are in `totalHeadcount` only.

**Is totalHeadcount the company's real employee number?**
No. It is the number of people the data holds as currently working at the companies that declare the domain. Use it to compare companies and teams, not as an official figure.

**Do I need a LinkedIn account or cookies?**
No. It is a headcount lookup without login, cookies or an account to ban.

#### Pricing

**What do I pay when a domain has no data?**
Nothing. There is no start fee, and domains without data, bad entries, repeats and notices are free.

***

### 🛟 Support

Use the **Issues** tab on this actor's Apify page for bugs, questions and feature requests. We answer within 48 hours on working days. Please include the run ID and the input you used. Published on Apify by b2bdata. Not affiliated with, endorsed by, or sponsored by any company or person named in the examples above.

# Changelog

This Actor's version history is a separate document: https://apify.com/b2bdata/headcount-by-department/changelog.md

# Actor input Schema

## `items` (type: `array`):

Company domains to look up. Example: \["stripe.com", "https://www.openai.com/about", "jane@microsoft.com"]. A website URL or an email address is reduced to its domain; a company name is not a domain and is listed in the MISSES record as invalid_domain. Each domain is looked up once; repeats are listed in MISSES as duplicate_input. A domain with no people on file returns one free notice row.

## `limit` (type: `integer`):

Maximum domains looked up in this run, which also caps the domains charged. 0 = all valid domains, up to the hard maximum of 1000. Above 1000 the run stops with a message; split the list.

## `includeAudit` (type: `boolean`):

Adds requestId, latencyMs and inputRaw to each row under audit. Omit for clean rows.

## Actor input object example

```json
{
  "items": [
    "stripe.com"
  ],
  "limit": 100,
  "includeAudit": false
}
```

# Actor output Schema

## `headcount` (type: `string`):

Domain, company, total headcount, people per department and seniority level, top department per domain

## `summary` (type: `string`):

Counts and stop reason for this run

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "items": [
        "stripe.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("b2bdata/headcount-by-department").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "items": ["stripe.com"] }

# Run the Actor and wait for it to finish
run = client.actor("b2bdata/headcount-by-department").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "items": [
    "stripe.com"
  ]
}' |
apify call b2bdata/headcount-by-department --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,b2bdata/headcount-by-department"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/mkwGnGy4wJhosUpKp/builds/2kegG0REigfvqvmYD/openapi.json
