# Find Newly Hired Executives by Company or Country (`b2bdata/new-exec-hires`) Actor

Moved to https://apify.com/b2bdata/executive-job-change-tracker (new price, same job). This older copy is deprecated and kept for reference.

- **URL**: https://apify.com/b2bdata/new-exec-hires.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 $1.40 / 1,000 new executive delivereds

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

## New Executive Hires by Country or Domain

New executive hires: give countries, company domains or industries, get one row per C-level or VP person who started a current role in the last N months, with title, company, company domain, start year, location and profile URL. A person with several current roles is one row. No login, no cookies, no start fee. You pay only for an executive row you receive.

**No login | Countries, domains or industries in | One executive per row | Start window in months | Odd rows skipped and never charged | JSON/CSV export**

> **Read this first: start dates are years, not months.** The data holds the start YEAR of a role, never the month. The source cuts the window on its own start date, but every row shows `startYear` only, and `startPrecision` on each row says `year`. A "last 6 months" run can therefore include someone whose role began earlier in the same year. See "How to read the rows".

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

***

### 💳 Pricing and plans

One event, `new-exec`, charged once per executive row delivered. Prices per 1,000 executives (set on the Pricing tab):

| Event | When it is charged | Free plan | Gold plan |
|---|---|---|---|
| `new-exec` | An executive row is delivered to your dataset | $1.896 | $1.396 |

Bronze and Silver prices are on the Pricing tab. Higher Apify plans only lower the price. Empty runs, searches with no match, rows skipped as odd, failed lookups and notices are free, and there is no start fee.

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

#### Success-only billing

You are charged only for an executive row you receive. A country or domain with no match gets one free notice row (`reason: "no_new_execs"`, `"unknown_domain"` or `"domain_too_broad"`). A repeat person, an entry that is not a domain, or a lookup that fails is listed in `MISSES` or skipped and costs nothing. A row that does not hold up (no cxo or vp role, a start year outside your window, no company named) is skipped before charging and counted in `RUN_SUMMARY`.

#### The free plan

Free Apify accounts get up to 100 executives per run, and as many runs as their Apify credit covers. Nothing is stored on our side. A larger `limit` is lowered to 100 and one free notice row (`reason: "free_plan_limit"`) says so. Use a paid Apify plan for no per-run limit.

#### Keep a run small while you test

`limit` (default 100, maximum 10,000) caps the whole run and the spend. Try one country with `limit: 5`, read the rows, then scale up.

***

### ⚡ Why this new executive hires actor?

- **A window, not a snapshot.** `monthsBack` (default 6, up to 24) is counted from today. Only people whose current C-level or VP role started inside it are returned.
- **Start from where you sell.** Countries, an industry, or a list of target-account domains. Mix them: domains plus a country returns only the executives who joined those companies and live there.
- **Honest dates.** The row says `startYear` and `startPrecision: "year"`. No invented month.
- **No spam rows.** Thin profiles (under 10 connections and no career start) are left out by default, because they are mostly self-declared "CEO at <brand>" entries.
- **Success-only, no start fee.** No match, odd rows, repeats and notices are free.

***

### 🎯 Use cases: reach leaders while the role is new

- **Trigger outreach.** A new CTO, CMO or VP Sales reviews tools and vendors in the first months.
- **Account watch.** Run your target-account domains and see who joined.
- **Market maps.** Count new VPs in one industry and country.
- **Agent workflows.** An AI agent holds a list of domains and needs "who is new at the top".

***

### 🚀 Quick start: new executives for a country

#### 1. One country

```json
{ "countries": ["us"], "limit": 5 }
```

#### 2. New leaders at your target accounts

```json
{ "companyDomains": ["salesforce.com", "https://www.microsoft.com/about", "shopify.com"], "monthsBack": 12, "limit": 100 }
```

#### 3. One industry in two countries

```json
{ "countries": ["gb", "de"], "industries": ["Software Development"], "monthsBack": 6, "limit": 200 }
```

