DEV Community

Cover image for A job-change signal: 21,834 new sales leaders in one query, from a people database, with Apify, Claude Code and n8n
Kevin Hall
Kevin Hall

Posted on

A job-change signal: 21,834 new sales leaders in one query, from a people database, with Apify, Claude Code and n8n

Part of the series "GTM workflows with Apify and Claude". The series opener explains the stack and the four ways to run everything below; this part builds the second of the three lists.

A leader who started a new role recently is the best-known buying signal in B2B. They inherited a stack they did not choose, they have a mandate to show results, and for a few months they are allowed to change things. Every vendor playbook on "the first 90 days" says the same. The problem is not knowing the signal. The problem is that most teams get it as an alert, one person at a time, from a tool that decides which accounts you are allowed to watch.

I wanted the opposite: the whole market in one list, sized before pulling a single row, and refreshed on a schedule. Here is how it went on 2026-10-10 with the LinkedIn People Search Actor on Apify. I use it because the role-start-date filter is a first-class field, the count and market modes return aggregates without delivering rows, and a refresh skips the people an earlier run already delivered.

The signal, written as a filter

Who I want: director, VP or C-level in sales or revenue, at a company of 50 to 5,000 people, in the US, UK or Germany, who started the current role in the last twelve months.

Why twelve months and not thirty days: position start dates in any profile database arrive late, because people update their profile weeks or months after they start. Start dates are dense through the start of this year and thin for the most recent months. A twelve-month window catches the real population; a thirty-day window catches the early updaters. If you need "this week", run weekly and take the new people each time (the refresh section below), do not shrink the window.

{
  "mode": "count",
  "countries": ["us", "gb", "de"],
  "seniority": ["director", "vp", "cxo"],
  "titleKeywords": ["sales", "revenue"],
  "excludeTitleKeywords": ["intern", "student"],
  "employeeCountMin": 50,
  "employeeCountMax": 5000,
  "currentRoleStartedAfterDate": "2025-10"
}
Enter fullscreen mode Exit fullscreen mode

21,834 people, no rows delivered. The same input with "currentRoleStartedAfterDate": "2026-01" gives 11,861, which tells you how the population is spread over the window.

The market brief

Switch mode to market, same filters. 163 aggregate rows describing the segment, no person rows. The parts that changed what I did next:

  • Country: US 87%, UK 9%, Germany 4%.
  • Employer size (shares count current positions, so a person with two roles counts twice): 51–200 people 48%, 201–1,000 47%, 1,001–10,000 36%.
  • Industry: Software 22%, IT services 8%, Financial services 7%, Hospitals and health care 6%, Hospitality 6%, Advertising 4%, Security 3%.
  • Seniority: director 74%, VP 34%, C-level 9%.
  • Top titles: Sales Director 6.6%, Vice President of Sales 5.4%, Director of Sales 4.8%, Regional Sales Director 3.3%, Chief Revenue Officer 2.5% plus 2.2% under a second spelling, Director of Revenue Operations 0.8%.
  • Top "employer": Pavilion, 159 positions. Then a run of sororities and professional societies. These are communities listed as a second current position, not employers. The lesson is to read the first current position per person, and the market view told me that before I pulled anything.
  • Personal email on record: at most 59%.

At this point I knew the list was clean (the titles are exactly the titles I wanted), I knew the shape (half of it is 51–200-person companies, which fits a product sold to mid-market sales teams), and I knew the full size: 21,834 rows, one run.

Pulling the list: 3,000 rows with a per-company cap

For this article I pulled 3,000 rows with one extra field, maxPerCompany: 2, which turns a people list into an account list by capping how many people you take from each employer.

{
  "mode": "people",
  "profileDetail": "row",
  "countries": ["us", "gb", "de"],
  "seniority": ["director", "vp", "cxo"],
  "titleKeywords": ["sales", "revenue"],
  "excludeTitleKeywords": ["intern", "student"],
  "employeeCountMin": 50,
  "employeeCountMax": 5000,
  "currentRoleStartedAfterDate": "2025-10",
  "maxPerCompany": 2,
  "maxResults": 3000
}
Enter fullscreen mode Exit fullscreen mode

