People Activity API Tutorial: Build an Executive-Move Intelligence Feed in 2026

Flat vector illustration of an alert card showing an executive's title change flowing into Slack and CRM icons

Disclosure: This article is published by Datamagnet. Vendor claims are self-reported unless otherwise noted.

People Activity API Tutorial: Build an Executive-Move Intelligence Feed in 2026

Here's a workflow most GTM and recruiting teams still run by hand: someone checks a target account's leadership page every few weeks, notices a new CRO or a departed VP, and forwards a screenshot to Slack. By the time that happens, the deal has already been reassigned or the seat has already been filled by an internal promotion nobody outside the company saw coming.

This tutorial builds the automated version. You'll watch a list of target accounts for leadership changes, catch new-executive announcements and departures as they happen, and push each event straight into Slack or your CRM — using Datamagnet's People Activity endpoint alongside its Signal API for structural job changes.

Key Takeaways

  • In 2025, public-company CEO exits hit a record 446, even as total CEO departures fell 9% to 2,032 (Challenger, Gray & Christmas, 2025).
  • CRO tenure averages just 25 months — the shortest of any C-suite role, versus 4.3 years for CMOs (Harvard Business Review, 2024; Spencer Stuart, 2025).
  • A job_change signal monitor catches structural title/employer changes via webhook; the People Activity endpoint catches the "excited to announce" post that often surfaces days earlier — you need both, not one or the other.
  • Route the same event two ways: sales gets a reopened-deal alert, recruiting gets an open-role alert, from a single feed.

Flat vector illustration of an alert card showing an executive's title change flowing into Slack and CRM icons

What Will You Build?

By the end of this tutorial, you'll have a small service that watches a list of LinkedIn profile URLs — your target accounts' executives — and emits a normalized event the moment one of them changes jobs, announces a move, or gets promoted. Two input lanes feed one merge step, which fans out to Slack and your CRM.

What it does:

  • Registers a job_change signal monitor on your watchlist so structural profile changes (new employer, new title) push a webhook the instant Datamagnet detects them
  • Polls the People Activity endpoint on a schedule to catch announcement posts — "thrilled to join," "next chapter," "excited to announce" — often before the structured profile field updates
  • Deduplicates both sources into one event stream, tagged by account and by whether it's a departure or an arrival
  • Pushes a formatted alert to a Slack channel and writes a note to the matching CRM record

Flat vector diagram of a job_change webhook lane and a People Activity polling lane merging into one deduplicated feed that fans out to Slack and CRM

Why Are Executive Moves a High-Value Signal for Both Sales and Recruiting?

A new decision-maker at a target account often means a stalled deal is worth reopening, while a departure at that same account usually means a leadership seat just opened up — and both read off the same event. In 2025, public-company CEO exits hit an all-time high of 446, up from 373 the year before, even as total CEO departures across all organizations fell 9% to 2,032 (Challenger, Gray & Christmas, 2025 CEO Turnover Report, 2025). Turnover below the CEO seat is moving just as fast, if not faster.

Citation capsule: CRO tenure now averages just 25 months — among the shortest of any C-suite role — and 62% of companies see revenue growth decline or stay flat in the fiscal year after a CRO transition (Harvard Business Review, The High Costs of Chief Revenue Officer Turnover, 2024). CMOs last longer, at 4.3 years on average, but that's still a fraction of a typical multi-year enterprise deal cycle (Spencer Stuart, CMO Tenure Study 2025, 2025).

CMO vs. CRO: Who Lasts Longer in the C-Suite? Horizontal bar chart. CMO average tenure: 4.3 years (about 52 months) at Fortune 500 companies. CRO average tenure: 25 months, about 2.1 years. Sources: Spencer Stuart CMO Tenure Study 2025; Harvard Business Review analysis of SBI Growth data, 2024. CMO vs. CRO: Who Lasts Longer? CMO 4.3 yrs CRO 25 mo Sources: Spencer Stuart CMO Tenure Study, 2025; Harvard Business Review / SBI Growth, 2024
CMO tenure at Fortune 500 companies averages 4.3 years — more than double a CRO's 25-month average run. Sources: Spencer Stuart, 2025; Harvard Business Review/SBI Growth, 2024.