***

### ⚙️ Inputs

| Key | Type | Default | What it does |
|---|---|---|---|
| `countries` | array of strings | none | Two-letter codes of where the executive lives (`us`, `gb`, `de`). One search per country, in the order given; the run limit fills in that order. `UK` is refused: use `gb`. |
| `companyDomains` | array of strings | none | Only executives whose new role is at these companies. Domains, website URLs or emails (reduced to the domain). A company name is listed in `MISSES` as `invalid_domain`. At most 200. |
| `industries` | array of strings | all | Industry names of the company joined, for example `Software Development`. Up to 20. An unknown name ends the run with a free notice. Employers without an industry on file are left out. |
| `monthsBack` | integer | 6 | How far back to look, 1 to 24, counted from today. |
| `limit` | integer | 100 | Most executives delivered in the whole run, which also caps what is charged. Maximum 10,000. Free plan: 100. |
| `includeAudit` | boolean | false | Adds an `audit` object to each row. |

Give at least one of `countries` or `companyDomains`; a search needs a place or a company to start from. Any other field stops the run before a lookup with `remove unsupported fields: ...`. Nothing is charged.

***

### 📤 Output

One result row per executive. Rows follow your countries or domains in the order given. Empty values are left out.

| Key | Type | Meaning, and when it is missing |
|---|---|---|
| `signalType` | string | Always `new_exec` |
| `fullName`, `firstName`, `lastName` | string | The person's name |
| `jobTitle` | string | Title of the new role |
| `seniority` | string | `cxo` or `vp`, grouped from the title |
| `companyName` | string | Company of the new role. Missing when only a domain is on file |
| `companyDomain` | string | Company website domain. Missing for about half of the rows, because many employers have no domain on file |
| `industry` | string | Industry of the company. Missing when none is on file |
| `companyEmployeeCount` | integer | Company size on file. Missing when unknown |
| `startYear` | integer | Year the role started. The only start information in the data |
| `startPrecision` | string | Always `year` |
| `windowFrom` | string | The date your window began (today minus `monthsBack`). The cut the source applied, not a date the row can be checked against |
| `location` | string | Place on the profile |
| `country` | string | Two-letter country code |
| `headline` | string | The profile headline |
| `profileUrl`, `slug` | string | Public profile URL and slug |
| `hasWorkEmail`, `hasPersonalEmail` | boolean | Whether an address is on record. Not the address |
| `updatedAt` | string | When the profile was last refreshed in the data |
| `audit` | object | Only with `includeAudit`: `requestId`, `latencyMs`, `inputRaw` |

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

```json
{
  "signalType": "new_exec",
  "fullName": "Ann Example",
  "firstName": "Ann",
  "lastName": "Example",
  "jobTitle": "Chief Technology Officer",
  "seniority": "cxo",
  "companyName": "Acme Co",
  "companyDomain": "acme.com",
  "industry": "Software Development",
  "companyEmployeeCount": 120,
  "startYear": 2026,
  "startPrecision": "year",
  "windowFrom": "2026-05-02",
  "location": "Dublin, Ireland",
  "country": "IE",
  "headline": "CTO at Acme Co",
  "profileUrl": "https://linkedin.com/in/ann-example",
  "slug": "ann-example",
  "hasWorkEmail": false,
  "hasPersonalEmail": false,
  "updatedAt": "2026-03-02T03:04:05Z"
}
```

#### How to read the rows

