{
"event_type": "joined",
"title": "Engineering",
"geo_country_code": "DE",
"days_ago": 7,
"page": 1,
"limit": 25
}
{
"success": true,
"message": "Job change events retrieved successfully",
"data": [
{
"event_type": "title_change",
"detected_at": "2026-05-01T17:20:04.389921Z",
"changed_at": "2026-05-01T00:00:00Z",
"detection_lag_days": 0,
"title": {
"current": "Data Engineer",
"previous": "Data Analyst"
},
"person": {
"profile_id": "dm_prsn_tnd66ysw4lmt4yighsalfnmf2tcanlpmp3exh6q",
"handle": "lecomte-quentin",
"full_name": "Quentin Lecomte",
"first_name": "Quentin",
"last_name": "Lecomte",
"headline": "Data Engineer @ Aktivco | Camusat Group",
"linkedin_url": "https://linkedin.com/in/lecomte-quentin"
},
"organization": {
"organization_id": "dm_org_33u32yb3sh56d6jxiw3lzljselgver2p4ons6",
"name": "AktivCo",
"slug": "activco",
"linkedin_url": "https://www.linkedin.com/company/activco"
}
}
],
"pagination": {
"page": 1,
"limit": 25,
"has_more": true
},
"credit_used": 10
}
Job Change Search
Search job-change events — people starting a new role, leaving a company, or being promoted — filtered by company, title, location and recency.
POST
/
api
/
v1
/
job-changes
/
search
{
"event_type": "joined",
"title": "Engineering",
"geo_country_code": "DE",
"days_ago": 7,
"page": 1,
"limit": 25
}
{
"success": true,
"message": "Job change events retrieved successfully",
"data": [
{
"event_type": "title_change",
"detected_at": "2026-05-01T17:20:04.389921Z",
"changed_at": "2026-05-01T00:00:00Z",
"detection_lag_days": 0,
"title": {
"current": "Data Engineer",
"previous": "Data Analyst"
},
"person": {
"profile_id": "dm_prsn_tnd66ysw4lmt4yighsalfnmf2tcanlpmp3exh6q",
"handle": "lecomte-quentin",
"full_name": "Quentin Lecomte",
"first_name": "Quentin",
"last_name": "Lecomte",
"headline": "Data Engineer @ Aktivco | Camusat Group",
"linkedin_url": "https://linkedin.com/in/lecomte-quentin"
},
"organization": {
"organization_id": "dm_org_33u32yb3sh56d6jxiw3lzljselgver2p4ons6",
"name": "AktivCo",
"slug": "activco",
"linkedin_url": "https://www.linkedin.com/company/activco"
}
}
],
"pagination": {
"page": 1,
"limit": 25,
"has_more": true
},
"credit_used": 10
}
Job Change Search
Cost:10 credits per page. A page with no matches costs 0.
Job changes are the highest-intent signal in B2B: a new VP of Engineering buys differently in week three than in month nine, and the person who just left your customer is the reason that account is about to churn. This endpoint returns those events as a feed you can poll.
Each event says what happened (joined, left, title_change), when it happened, when we detected it, and who it happened to — with the person and the company already attached, so a single call gives you something you can act on without a second lookup.
Freshness, and why staleness is ordered rather than hidden
Results are sorted by freshness. The feed opens with same-day detections; events we detected long after the fact surface as you page deeper. Every real dated event is included — a stale one is ordered later, never dropped. Each event carriesdetection_lag_days, the number of days between the change happening and our detection of it. Lower is fresher.
Poll page
1 on a schedule and stop as soon as detection_lag_days exceeds what you have already processed. That gives you the new events without paging through history every run.Request body
string
Restrict to one kind of event. Omit to receive all three.
| Value | Meaning |
|---|---|
joined | The person started a new position at the company. |
left | The person is no longer at the company. |
title_change | The person’s title changed at the same company. |
string | string[]
Filter to specific companies, by the
organization_id returned on every event. Exact match, and more precise than a name search.Accepts an array or a comma-separated string. Up to 50 companies per request, counted across this field and company_urls together."organization_ids": ["dm_org_edmab4ei56y3w6cgktcgegllpqrxdbnlt4aqk"]
string | string[]
Filter to specific companies by LinkedIn company URL, or by the slug at the end of one. Use this the first time you track a company, when you do not have its A company we do not yet track returns
organization_id yet — the response gives you the id to use from then on."company_urls": ["https://www.linkedin.com/company/stripe/", "figma"]
400. Resolving a URL is slower than passing an id, so prefer the id once you have it.string
Word-match on the new job title. Minimum 3 characters.Example:
"Engineering" matches VP of Engineering and Engineering Manager.string
Partial match on the person’s current city. Minimum 3 characters.Example:
"Berlin"string
Two-letter country code for the person’s current location. Case-insensitive.Example:
"DE"The two geo filters narrow the search but are not echoed back on the events — they filter on a location the event record does not carry. If you need the person’s location, fetch their profile with
person.linkedin_url.integer
Recency window on the detection time, in days. Range
1–365. Omit it to search all events.Example: 7 returns everything detected in the last week, whenever the change itself happened.integer
default:1
Page number, 1-based. Maximum
10000; anything higher returns 400.integer
default:25
Events per page. Range
1–50.This endpoint also accepts
GET with the same attributes as query parameters, e.g.
GET /api/v1/job-changes/search?event_type=joined&days_ago=7&title=Engineering.In a query string, list fields are comma-separated: ?organization_ids=dm_org_abc,dm_org_def.Company filter options
Two ways to name a company — use whichever you have:| Field | What you provide | Cost |
|---|---|---|
organization_ids | An organization_id from a previous response | Fastest; no extra work |
company_urls | A LinkedIn company URL or slug | One resolution step the first time each company is seen |
Working with ids
Every event carries two Datamagnet ids:person.profile_id—dm_prsn_…organization.organization_id—dm_org_…
organization_id is Datamagnet’s own identifier for that company — the same one our company records are keyed by — so an id you store today keeps working as we add more about that company.
To pull the full record behind an event, use the LinkedIn URL that comes with it:
Response
{
"event_type": "joined",
"title": "Engineering",
"geo_country_code": "DE",
"days_ago": 7,
"page": 1,
"limit": 25
}
{
"success": true,
"message": "Job change events retrieved successfully",
"data": [
{
"event_type": "title_change",
"detected_at": "2026-05-01T17:20:04.389921Z",
"changed_at": "2026-05-01T00:00:00Z",
"detection_lag_days": 0,
"title": {
"current": "Data Engineer",
"previous": "Data Analyst"
},
"person": {
"profile_id": "dm_prsn_tnd66ysw4lmt4yighsalfnmf2tcanlpmp3exh6q",
"handle": "lecomte-quentin",
"full_name": "Quentin Lecomte",
"first_name": "Quentin",
"last_name": "Lecomte",
"headline": "Data Engineer @ Aktivco | Camusat Group",
"linkedin_url": "https://linkedin.com/in/lecomte-quentin"
},
"organization": {
"organization_id": "dm_org_33u32yb3sh56d6jxiw3lzljselgver2p4ons6",
"name": "AktivCo",
"slug": "activco",
"linkedin_url": "https://www.linkedin.com/company/activco"
}
}
],
"pagination": {
"page": 1,
"limit": 25,
"has_more": true
},
"credit_used": 10
}
null rather than being omitted, so your parsing code never has to branch.
Dates, and the two of them
changed_at is when the change took effect. detected_at is when we saw it. detection_lag_days is the gap, in days.
title.previous is only meaningful on title_change and left; a joined event has no title to have replaced, so it comes back null.
Pagination
pagination.has_more tells you whether another page exists. Keep incrementing page while it is true, up to the 10000 page cap.
An empty data array with has_more: false means you have reached the end of the feed for those filters — and costs 0 credits.
Errors
| Status | Meaning | What to do |
|---|---|---|
400 | A filter was invalid — an unknown event_type, a text filter under 3 characters, days_ago outside 1–365, limit above 50, a page above 10000, or a company we do not track. message names the problem. | Fix the request. Nothing is charged. |
401 | Missing or invalid API key. | Send your key as Authorization: Bearer YOUR_API_KEY — see Authentication. |
403 | Your plan does not include job change data. | Contact us at signals@datamagnet.co. |
429 | Rate limit for your plan exceeded. | Back off and retry; see the X-RateLimit-* response headers. |
503 | Job change search is temporarily unavailable — upstream capacity, not a fault in your request. | Retry after the number of seconds in the Retry-After header: 60 when the search is being rate limited, longer when capacity is exhausted. Nothing is charged. |
Was this page helpful?