On the recruiting side, a departure at a target company creates the open req before it's ever posted publicly, and it isn't cheap to fill: executive hires cost roughly seven times more to source than nonexecutive ones, averaging $35,879 per hire versus $5,475 (SHRM, 2025 Benchmarking Reports, 2025). Roughly 86% of B2B purchases also stall at some point in the buying cycle (Forrester, The State Of Business Buying, 2024), and a leadership change is one of the few events reliably shown to restart a stalled one — which is exactly why both teams want the same alert, routed differently.

Prerequisites

You'll need a Datamagnet API key, a place to receive webhook events, and a scheduler for polling. None of this requires more than basic REST API familiarity.

You'll need:

  • A Datamagnet API key (authentication guide)
  • Node.js 18+ or Python 3.10+ (examples below use Node)
  • A public HTTPS endpoint to receive webhooks (a serverless function or a small Express app behind a reverse proxy both work)
  • A cron scheduler (a hosted cron service or a simple node-cron job) for the polling lane
  • A Slack incoming webhook URL and/or a CRM API key
  • ~45 minutes to complete

Tested on: Node.js 20, macOS and Linux.

Step 1: How Do You Build Your Target-Account Watchlist?

In this step, you'll assemble the list of LinkedIn profile URLs the rest of the pipeline will monitor, so every downstream signal has a known set of people to check against. Start with the executives at accounts you already sell into or recruit against — champions, economic buyers, and department heads at companies on your target list.

If you don't have named contacts yet, pull them with a filtered search instead of guessing:

curl -G https://api.datamagnet.co/api/v1/icp-people-search \
  -H "Authorization: Bearer $DATAMAGNET_API_KEY" \
  --data-urlencode "job_title=VP Sales,Chief Revenue Officer,VP Engineering,Chief Technology Officer" \
  --data-urlencode "company_name=Acme Corp,Globex,Initech" \
  --data-urlencode "seniority=VP,C-Level"

Expected output:

{
  "results": [
    {
      "name": "Jordan Reyes",
      "title": "VP Sales",
      "company": "Acme Corp",
      "profile_url": "https://www.linkedin.com/in/jordan-reyes-example"
    }
  ],
  "page": 1,
  "total_results": 14
}

Store each profile_url in a watchlist table alongside the account name and a tag (sales or recruiting, or both) so later steps know how to route the alert. The ICP Search filter reference documents every filter available if you need to narrow this list further.

Step 2: How Do You Catch Structural Job Changes With a job_change Signal Monitor?

This step catches the clean case — someone's employer or title field on LinkedIn actually changes — and delivers it to you as a webhook the moment Datamagnet detects it, so you're not the one polling for it. Register one signal monitor against your entire watchlist rather than one per person.

curl -X POST https://api.datamagnet.co/api/v1/signal/create \
  -H "Authorization: Bearer $DATAMAGNET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "signal_type": "job_change",
    "profile_urls": [
      "https://www.linkedin.com/in/jordan-reyes-example",
      "https://www.linkedin.com/in/priya-menon-example"
    ],
    "webhook_url": "https://yourapp.com/webhooks/datamagnet",
    "webhook_secret": "a-long-random-string-you-generate"
  }'

What just happened: Datamagnet now checks every profile in profile_urls on an ongoing basis and fires a webhook, signed with your webhook_secret, to webhook_url the moment a tracked profile's employer or title changes — no cron job of your own required for this half of the pipeline. The Create Signal endpoint documents the full request schema, and the webhooks reference covers payload format, retry behavior, and HMAC signature verification.

Watch out: verify the X-Datamagnet-Signature header (an HMAC-SHA256 of the raw request body) on every webhook before you trust the payload, using a timing-safe comparison like Node's crypto.timingSafeEqual or Python's hmac.compare_digest. Skipping this step means anyone who guesses your endpoint URL can inject fake departure events into your feed.