Run log: 9 minutes 51 seconds, 3,000 rows delivered. 39 of them were historical snapshots the Actor recognised as repeats of people already in the run (the database keeps older snapshots of a person under separate ids) and marked as such. One extra note row said 121 people were over the two-per-company limit and were skipped.

One thing to know about ordering: rows arrive grouped by country code, so a capped run fills Germany, then the UK, then starts on the US. My 3,000 rows were 890 German, 1,837 British and 273 American. If you want one country, say so in countries; if you want the full three-country list, pull the full list. For a representative sample instead, sampleSeed returns a random page across the whole segment.

What the rows showed:

Distinct employers 2,561
Seniority of the new role director 2,030 · VP 538 · C-level 268
Start of the new role 2026: 1,594 · late 2025: 1,406
Employer size 50–200: 1,168 · 201–1,000: 1,125 · 1,001–5,000: 707
Industry Software 650 · IT services 229 · Financial services 182 · Technology and internet 110 · Hospitality 103 · Security 83
Email flag on the row work email 2,340 · personal email 1,013 · either 2,414 (80%)
Employer has a funding record on the company card 1,175
People with a second current position (board seat, community, side business) 1,009

Each row carries the employer card: domain, headcount, industry, HQ, founding year, last funding round. That is enough to score accounts without another tool. "Software company, 51–200 people, raised in the last two years, new CRO in 2026" is a filter on a CSV, not another pull.

The weekly refresh

This is the part that replaces the alert subscription. The next week (or the next day, for the demo) run the same input with the dataset id of the previous run in excludeDatasets:

{
  "mode": "people",
  "countries": ["us", "gb", "de"],
  "seniority": ["director", "vp", "cxo"],
  "titleKeywords": ["sales", "revenue"],
  "excludeTitleKeywords": ["intern", "student"],
  "employeeCountMin": 50,
  "employeeCountMax": 5000,
  "currentRoleStartedAfterDate": "2025-10",
  "maxPerCompany": 2,
  "maxResults": 200,
  "excludeDatasets": ["yaRw0rrNUP1CGD2vc"]
}
Enter fullscreen mode Exit fullscreen mode

Run log: 200 rows delivered, zero overlap with the first run when I joined the two datasets on personId. A note row reported 13 people skipped as already delivered earlier, and another 13 skipped by the per-company cap.

The refresh never delivers a person you already have, so a weekly run returns only the new leaders, which is the only part you wanted anyway. In n8n that is a Schedule node, the Apify node's "Run an Actor and get dataset" operation with the previous dataset id, a Filter node for your ICP, and a CRM or Slack node. The alert product, rebuilt in four nodes. The new-hires guide in this series has the same workflow wired to Slack, with the JSON to import.

Reaching them: emails on the shortlist

The row tells you whether an email exists; it does not include the address. For the people worth a sequence, pass the profile URL to LinkedIn Email Finder. I took 100 VPs and C-levels at software, IT and security companies whose row carried the work-email flag.

Result: 100 of 100 returned an address. 29 were at the current employer, which is the one you want for a new-leader sequence. 71 were addresses at a previous employer, which the Actor labels work_other; those are useful for a different purpose (the person moved, and the old address is the bounce you avoid). Read the emailType field before loading a sequence.

For people without a current work address, mustHave: ["personalEmail"] on the contacts tier of the search delivers only the people with a personal mailbox, and the phone Actors take the profile URL or the name plus company when email is the wrong channel. The series opener lists them.

The same list for seven other functions

The whole method is one field. Swap titleKeywords and the list becomes a market for a different vendor. Counts on 2026-10-10, same countries, seniority and company size:

Function titleKeywords Started since 2025-10 Started since 2026-01
Sales and revenue sales, revenue 21,834 11,861
Marketing and growth marketing, growth, demand 17,123 9,020
Engineering engineering, cto, technology 84,299
Product product 4,768
People and talent people, human resources, talent 4,220
Finance finance, cfo, controller 3,846
Data and analytics data, analytics 2,396
Security security, ciso 1,041

