The Mine Works
← All posts
tutorial October 8, 2026 · 11 min read Updated October 8, 2026

Track New People at Your Target Accounts Every Week

Schedule B2B Leads Finder in monitor mode so each weekly run returns only people you have not been sent before, and never bills the same person twice.

Try the scraper

The actor referenced in this article. Pay only for results delivered.

View the scraper →

Most account lists are built once and then go stale. A new head of marketing joins one of your target companies, and the first you hear of it is when a rival’s sales rep has already booked the call. People who just started a job are worth reaching early: they are setting budgets, picking vendors and looking for quick wins.

Try it live: B2B Leads Finder, Like Apollo. Pay per result delivered. Failed and empty results are never charged.

The B2B Leads Finder has a switch for this, monitorMode. With it on, the actor remembers every person it has delivered to your Apify account (up to 50,000) and each later run returns only people you have not been sent before. Put it on a weekly schedule and you get a short list of new names at your accounts instead of the same list again.

What you get and who it is for

Each weekly run writes person rows with the same fields as a normal run:

FieldWhat it holds
company, domainThe account the person was found for
name, job_title, linkedin_urlThe person and their LinkedIn headline, from search results
title_matchtrue when the headline matches one of your job titles
email, email_confidence, email_noteA business email, with found, pattern_matched or guessed saying how it was made
sourcelinkedin-serp or website-scrape (the company’s own team page)
charged, scraped_atWhether the row was billed, and when it was written

Plus a free summary row at the end. In monitor mode, already_delivered_in_earlier_run in the summary counts the people the run skipped because you already have them, and monitor is true.

This is for account executives watching a named list of accounts, ABM teams who want a trigger for outreach, and anyone who already built a contact list once and wants to keep it current without paying for the same people every week.

Why repeat runs need monitor mode

We run a small daily check on stripe.com with monitor mode off and maxLeadsPerCompany at 1. Each day it returns three people, because a search page already fetched is delivered in full. On October 4, all three people it returned had already been delivered by our October 1 run on Stripe, and all three were charged again. On October 5 and October 6 it returned three different people each day.

Two lessons come from that. Without monitor mode, a repeat run pays again for people you already hold. And web search does not return the same people in the same order every day, so a run can surface someone who was always there but did not show up last time. Monitor mode fixes the first problem. The second one is why “new to you” is not the same as “just hired”.

The input

{
  "companies": ["stripe.com", "notion.so", "linear.app", "vercel.com", "supabase.com"],
  "jobTitles": ["Head of Marketing", "VP Marketing", "CMO"],
  "maxLeadsPerCompany": 5,
  "scrapeWebsite": true,
  "monitorMode": true
}
  • monitorMode is the switch this job depends on. It defaults to false.
  • companies takes up to five domains per run. For more accounts, save one task per group of five. Use domains rather than names, so emails can be built.
  • jobTitles puts matching people first and labels them title_match: true. It does not drop everyone else: people outside those titles are delivered and charged too.
  • maxLeadsPerCompany caps how many people each company adds per run, and a run delivers at most 25 in total. A low number keeps each weekly run small and lets the backlog clear over several weeks.
  • Keep companies and jobTitles the same from run to run. Changing them makes it a different search.

What the output looks like

We do not have a monitor mode run of our own to show, so these rows come from the daily stripe.com check described above, runs 5pAvvhY9fRI5mKdPH (October 5, 2026) and YYvtu2GWhJOFQgvcB (October 6). They are the kind of rows a weekly run hands you: people the previous run did not include. Names, emails and profile URLs are masked here.

[
  {
    "company": "Stripe",
    "domain": "stripe.com",
    "name": "T*** M***",
    "job_title": "Head of Latin America Regulatory at Stripe",
    "linkedin_url": "https://www.linkedin.com/in/***",
    "email": "t***.m***@stripe.com",
    "email_confidence": "guessed",
    "email_note": "Guess: first.last is the most common format and stripe.com publishes no staff address to learn from. Mailbox not verified, check before sending.",
    "source": "linkedin-serp",
    "scraped_at": "2026-10-05T09:00:40.659Z",
    "charged": true
  },
  {
    "company": "Stripe",
    "domain": "stripe.com",
    "name": "E*** M***",
    "job_title": "Chief Revenue Officer | Payments & Link l Stripe",
    "linkedin_url": "https://www.linkedin.com/in/***",
    "email": "e***.m***@stripe.com",
    "email_confidence": "guessed",
    "source": "linkedin-serp",
    "scraped_at": "2026-10-06T09:00:34.757Z",
    "charged": true
  },
  {
    "company": "Stripe",
    "domain": "stripe.com",
    "name": "F*** R***",
    "job_title": "Stripe",
    "linkedin_url": "https://www.linkedin.com/in/***",
    "email": "f***.r***@stripe.com",
    "email_confidence": "guessed",
    "source": "linkedin-serp",
    "scraped_at": "2026-10-06T09:00:34.955Z",
    "charged": true
  }
]