Datamagnet's Champion Tracker cookbook walks through a full job_change signal reference implementation if you want to see this step wired into a complete account-monitoring setup.

A monitor like this earns its keep because the underlying churn rate keeps climbing. In 2025, public-company CEO exits reached 446 — a record high — while total CEO departures across all organizations dipped to 2,032 (Challenger, Gray & Christmas, 2025 CEO Turnover Report, 2025) — a one-time manual check of your account list simply can't keep pace with that rate of change, and it completely misses departures at the private and mid-market companies most sales and recruiting teams actually track.

CEO Exits: Down Overall, Up at Public Companies Grouped bar chart. Public-company CEO exits: 373 in 2024, 446 in 2025 (record high). Total CEO departures across all organizations: 2,221 in 2024, 2,032 in 2025. Source: Challenger, Gray & Christmas, 2025 CEO Turnover Report. CEO Exits: Down Overall, Up at Public Companies Public-company exits 373 2024 446 2025 All CEO departures 2,221 2024 2,032 2025 Source: Challenger, Gray & Christmas, 2025 CEO Turnover Report
Total CEO departures fell 9% in 2025, but exits at publicly traded companies hit a record high — the kind of split that a fixed watchlist check, rather than continuous monitoring, would likely miss. Source: Challenger, Gray & Christmas, 2025.

Step 3: How Do You Catch Early Announcements With the People Activity Endpoint?

A structural profile update can lag behind reality by days — the person's own announcement post is usually the earliest signal you'll get, and this step is how you catch it. Poll the People Activity endpoint on a schedule for each profile in your watchlist and scan recent posts for announcement language.

curl -G https://api.datamagnet.co/api/v1/people/activity \
  -H "Authorization: Bearer $DATAMAGNET_API_KEY" \
  --data-urlencode "url=https://www.linkedin.com/in/jordan-reyes-example" \
  --data-urlencode "type=posts"

Expected output:

{
  "cursor": "eyJvZmZzZXQiOjEwfQ==",
  "message": [
    {
      "activity_type": "post",
      "text": "Thrilled to announce I'm joining Globex as Chief Revenue Officer...",
      "created_at": "2026-09-01T14:22:00Z",
      "url": "https://www.linkedin.com/feed/update/example-post-id",
      "is_repost": false,
      "author": {
        "title": "Jordan Reyes",
        "occupation": "Chief Revenue Officer at Globex",
        "public_identifier": "jordan-reyes-example"
      }
    }
  ]
}

Each call to this endpoint costs 10 credits, so factor that into your poll frequency once your watchlist grows past a couple hundred profiles.

// poll-activity.js
const ANNOUNCEMENT_PHRASES = [
  "excited to announce", "thrilled to join", "next chapter",
  "new role", "stepping into", "excited to share"
];

async function checkActivity(profileUrl) {
  const res = await fetch(
    `https://api.datamagnet.co/api/v1/people/activity?url=${encodeURIComponent(profileUrl)}&type=posts`,
    { headers: { Authorization: `Bearer ${process.env.DATAMAGNET_API_KEY}` } }
  );
  const { message: activity } = await res.json();

  return activity.filter((item) =>
    ANNOUNCEMENT_PHRASES.some((phrase) =>
      item.text?.toLowerCase().includes(phrase)
    )
  );
}
// Run this against every watchlist profile once per polling cycle,
// then hand any matches to the merge step in Step 4.

What just happened: the function fetches a profile's recent posts and flags any that use common move-announcement phrasing, so you catch the move even before LinkedIn's structured employer field updates. This is also useful year-round beyond move detection.

Our own product analytics show this is Datamagnet's fourth most-read docs page site-wide — ahead of most of the company and search endpoints — because teams already default to it for a simpler job than move detection: checking whether a contact is still active before a rep spends time on outreach.

Most job-change tooling treats the structured profile field as the source of truth and misses that the announcement post is usually the earlier and higher-intent event — the person is telling their network before LinkedIn's own system reflects it. Polling for that language, even on a modest interval, routinely surfaces a move a day or two before a webhook fires on the structured field.