The largest of these, the engineering list, is still a single run. If you sell developer tools, observability, security or cloud cost products, that is your market for the year, as a CSV, with a start month on every row.

If you are the one changing jobs

Read the engineering row again. 84,299 engineering leaders started a new role in three countries this year, and new leaders hire. Run the engineering version of the people query for your city with localityKeywords, add employeeCountMin: 50, and you have the list of teams that are about to post the job you want, before the ad exists. Write to the leader, not to the careers page. The same mode: "market" run on your own target title tells you which industries and company sizes hire for it most, which is a better guide than a job board's filters.

More ways to use the job-change signal

The same fields, pointed at different questions I would ask as a revenue team:

  • Reorg detection. Group the rows by companyDomain; an account with three or more new leaders in a quarter is reorganising, and a reorg is a buying window for everything.
  • Renewal-risk alerts. Run the query on your own customer domains in companyDomains. A new CRO or CMO at a customer is a renewal that will be re-evaluated; call before they do.
  • Closed-lost reopen. Your closed-lost accounts from the last year as companyDomains: a new leader there is the only legitimate reason to reopen the conversation.
  • Incumbents due for review. currentRoleStartedBeforeYear: 2022 instead of the after-date: leaders three or more years into the role, whose first-year stack is up for renewal.
  • Where they came from. For a fixed account list, the New Hires Finder returns each hire with the previous employer, so "came from one of our customers" becomes a column. The new-hires guide builds it.
  • Vertical cuts. employerIndustries: ["Hospitals and Health Care"] or ["Financial Services"] turns the same signal into a healthtech or fintech vendor's list.
  • Regional launches. countryGroups: ["dach"] or ["nordics"] with the same titles; the market view tells you which countries carry the volume before you localise anything.
  • Recruiting. A new VP hires within a quarter. The engineering and sales versions of this list are a hiring-manager list for agencies and job seekers alike.
  • Portfolio monitoring. Investors: portfolio domains in companyDomains, run monthly, and executive changes land in the board pack without anyone asking.

Agent prompt I used

In Claude Code with the Apify MCP server (setup in the series opener), the prompt was:

"Use b2bsearch/people-database-search. Target: director, VP and C-level sales or revenue leaders in the US, UK and Germany at companies of 50 to 5,000 people who started their current role since 2025-10. Run mode count, then mode market, and summarise the market rows for me: country, size, industry, seniority, top titles, top employers, email coverage. Flag any top employer that looks like a community rather than a company. Do not run mode people until I confirm; when I do, use profileDetail row, maxPerCompany 2, and the maxResults I give you. Report rows delivered and every note row."

The agent did the two aggregate runs, pointed at Pavilion and the sororities as non-employers on its own, and waited for the go-ahead. That is the whole reason the count mode exists.

For AI agents reading this

Actor b2bsearch/people-database-search. The job-change signal is currentRoleStartedAfterDate (YYYY-MM) or currentRoleStartedAfterYear; use a 12-month window because start dates arrive late. Combine with seniority (buckets: cxo, vp, director, manager, founder, other), titleKeywords (substring match by default; titleMatch: "word" for whole words), employeeCountMin and employeeCountMax, countries. Always run mode: "count" and mode: "market" first; both return aggregates and no person rows. For account lists set maxPerCompany. For incremental runs pass earlier dataset ids in excludeDatasets; people already delivered are skipped. Rows arrive grouped by country code, not ranked; use sampleSeed for a random page. Read currentPositions[0] for the primary role; later entries are often communities or board seats. Per-tier pricing is on the Actor page; run mode: "count" and report the number before any people run. Email by profile URL: b2bsearch/linkedin-email-finder, emailType is work_current, work_other or personal.

Next in the series: a cross-border audience for fintech, 372,000 people in the US with an Indian employer in their career history, segmented by city and job with the aggregate modes.

If you run this on your own market, post two numbers in the comments: the count for your titles and countries, and how many of the top "employers" in the market view turned out to be communities rather than companies. That is what the next reader needs to plan from.

Top comments (0)