Fiber AI
Monitoring & signals

Tracker

Watch companies and people for the changes that matter — 55 rule types, refresh cadences from daily to every six months, signals delivered by webhook and API.

We built Tracker to watch companies and people on LinkedIn so you don't have to. Add entities to a list, pick rules ("headcount grew 20%", "person changed company", "new funding round"), and we re-check everything on your schedule. When a rule matches, you get a signal: pushed by webhook and stored for the API. You declare what matters; the watching, comparing, and noise-filtering happen for you.

Core concepts

ConceptWhat it is
ListA named set of entities sharing rules and a refresh cadence — company list or person list, never mixed
Dynamic listA list backed by a saved search: membership refreshes itself, and a member that stops matching emits a departure signal
EntityOne company or person, tracked with its own history
RuleA condition checked on every refresh ("headcount crossed 100"). Up to 25 per list
SignalA detected change that matched a rule — delivered by webhook and stored for the API
Refresh intervalHow often a list's entities are re-checked: 1–180 days (required when you create the list)
LookbackOptional per-rule window (1–90 days) — compare against ~N days ago instead of the last check

A rule fires only when tracked data actually changes in a way that matches; a refresh that finds nothing produces nothing.

How monitoring behaves

  • The first check is a baseline. When you add an entity, its first check snapshots the current state and fires nothing (there's nothing to compare against yet). Most rules can fire from the second check onward, about one refresh interval after the baseline: a daily list added today can fire its first signals tomorrow, a weekly list about a week in. Some rules wait one more check; see when a rule can first fire. A fresh list with no immediate signals is behaving correctly. (Want history on day one? See day-one signals.)
  • The baseline check is billed. Adding an entity is free, but its first check is a normal check at your list's per-entity rate, so loading 1,000 people onto a standard-rate list costs 2,000 credits before the first scheduled run.
  • Every check runs on fresh data. What a check compares against is never older than your list's refresh interval — a monthly list is never diffing stale leftovers. If a check can't complete, we retry that entity on the next cycle; an incomplete check never turns into a false "change".
  • The list sets the cadence. A scheduled run checks every active entity in the list, and runs land overnight (US-Pacific), so mornings show fresh data. New entities are usually baselined within about an hour of adding, without delaying the entities already tracked. The exception is when the list's next scheduled run is less than half a refresh interval away: new entities then wait and are baselined in that run. On a 30-day list, anyone added in the 15 days before a run is baselined by the run itself. The company and person list endpoints return nextRefreshAt, so you always know when the next scheduled run is. Adding or removing entities never moves it.
  • You can refresh on demand. Refresh a company or person tracker list immediately, at most once every 12 hours per list. Check the tracker overview first to see what the run will cost.
  • You can move a scheduled run. Reschedule a list's next refresh to any date from tomorrow up to 90 days out (free); later refreshes continue at the list's interval, counted from the rescheduled run. Use it to skip a cycle or line runs up with your billing period.
  • Run heads-ups are available on request. We can tell you about a day before a scheduled run and again when it completes (tracker.list_run_upcoming and tracker.list_run_completed at Webhooks, plus in-app notifications). Ask us to turn them on for your organization.
WhenWhat happens
Entity first addedBaseline captured, usually within about an hour, billed as a check. No signal.
Second refresh onwardCompared against the previous state; matches fire signals (some rules from the third check).
Entity paused / removedNo further checks or charges; signal history is kept.

When a rule can first fire

Every rule needs a before and an after, so nothing fires on the baseline check. From there, rules fall into two groups:

Earliest signalRules
Second check (one interval after the baseline)person_changed_company, person_got_promoted, person_got_demoted, headcount and growth rules, funding rounds, job postings, news, posts, location and status changes, and every other rule not listed below
Third check (two intervals after the baseline)person_title_changed, person_started_company, person_added_role, person_employment_type_changed, person_skills_added, person_new_certification, person_tag_gained, person_summary_changed, person_became_verified, person_became_premium, person_became_influencer, person_became_top_voice, technology_added, company_name_changed, company_description_changed, company_went_inactive, acquired_company, new_investor, joined_accelerator

The third-check rules skip the first comparison after the baseline: that comparison is a settling pass for a newly added entity, not a change. On a 30-day list, person_changed_company can fire about 30 days after you add someone and person_title_changed about 60 days after. Three status rules sit in between: person_open_to_work, person_is_hiring, and person_stealth_changed report a status switching off from the second check, but a status switching on only from the third. Want acquired_company, new_investor, or joined_accelerator on day one? All three support day-one signals.

Day-one signals (initial look-back)

A brand-new list doesn't have to start silent. Pass initialSignals when you add entities (or save rules) and we look backward (up to 30 days, defaulting to your refresh interval) and fire signals for recent events that would have matched: funding rounds, matching job postings, news, posts, acquisitions, new investors, layoffs. For joined_accelerator, day-one signals cover every accelerator membership the company already holds, whatever its age — see Accelerators. Set lookbackPeriodDays explicitly: the default is your refresh interval, so a daily list only looks back one day unless you say otherwise.

Look-back works for rules anchored to dated events, plus accelerator membership — 16 of the 55 types:

new_funding_round, funding_stage_changed, job_posting_with_keyword, job_posting_in_function, news_with_keyword, company_news, company_posted, company_posted_with_keyword, acquired_company, new_investor, joined_accelerator, recent_layoffs, person_posted, person_posted_with_keyword, person_reacted_to_post, person_commented_on_post

Diff-based rules (headcount thresholds, title changes, profile edits) need a before and an after, so they start from the second check as usual — unsupported rules come back listed in skippedRules, nothing fails. The tracker rule catalog marks every rule with supportsInitialSignals so you can check programmatically.

Day-one signals are billed per entity, only on success, and they seed the baseline — the first scheduled refresh won't re-fire the same events.

Adding entities: identifiers

Send at least one identifier per entity — any of these works:

EntityIdentifiers
PersonlinkedinUrl (linkedin.com/in/...), linkedinSlug, linkedinUserId (numeric)
CompanylinkedinUrl, linkedinSlug, linkedinOrgId (numeric), domain

Prefer the numeric ID when you have it: it stays stable even when a person changes their vanity URL, so it's the safest key for long-lived lists. A slug we can't resolve right away is still accepted: the entity is created as pending and resolved automatically, so a bulk upload never fails on individual rows.

Legacy /pub/... LinkedIn URLs (the pre-2013 format) aren't parsed; convert them to the /in/ form, or send the slug or numeric ID directly.

Limits

LimitValue
Companies per list100,000
People per list100,000
Tracked entities per organization100,000
Members per dynamic list10,000
Rules per list25
Entities per add/remove call10,000
Refresh interval1–180 days
Lookback period1–90 days
Reschedule next runTomorrow to 90 days ahead
API requests120 / minute (reads and writes)

Billing

  • 2 credits per entity per refresh at the standard rate. 500 companies checked weekly ≈ 1,000 credits/week. See Billing & credits.

  • Volume and frequency earn automatic discounts:

    RateCredits / entity / refreshWhen it applies
    Standard2default
    Scale110,000+ tracked entities or a daily interval
    Bulk0.510,000+ tracked entities and a daily interval

    Tiers apply on their own, with no plan change needed. On-demand refreshes bill at the Standard or Scale rate, never Bulk, and don't move the next scheduled run.

  • Premium rules set a floor. Nine premium rules watch news coverage and LinkedIn activity on every refresh, so a list containing at least one bills at minimum the Scale rate (1 credit / entity / refresh) — the Bulk discount never drops below it. Standard-rate lists are unaffected, and the floor applies once per list no matter how many premium rules it carries.

  • Free: adding entities, reading signals, managing lists, dummy signals, payload previews. Two exceptions: the baseline check an entity gets after you add it is a normal check at your list's rate, and opting into day-one signals bills per entity, on success.

  • Charged per refresh, never per signal: a refresh firing five signals costs the same as one firing none.

  • Out of credits? Nothing breaks. If your balance can't cover a run, we skip it before charging anything: no partial runs, no backlog of surprise charges. The list retries daily instead of waiting out its full interval, and tracking resumes on its own once you top up.

  • Forecast spend with the tracker overview (GET /v1/tracker/overview): every list, its rules, an upcoming-refresh timeline, and estimated daily/monthly credit burn.

  • Pause a list (isActive: false) to stop charges without losing anything.

Credit amounts are defaults — custom pricing is available for bulk use cases.

Rule mechanics

  • Per-item rules emit one signal per detected item (posts, news, job postings, funding rounds), so webhook volume can fan out. Aggregated rules (headcount, title changes) emit one signal per entity per refresh.
  • Updating rules — two modes, never mixed: pass trackingRules to replace the whole set, or addRules / removeRuleIds for granular edits. Combining them in one request returns a 400; the guard exists so a small edit can't accidentally wipe your rule set.
  • Rule identity survives edits. Saving a list keeps the same ruleId for rules you didn't change, so signal history and integrations keyed on ruleId stay intact.

Company rules

GroupRulesTune with…
Headcount & growthheadcount_crossed_threshold, headcount_growth_percent, employee_count_milestone, department_size_threshold, recently_hired_with_title, follower_count_growththresholds, % change, direction, departments, title keywords
Funding & financialsnew_funding_round, funding_stage_changed, acquired_company, new_investor, joined_acceleratormin amount, round/stage types, investor types, accelerators
Hiring & postingsjob_posting_with_keyword, job_posting_in_functionkeywords, seniority, work modality, min postings, functions
News & contentcompany_news, news_with_keyword, company_posted, company_posted_with_keywordkeywords, min articles/reactions
Profile changeshq_location_changed, company_status_changed, technology_added, company_name_changed, company_description_changed, company_logo_changed (temporarily paused), company_went_inactive, new_office_location, recent_layoffscountries/cities, target statuses, technologies, keywords

Example:

{
  "type": "new_funding_round",
  "entityType": "company",
  "minAmountUsd": 10000000,
  "roundTypes": ["series_b", "series_c"]
}

Person rules

GroupRules
Career changesperson_changed_company, person_title_changed, person_got_promoted, person_got_demoted, person_started_company, person_employment_type_changed, person_stuck_in_role, person_tenure_milestone, person_added_role
Status signalsperson_stealth_changed, person_open_to_work, person_is_hiring, person_became_verified, person_became_premium, person_became_influencer, person_became_top_voice
Profile changesperson_headline_changed, person_summary_changed, person_location_changed
Skills & activityperson_skills_added, person_new_certification, person_tag_gained, person_posted, person_posted_with_keyword, person_reacted_to_post, person_commented_on_post
Growthperson_connections_milestone, person_follower_milestone
Contactperson_contact_change

That's the full catalog: 26 company rules and 29 person rules. company_logo_changed is temporarily paused and won't fire until we re-enable it; you can keep it on a list in the meantime. Direction rules take direction (entered/exited, started/stopped, or either); keyword rules take match lists. The full field schema for every rule is in the API reference; the tracker rule catalog (GET /v1/tracker/rules) returns it programmatically.

Stealth moves are trackable. person_stealth_changed fires when a watched person enters or exits stealth mode; see Stealth Tracking for why that signal matters.

Premium rules

Nine rules watch more than the base profile: on every refresh we also monitor news coverage and LinkedIn activity for each tracked entity:

WatchesRules
News coverage (company)company_news, news_with_keyword
LinkedIn posts (company)company_posted, company_posted_with_keyword
LinkedIn activity (person)person_posted, person_posted_with_keyword, person_reacted_to_post, person_commented_on_post
Contact details (person)person_contact_change
  • A list containing at least one premium rule bills at minimum the Scale rate; see Billing. The floor applies once per list: one premium rule or eight, same per-entity rate.
  • Mix premium and standard rules freely within the 25-rules-per-list limit.
  • Spot them programmatically: the tracker rule catalog (GET /v1/tracker/rules) returns isPremium for every rule type, and the tracker overview's estimates already reflect the floor, so you see the true per-refresh cost before a premium rule ever runs.

Recipes

  • Buying intent (companies): job_posting_with_keyword ("data engineer")
    • headcount_growth_percent (15%+) + new_funding_round ($5M+) — any one means spending; all three together is a very strong signal.
  • Champion tracking (people): person_changed_company + person_got_promoted + person_open_to_work — a champion who moves companies is a warm intro to their new org.
  • Fundraise timing (companies): headcount_growth_percent (30% over 90 days via lookbackDays) + job_posting_in_function (Finance, Legal) + recently_hired_with_title (CFO, VP Finance).

Signals & noise

Tracker is conservative by design. It alerts on real changes and stays quiet otherwise:

  • Tiny fluctuations don't fire. A one-person headcount wobble or a few-follower dip never triggers a rule; a change that crosses a threshold you set always counts.
  • The same signal doesn't fire twice. Within a rule's lookback window we suppress repeats, and a status that flips on and off ("open to work" toggled back and forth) won't flood you with alerts.
  • Missing data isn't a change. A temporarily unavailable field is treated as unknown, never as "went to zero" — no false "person left their company" alerts.
  • A second job isn't a job change. Taking on an additional role without leaving the current one doesn't count as changing company — person_changed_company fires on actual moves.
  • Cosmetic title edits aren't promotions. Only real moves up or down fire promotion and demotion signals.
  • Every signal carries a confidence score (0–1). Deterministic rules report 1.0; interpretive ones report lower scores so you can weight them in your automations.

Consuming signals

Webhooks (real-time). A firing rule delivers a tracker.signal_detected webhook: a self-contained envelope with the entity, the matched rule, a human-readable summary, structured changeData, source URLs (sources), a methodology note, and confidence:

{
  "success": true,
  "eventId": "evt_...",
  "occurredAt": "2026-06-02T12:00:00.000Z",
  "entity": {
    "type": "company",
    "identifiers": { "linkedinSlug": "example-co", "domain": "example.com", "name": "Example Co" }
  },
  "subscription": {
    "id": "sub_...",
    "type": "headcount_crossed_threshold",
    "listId": "list_...",
    "listName": "Target Accounts"
  },
  "signal": {
    "type": "headcount_crossed_threshold",
    "summary": "Headcount crossed 100 (now 104)",
    "changeData": { "...": "rule-specific diff — see below" },
    "sources": [],
    "methodology": "...",
    "confidence": 1.0,
    "isDummy": false
  },
  "billing": { "creditsCharged": 2, "billingMethod": "per_check" }
}

eventId is stable across retries; dedupe on it. billing shows what the check that produced the signal cost; signals themselves are never billed separately. Configure the endpoint at fiber.ai/app/webhooks; see Webhooks for signatures and retries.

What's inside changeData

The envelope is identical for every rule: what varies is changeData, the structured diff. Preview a signal payload (POST /v1/tracker/rules/preview-signal) returns the exact shape for any rule, free; hit it before you write a parser, not after.

The rule integrators ask about most is person_changed_company, so here's its changeData (placeholder values):

[
  {
    "companyName": "NewCo",
    "linkedinCompanyId": "11111111",
    "linkedinCompanySlug": "newco",
    "companyLinkedinUrl": "https://www.linkedin.com/company/newco",
    "companyDomains": ["newco.example.com"],
    "crunchbaseSlug": "newco",
    "title": "VP Engineering",
    "isCurrent": true,
    "startDate": "2026-01-01T00:00:00.000Z",
    "endDate": null,
    "location": "City, Country",
    "employmentType": "Full-time",
    "seniority": "Executive"
  },
  {
    "companyName": "OldCo",
    "linkedinCompanyId": "22222222",
    "linkedinCompanySlug": "oldco",
    "companyLinkedinUrl": "https://www.linkedin.com/company/oldco",
    "companyDomains": ["oldco.example.com"],
    "crunchbaseSlug": null,
    "title": "Director of Engineering",
    "isCurrent": true,
    "startDate": "2023-01-01T00:00:00.000Z",
    "endDate": null,
    "location": "City, Country",
    "employmentType": "Full-time",
    "seniority": "Director"
  }
]

Reading it:

  • Both sides of the move are included — the new position(s) first, then the role that was left. Use array order to tell them apart, not isCurrent (which reflects each role's state as last seen).
  • Identifier fields are populated when available and null otherwise — never guessed. Match on linkedinCompanyId, linkedinCompanySlug, companyLinkedinUrl, companyDomains, or crunchbaseSlug, and build your matcher as a ladder with fallbacks rather than requiring any single field. crunchbaseSlug is the employer's Crunchbase permalink when we know it, so systems keyed on Crunchbase can update straight from the signal without a second lookup. Company signals carry the tracked company's crunchbaseSlug in the entity identifiers the same way.
  • Dates are ISO strings and nullable — endDate on the old role fills in once the change is publicly reflected.

API polling (the reliable fallback). Every signal is stored whether or not a webhook exists. Read them from the tracker signal feed (GET /v1/tracker/signals/{listId}) using since (ISO timestamp), cursor, pageSize (1–100, default 50), and filter: real (default), dummy for test signals only, or all. Reading signals is free. The older per-list changes endpoint (GET /v1/tracker/changes/{listId}) is deprecated; move to the signal feed. A robust

integration does both: webhooks to react now, polling with since to backfill anything missed.

Signals stay readable through the API indefinitely. A signal is the record: its changeData and the entity's identifiers. There is no separate before-and-after snapshot to fetch later, so store what you need from the signal when it arrives, and use a profile lookup when you want the full current profile.

Testing tools (both free):

  • Fire test signals (POST /v1/tracker/fire-dummy/{listId}): mark rules isDummy: true and fire synthetic signals through your real webhook, flagged isDummy: true, as often as you like. On an empty list we add a well-known example (Google for company lists, Bill Gates for person lists) so you can test before loading real entities.
  • Preview a signal payload (POST /v1/tracker/rules/preview-signal): send a rule, get an example payload. Nothing is created, nothing is delivered.

Managing lists

  • The tracker overview (GET /v1/tracker/overview) shows everything at a glance and answers "how many lists do I have, and how much am I tracking?" Its summary returns totalCompanyLists, totalPersonLists, totalCompaniesTracked, totalPeopleTracked, totalActiveRules, and estimated daily and monthly credits. Its upcomingRefreshes lists each list's next run (refreshAt), how many entities it will check, and the estimated credits.
  • Pause/resume: isActive: false stops refreshes and charges; rules, interval, and history are preserved. Flip back to resume.
  • Remove an entity: stops its future checks and charges; signal history is preserved. Re-adding the same entity later does not reset its baseline: its first check back compares against the state we last saw before removal, so anything that changed in between surfaces as signals then. A genuinely new entity starts fresh.
  • Delete a list: deactivates the list and everything in it; signal history is retained. A deleted list can't be restored, and adding its entities to a new list baselines (and bills) them again. If you may need the list later, pause it instead.

FAQ

Does a signal include the full profile? No — a signal carries the rule that fired, a human-readable summary, the structured diff (changeData), and the entity's identifiers. For job changes the diff already includes the complete new-position details, which covers most CRM-update flows. Need the full refreshed profile? Any identifier from the signal works with our profile-lookup endpoints — that's a separate, charged call.

I fetch the full profile after a signal. Will it show the change? Yes. The profile data a tracker check refreshes is the same data our people search and profile lookups return, and it shows up there within minutes of the check. Wait a few minutes after the webhook, or confirm the profile's last_updated_at is at or after the signal's timestamp, and you have the state the tracker saw. You don't need a live fetch for this; reserve it for when you need a guaranteed fresh read, since it costs more per profile.

Is there a "left their job" or "retired" signal? Not currently — person_changed_company fires on moves, and the closest status signal is person_open_to_work. If a "left, nothing new yet" signal matters for your use-case, tell us.

Why haven't my lists produced any signals? Work down this list:

  1. New lists start quiet. Unless you turned on day-one signals, the first signals arrive after a refresh cycle.
  2. Nothing matched yet. A refresh that finds no matching change fires nothing, and small fluctuations are filtered out (see Signals & noise). Narrow, high-threshold rules on a short list can go quiet for weeks. Fire a test signal to confirm your webhook is wired up.
  3. Your rules need a third check. Title, role, skills, and most profile-edit rules skip the first comparison after the baseline, so their first signal lands two intervals in; see when a rule can first fire. The whole list is checked together on each run, so signals arrive in bursts with quiet stretches in between.
  4. You ran out of credits. Runs your balance can't cover are skipped, not queued; tracking resumes once you top up.
  5. Your plan doesn't include Tracker. Tracker calls return 403 with "Your current plan does not include Tracker access." Contact your account manager to enable it.
  6. The rule is paused. company_logo_changed is temporarily paused.

Why did only part of my list refresh today? A scheduled run checks every active entity in the list, so a partial refresh on a non-run day is the hourly pass baselining entities you just added. The tracker overview shows exactly what's due next.

Can I move or skip a scheduled run? Yes. Reschedule the list's next refresh to any date from tomorrow up to 90 days out; later refreshes follow the list's interval from there. To run now instead, refresh on demand (at most once every 12 hours per list).

Does Tracker integrate with Salesforce or HubSpot? There's no native CRM sync for Tracker. Point a webhook endpoint at your own handler (or a no-code automation tool) and write each tracker.signal_detected event into your CRM. A job-change signal already includes the new company and title, which covers most CRM-update flows. Poll the signal feed with since to backfill anything a webhook missed.

What happens when a check fails? The entity is retried on the next cycle, and a failed check never fabricates a signal — missing or incomplete data is treated as unknown, not as a change.

API quick reference

All operations are under /v1/; each links to its focused reference page in the operation index.

OperationMethod & path
createTrackerCompanyListPOST /v1/tracker/company-lists
listTrackerCompanyListsGET /v1/tracker/company-lists
getTrackerCompanyListGET /v1/tracker/company-lists/{listId}
updateTrackerCompanyListPATCH /v1/tracker/company-lists/{listId}
deleteTrackerCompanyListDELETE /v1/tracker/company-lists/{listId}
addTrackerCompaniesPUT /v1/tracker/company-lists/{listId}/companies
removeTrackerCompaniesDELETE /v1/tracker/company-lists/{listId}/companies
refreshTrackerCompanyListPOST /v1/tracker/company-lists/{listId}/refresh
getTrackerPersonListGET /v1/tracker/person-lists/{listId}
refreshTrackerPersonListPOST /v1/tracker/person-lists/{listId}/refresh
rescheduleTrackerCompanyListPOST /v1/tracker/company-lists/{listId}/reschedule
rescheduleTrackerPersonListPOST /v1/tracker/person-lists/{listId}/reschedule
Other person-list operationsSame shape under /v1/tracker/person-lists… (…People)
listTrackerSignalsGET /v1/tracker/signals/{listId}
listTrackerChanges (deprecated)GET /v1/tracker/changes/{listId}
getTrackerOverviewGET /v1/tracker/overview
listAvailableTrackerRulesGET /v1/tracker/rules
fireTrackerDummyPOST /v1/tracker/fire-dummy/{listId}
previewTrackerSignalPOST /v1/tracker/rules/preview-signal

On this page