Flat vector illustration of a magnifying glass scanning a social post card and highlighting the phrase "excited to announce"

Step 4: Merge Both Signals Into One Deduplicated Feed

By the end of this step, webhook events from Step 2 and polling results from Step 3 land in one normalized table instead of two separate, uncoordinated streams. Normalize every event into the same shape so downstream routing logic doesn't need special cases per source.

// normalize.js
function normalizeEvent(raw, source) {
  return {
    profile_url: raw.profile_url,
    account_tag: raw.account_tag,      // "sales", "recruiting", or both
    event_type: source === "signal" ? raw.change_type : "announcement",
    detected_via: source,               // "signal" or "activity_poll"
    occurred_at: raw.detected_at || raw.posted_at,
  };
}

// Dedupe on (profile_url, event_type) within a 7-day window so a
// signal-confirmed job change doesn't also fire a duplicate
// announcement alert for the same move.
function isDuplicate(event, recentEvents) {
  return recentEvents.some(
    (e) =>
      e.profile_url === event.profile_url &&
      e.event_type === event.event_type &&
      Math.abs(new Date(e.occurred_at) - new Date(event.occurred_at)) < 7 * 86400000
  );
}

Verify it worked: send one test webhook payload and one test polling match through this function; you should see a single row per real-world move in your events table, not two rows for the same person within the same week.

Step 5: How Do You Push Alerts to Slack and Your CRM?

This step turns a normalized event into something a rep or recruiter actually sees, routed to the right channel and written to the right record. Push to Slack for immediate visibility and to your CRM so the history sticks to the account.

// notify.js
async function sendAlert(event) {
  const label = event.event_type === "job_change" ? "moved" : "announced a move";
  const text = `🔔 *${event.account_tag.toUpperCase()}*: A tracked contact ${label} — <${event.profile_url}|view profile>`;

  await fetch(process.env.SLACK_WEBHOOK_URL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ text }),
  });

  // Write a timeline note to the matching CRM record; see the
  // Datamagnet HubSpot integration for a managed version of this step.
  await fetch(`${process.env.CRM_BASE_URL}/notes`, {
    method: "POST",
    headers: { Authorization: `Bearer ${process.env.CRM_API_KEY}` },
    body: JSON.stringify({
      contact_url: event.profile_url,
      note: `Executive move detected via ${event.detected_via} on ${event.occurred_at}`,
    }),
  });
}

Teams that don't want to maintain this last mile themselves can wire the same events straight into HubSpot with the Datamagnet HubSpot integration, which updates contact and company records automatically when a signal fires instead of requiring a rep to log it by hand.

The teams that get the most out of this pipeline are the ones that resist the urge to route every event to a single #signals channel. A firehose of "someone changed jobs" alerts gets muted within a week. Splitting the same feed into #sales-reopened-accounts and #recruiting-open-seats keeps each alert relevant to the person reading it.

Polling vs. Webhooks: Which Should Drive Your Alerts?

Neither approach alone covers this use case completely, which is why Steps 2 and 3 use different delivery models for different kinds of evidence. Here's the trade-off in practice:

job_change Signal (webhook)People Activity (polling)
Delivery modelPush — Datamagnet calls your endpointPull — your job calls the endpoint on a schedule
Typical latencyMinutes after the structured field changesBounded by your polling interval (hourly to daily is typical)
What it catchesNew employer, new title — a confirmed structural changeAnnouncement language, often before the structured field updates
Cost patternBilled against detected eventsBilled per API call, whether or not anything changed
Infrastructure you ownA public HTTPS endpoint that verifies the HMAC signatureA scheduler and a store of the last-seen state per profile
Main failure modeA missed delivery if your endpoint is down without retry handlingA missed detection if your polling interval is wider than how often the person posts

A daily poll is a reasonable default for the activity lane on a watchlist of a few hundred people — tighten it to hourly only for your highest-priority accounts, since every poll costs a call whether or not it finds anything new. Datamagnet's VIP Engagement Radar cookbook covers the same polling-versus-webhook trade-off for a narrower list of high-value prospects you want to react to within minutes.