Note what is and is not there. None of these rows says when the person joined. A chief revenue officer at Stripe is a senior person worth knowing, but nothing in the row says they started last week. The third row’s headline is only the company name, so you cannot tell the role at all without opening the profile. Every email is guessed, with a note that the mailbox is not verified, because Stripe publishes no staff addresses.

A company check matters here too. In our October 1 run on notion.so, three people marked as CEO matches ran other businesses with Notion in their names. A monitor run on a short company name will keep turning up people like that, and each is “new” the first time.

Run it in Apify Console

  1. Open https://apify.com/themineworks/b2b-leads-finder and click Try for free.
  2. Enter up to five domains in Target companies and your roles in Job titles to target.
  3. Set Max leads per company to 5 and tick Monitor mode (only new people).
  4. Click Save as a new task and give it a name, such as accounts-group-1.
  5. Run the task once and check the summary row: monitor should be true.
  6. In the task, open Schedules, add a schedule with the cron expression 0 7 * * 1 (Mondays at 07:00) and save.
  7. Each Monday, open the run’s Output tab, or let an integration pick up the rows for you.

The first few runs work through each company’s backlog of people you have never been sent, so they are fuller. After that, runs get shorter and some return nobody.

Run it from Python

pip install apify-client

This script runs one group of accounts in monitor mode, flags rows that need a look, and appends the new people to a history file with the date they first appeared. Run it weekly from your own scheduler, or see the note after it to read an Apify-scheduled task instead.

import csv
import re
from datetime import date
from pathlib import Path
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
RUN_INPUT = {
    "companies": ["stripe.com", "notion.so", "linear.app", "vercel.com", "supabase.com"],
    "jobTitles": ["Head of Marketing", "VP Marketing", "CMO"],
    "maxLeadsPerCompany": 5,
    "scrapeWebsite": True,
    "monitorMode": True,
}
HISTORY = Path("account_people_history.csv")
FIELDS = ["first_seen", "company", "domain", "name", "job_title", "title_match",
          "linkedin_url", "email", "email_confidence", "review", "source"]

def review_flags(row):
    flags = []
    headline = re.sub(r"[‎‏]", "", row.get("job_title") or "").lower()
    company = row["company"].lower()
    if row.get("source") == "linkedin-serp":
        i = headline.find(company)
        before = headline[:i].split() if i >= 0 else []
        if i < 0:
            flags.append("company not in headline")
        elif before and before[-1] not in {"at", "@", "of", "for", "in", "|"}:
            flags.append("may be another company")
        elif re.search(r"\b(inc|corp|llc|ltd|gmbh|pvt)\b", headline[i:].split("|")[0]):
            flags.append("may be another company")
        if headline.strip(" .") == company:
            flags.append("headline is only the company name")
        if re.search(r"\b(ex|former|formerly)\b", headline):
            flags.append("headline mentions a past job")
    if row.get("email_confidence") != "found":
        flags.append("verify email" if row.get("email") else "no email")
    return "; ".join(flags)

run = client.actor("themineworks/b2b-leads-finder").call(run_input=RUN_INPUT)
items = list(client.dataset(run["defaultDatasetId"]).iterate_items())

summary = next((i for i in items if i.get("_type") == "summary"), {})
if not summary.get("monitor"):
    print("Warning: monitor mode was not active, rows may repeat earlier runs")

known = set()
if HISTORY.exists():
    with HISTORY.open(newline="", encoding="utf-8") as f:
        known = {(r["linkedin_url"] or r["email"]) for r in csv.DictReader(f)}

new_rows = []
for item in items:
    if item.get("_type") or not item.get("charged"):
        continue  # report rows and free company inboxes
    key = item.get("linkedin_url") or item.get("email")
    if key in known:
        continue  # safety net if monitor memory was unavailable
    item["first_seen"] = date.today().isoformat()
    item["review"] = review_flags(item)
    new_rows.append(item)

write_header = not HISTORY.exists()
with HISTORY.open("a", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=FIELDS, extrasaction="ignore")
    if write_header:
        writer.writeheader()
    writer.writerows(new_rows)

print(f"{len(new_rows)} new people, "
      f"{summary.get('already_delivered_in_earlier_run', 0)} skipped as already delivered")