- **Year precision.** The data holds a start year for a role. The window cut uses the source's own start date, which is not returned. A row can have started earlier in `startYear` than the first day of your window, never in a year before it. For month-level certainty, check the profile.
- **How recent the data is.** `updatedAt` is when the profile was last refreshed. A hire that happened after that refresh is not in the data. The newest `updatedAt` of your run is in `RUN_SUMMARY` as `newestProfileUpdate`. A short window can return few rows.
- **Seniority is grouped from the title.** `cxo` and `vp` are title buckets. A "Chief of Staff" can sit in `cxo` and an "Assistant Vice President" in `vp`. Founders are a separate bucket and are not included, so "CEO and Co-founder" may be missing. Check `jobTitle` before you act.
- **Small and new companies are included.** A one-person company with a "Chief Executive Officer" is a real row. Use `companyEmployeeCount` to filter.
- **Country is where the person lives**, not where the company is.
- **Several current roles.** A person is delivered once, under the role that matched.
- **Order.** Rows come in the order of your countries or domains, and inside each in the order the data stores them. They are not ranked by date.

#### Other records in the run

- **`MISSES`** (key-value store): `[{index, input, reason}]` for every search without a result. Reasons: `no_new_execs`, `unknown_domain`, `domain_too_broad`, `invalid_domain`, `duplicate_input`, `not_looked_up_limit`, or `Upstream HTTP <code>`.
- **`errors-new-exec-hires`**: searches that failed for a technical reason, so you can re-run only those. `(partial: ...)` means some executives were already delivered.
- **`RUN_SUMMARY`**: window, rows delivered, repeats skipped, odd rows skipped by reason, `newestProfileUpdate`, and per search how many were delivered.
- **Notice rows** (`type: "notice"`): no match, platform domain, unknown industry (`unknown_industry`), a lowered free-plan limit (`free_plan_limit`), a reached `limit` (`limit_reached`), a reached spend limit (`charge_limit_reached`) or invalid input (`invalid-input`). Never charged.

#### What is NOT returned