How Do You Route the Same Alert Differently for Sales and Recruiting?

The event itself doesn't change between teams — only what you do with it does, so the account_tag field from Step 4 is what decides the routing, not a separate pipeline. A departure at a target account tells sales the deal may be back in play and tells recruiting a leadership seat just opened.

Flat vector illustration of one alert bell branching into a "reopened deal" path for sales and an "open role" path for recruiting

For sales: tag the account as sales if it's a current or past opportunity, and route arrival events (a new decision-maker showing up) to the original rep or account owner, since they already have the relationship history.

For recruiting: tag the account as recruiting if it's a sourcing target, and route departure events straight to a sourcer, since the departing exec's old team is now the most reachable pool of candidates for the resulting opening — many of whom haven't updated their own profiles yet either. Datamagnet's Recruiting Intelligence page and Sales Intelligence page both cover how each team builds on the same underlying signal data.

Testing and Troubleshooting Your Feed

Run these checks before you trust the pipeline with real accounts.

Manual verification checklist:

  • Send a test webhook payload with a known signature and confirm your endpoint accepts it
  • Manually trigger one polling cycle against a profile with a recent public post and confirm it returns activity
  • Submit the same event twice and confirm the dedupe logic in Step 4 collapses it to one row
  • Confirm a Slack message and a CRM note both land for a single test event
ProblemSymptomFix
Webhook never arrivesNo rows appear from the signal sourceConfirm your webhook_url is publicly reachable and returns a 2xx status quickly; slow responses can trigger retries that look like duplicates
Signature check failsYour endpoint rejects every payloadRecompute the HMAC using the raw request body, not a re-serialized JSON object — re-serialization can change byte order and break the signature match
Activity poll returns an empty arrayNo announcement matches ever appearThe profile may be private or inactive; treat repeated empty results as a low-priority profile rather than a broken integration
Same move alerted twiceDuplicate Slack messages for one personCheck your dedupe window in Step 4 — a signal firing on day 1 and a poll match on day 3 should collapse into one event if the window is wide enough
CRM note fails silentlySlack alert lands but no CRM record updatesLog the CRM API response code separately from the Slack response; a shared try/catch block can swallow a CRM failure that a Slack success masks

Next Steps

Now that alerts are flowing, the next improvement is usually tightening who's on the watchlist rather than adding more signal types. Two extensions are worth prioritizing:

  • Score priority by account value, so a departure at your top-tier account pages someone immediately while a mid-tier one waits for the next digest
  • Widen the watchlist with a scheduled ICP query instead of a fixed list, so new decision-makers you haven't met yet still surface, using the same filters from Step 1

Datamagnet's Signal API also supports five other signal types beyond job_change — engagement and keyword signals are natural next additions once this feed is running.

Frequently Asked Questions

What's the difference between the People Activity endpoint and a job_change signal?

A job_change signal watches for a structural change to someone's employer or title field and pushes a webhook the moment it's detected. The People Activity endpoint returns recent posts and engagement, which you poll to catch announcement language — often earlier, but requiring your own scheduler.

How often should I poll the People Activity endpoint?

Daily is a reasonable default for most watchlists, since polling more often than someone typically posts just adds cost without adding coverage. Reserve hourly polling for a short list of your highest-priority accounts, where an extra day of latency has a real cost.

Can the same feed serve both sales and recruiting alerts?

Yes — tag each watchlist entry with sales, recruiting, or both, and route the normalized event from Step 4 accordingly. The underlying data doesn't change; only the destination channel and the framing of the alert (reopened deal vs. open role) differ.

Do I need a public webhook endpoint, or can I run this on polling alone?

You can run the whole pipeline on polling if you don't want to stand up a public endpoint yet, but you'll miss the faster, push-based detection the job_change signal provides and add unnecessary API calls checking profiles that haven't changed. Most teams add the webhook lane once the polling-only version proves the use case.

Sources

Pratik Dani

About Pratik Dani

CEO, Founder