for r in sorted(new_rows, key=lambda r: not r.get("title_match")):
    tag = "MATCH" if r.get("title_match") else "other"
    print(f"[{tag}] {r['company']}: {r.get('job_title', '')} | {r['review'] or 'clean'}")

The review column is there so the weekly list gets read, not just imported. On the daily Stripe rows above, it marks every row “verify email” and the third “headline is only the company name”. It also marks the chief revenue officer as “may be another company”, because the headline ends “Link l Stripe”: a false alarm, and the reason these are flags for a person rather than filters. On our October 1 Notion rows it marks all three wrong-company CEO matches as “may be another company”, and “headline mentions a past job” catches headlines like “Ex-TikTok”, which are worth a glance even when the past job was somewhere else. The local history check is a second line of defense: the actor says that if it cannot open its monitor memory, the run delivers everything and says so in the log, and the monitor check at the top catches the case where the switch was off.

If you prefer Apify’s own schedule, keep the task running there and read its latest run instead of starting one: replace the call line with run = client.task("YOUR_USERNAME~accounts-group-1").last_run(status="SUCCEEDED").get(), then read the dataset the same way.

Turn the list into an alert

A history file is fine for one person. For a team, send each run’s new rows somewhere they will be seen:

  • Apify’s Google Sheets integration appends each run’s dataset to a sheet, which works well with a filter view on title_match.
  • A webhook on the task can call your own endpoint when a run succeeds, which can post title matches to a Slack channel.
  • Make, Zapier and n8n can pick up the dataset and create a task in your CRM for each title_match: true row, after a check of the company in the headline.

Keep the alert to title matches with a clean company check. The other rows are worth keeping in the history file, but they are not a reason to interrupt anyone.

What it costs

From October 13, 2026 the price is $6.10 per 1,000 leads on the Bronze plan, $4.95 on Silver and $4.00 on Gold and above, plus a flat $0.005 per run. Until October 12 it is $3.75, $3.50 and $3.00 per 1,000 leads with no run fee.

Monitor mode changes what you pay for over time, because you never pay for the same person twice:

  • A backlog week: five companies at five leads each is 25 leads. On Bronze that is $0.1525 plus $0.005, about $0.16 a run.
  • A quiet week: two new people is $0.0122 plus $0.005, about $0.02 on Bronze.
  • A week with nobody new: no lead charges, only the $0.005 run fee.
  • Ten accounts for a month: two tasks of five, run every Monday, is eight runs. If every run were a full backlog run, that would be 200 leads, $1.22 plus $0.04 on Bronze, $1.26 in all. Once the backlog clears, the same month costs a few cents plus $0.04 in run fees.

Limits worth knowing

  • New to you, not newly hired. Monitor mode tracks what it has delivered to your account. A new row can be a new hire, a headline change or a profile that only just surfaced in search.
  • The first runs are backlog. On a large company the actor works deeper through the roster run by run before later runs return only genuinely new profiles.
  • Job titles rank, they do not filter. People outside your titles are delivered and charged. Keep maxLeadsPerCompany low.
  • Five companies and 25 leads per run. More accounts mean more tasks, each with its own schedule.
  • Search lags real life. A headline is only as current as the person’s profile and the search engine’s copy of it, so a job change can take a few weeks to appear.
  • Emails need checking. At large companies most addresses are guessed. Verify before sending. Smaller accounts often publish real addresses, which a sweep of the company’s own site will find.
  • The data is personal. Names, titles and work emails fall under GDPR, CCPA and similar laws, and cold email has its own rules.
Related Actor

Explore the scraper referenced in this article: inputs, outputs, and pricing, then run it on Apify.

Frequently asked questions

Does monitor mode tell me who was just hired? +

Not exactly. It returns people the actor has not delivered to your account before. Once the backlog for a company is worked through, that is mostly people newly visible in search: new hires, people who changed their headline, and profiles a search engine indexed late. Check the profile before you treat someone as a new hire.

Am I charged again for people I already have? +

No. With monitorMode on, people delivered by an earlier monitor mode run are skipped and not charged. A run that finds nobody new charges no leads, and from October 13, 2026 it still carries the $0.005 run fee.

Can I change the company list later? +

Keep the company list and job titles the same between runs, as the actor's own guidance says. For a different set of accounts, save a second task with its own list rather than editing the first one.

Do job titles filter out everyone else? +

No. Job titles rank the results: matches come first and are labeled title_match true, but people who do not match are still delivered and charged. Use maxLeadsPerCompany to cap how many people each company can add per run.

Apify Store

Find a ready-made scraper for your job

Apify has over 80,000 scrapers and automations, ours included. Start free with $5 of platform credit every month.