- Email addresses or phone numbers (use [email-finder](https://apify.com/b2bdata/email-finder))
- Exact start dates or months
- People who left a company (use `b2bdata/departures-by-company` when it is available)
- Directors, managers or founders

***

### 🛡️ Reliable results

**Our data promise.** We work to keep the data current, and we stand behind it. If an executive has moved on, a role looks wrong or a row is off, open the Issues tab with your run link. We review every report and sort it out with you, case by case.

- **No match is free and visible.** Every search without a row is in `MISSES` or a notice, and is never charged.
- **Odd rows are not sold.** A row without a cxo or vp role, with a start year outside your window or with no company is skipped before charging.
- **Repeats are charged once.** The same person found twice is one row.
- **Each row is saved as it is delivered.** A run that stops, or fails halfway, keeps what it already produced.
- **Typos fail early.** An unsupported field, a bad country code or a limit above its maximum stops the run before any charge.

***

### 🤖 For AI agents (MCP)

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

> "Use b2bdata/new-exec-hires with countries=\["us"], industries=\["Software Development"], monthsBack=6 and limit=20. Return fullName, jobTitle, companyName, companyDomain, startYear and profileUrl."

**When to use it.** You want C-level or VP people who recently started a role, by place, industry or company.
**When not to.** You need people who left a company, exact start months, or email addresses (use b2bdata/email-finder).
**Input contract.** At least one of `countries` (ISO-2 codes) or `companyDomains`; optional `industries`, `monthsBack` 1-24 (default 6), `limit` 1-10,000 (default 100).
**Output contract.** One row per executive with `fullName`, `jobTitle`, `seniority`, `companyName`, `companyDomain`, `startYear`, `startPrecision`, `profileUrl`. Output is deterministic and schema-stable. No match is a free notice row.
**Smoke test.** `{"countries": ["us"], "limit": 1}` returns one row.
**Error cases.** Neither `countries` nor `companyDomains`, an unsupported field, `UK` instead of `gb`, an unknown industry or a bad number ends the run SUCCEEDED with one free 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 `companyDomain` and map `fullName`, `jobTitle` and `profileUrl` to contact fields.

***

### 🕐 Schedule examples

- **Weekly new-leader watch.** A saved task with your target-account domains and `monthsBack: 3`, run weekly; join on `profileUrl` to skip rows you already saw.
- **Monthly market scan.** One task per country with `monthsBack: 1`.

***

### 🔗 Related actors

- `b2bdata/people-at-company`: every employee of a company by domain, filtered by seniority. [Open](https://apify.com/b2bdata/people-at-company)
- `b2bdata/email-finder`: a work email for a person at a domain. [Open](https://apify.com/b2bdata/email-finder)
- `b2bdata/linkedin-people-search`: search people across companies by title and location. [Open](https://apify.com/b2bdata/linkedin-people-search)
- `b2bdata/headcount-by-department`: how many people a company has per team. [Open](https://apify.com/b2bdata/headcount-by-department)

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

***

### ❓ FAQ

#### Data and coverage

**How do I find executives who recently started a new role?**
Give `countries` or `companyDomains` and set `monthsBack`. You get one row per C-level or VP person whose current role started inside the window.

**Can I see the month an executive started?**
No. The data holds the start year only. Rows show `startYear` and `startPrecision: "year"`.

**Why did I get few rows for a short window?**
The data is a snapshot refreshed on a schedule. `newestProfileUpdate` in `RUN_SUMMARY` shows the newest profile in your run. A move after the last refresh is not in it.

**Why is a company domain missing on some rows?**
Many employers have no website domain on file. The row keeps `companyName` and leaves the domain out; it is never guessed.

**Do I get email addresses?**
No. `hasWorkEmail` and `hasPersonalEmail` say whether an address is on record. Use `b2bdata/email-finder` to find one.

#### Pricing

**What do I pay when nothing matches?**
Nothing. There is no start fee, and no-match searches, skipped odd rows, repeats and notices are free.

***

### ⚖️ Legal

Use for B2B prospecting only. Comply with all applicable laws including GDPR, CCPA, and CAN-SPAM.

The rows come from public professional information. You decide how to use them, and you are responsible for lawful use in your own region.

***

### 🛟 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/new-exec-hires/changelog.md

# Actor input Schema

## `countries` (type: `array`):

Two-letter country codes of where the executive lives, for example \["us", "gb", "de"] (the United Kingdom is gb). One search per country, in the order you give; the run limit fills in that order. Give countries, companyDomains, or both. Omit both and the run ends with a free notice.

## `companyDomains` (type: `array`):

Only executives whose NEW role is at these companies. Example: \["salesforce.com", "https://www.microsoft.com/about", "jane@stripe.com"]. A website URL or email is reduced to its domain; a company name is not a domain and is listed in MISSES as invalid_domain. One search per domain, at most 200. Combined with countries, both must match. Omit to search by country only.

## `industries` (type: `array`):

Industry names of the company the executive joined, for example \["Software Development", "Financial Services"]. Up to 20. An unknown name ends the run with a free notice. Only about 59 percent of people have an employer with an industry on file, so this filter leaves out the rest. Omit for every industry.

## `monthsBack` (type: `integer`):

How far back to look, counted from today. Default 6, maximum 24. The data holds a start YEAR for each role, so a row is checked against the start year of the window and the source cuts on its own start date; rows show startYear only, never a month.

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

Most executives delivered in the whole run, which also caps what is charged. Default 100, maximum 10,000. Free plan: 100 per run. Searches not reached are listed in MISSES and are not charged.

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

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

## Actor input object example

```json
{
  "countries": [
    "us"
  ],
  "monthsBack": 6,
  "limit": 100,
  "includeAudit": false
}
```

# Actor output Schema

## `people` (type: `string`):

One row per new executive: name, title, seniority, company, domain, start year, location and profile URL

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

Counts, people per domain 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 = {
    "countries": [
        "us"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("b2bdata/new-exec-hires").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 = { "countries": ["us"] }

# Run the Actor and wait for it to finish
run = client.actor("b2bdata/new-exec-hires").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 '{
  "countries": [
    "us"
  ]
}' |
apify call b2bdata/new-exec-hires --silent --output-dataset

```

## MCP server setup

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

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/DgIbICeKGoMfAbMQw/builds/MgnocmbANlFURF9wK/openapi.json
