<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[GTM workflows with Apify and Claude]]></title><description><![CDATA[Practical B2B data workflows with Apify Actors and Claude Code: lead lists, call lists, account signals and what each run costs. Counts and prices come from rea]]></description><link>https://gtm-workflows.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Wed, 04 Nov 2026 12:10:45 GMT</lastBuildDate><atom:link href="https://gtm-workflows.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Lusha and ZoomInfo alternative in Claude Code: a call list, four ways]]></title><description><![CDATA[Mobile numbers for an ICP without a Lusha or ZoomInfo seat: four Apify Actors, free counts first, run from Claude Code over MCP.]]></description><link>https://gtm-workflows.hashnode.dev/lusha-and-zoominfo-alternative-in-claude-code-a-call-list-four-ways</link><guid isPermaLink="true">https://gtm-workflows.hashnode.dev/lusha-and-zoominfo-alternative-in-claude-code-a-call-list-four-ways</guid><category><![CDATA[sales]]></category><category><![CDATA[lead generation]]></category><category><![CDATA[AI]]></category><category><![CDATA[mcp]]></category><dc:creator><![CDATA[Kevin Hall]]></dc:creator><pubDate>Sat, 31 Oct 2026 13:59:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6ac951f8c58437c7a9101e18/057987d0-6083-4657-bcf1-3e4908fe7ecb.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Lusha and ZoomInfo sell one column: the mobile number. Everything else on the row you can get in ten other places. And the number is the column that is empty most of the time, whichever logo is on the seat.</p>
<p>I build outbound lists for a living, and teams that dial ask me the same thing every month. Not "does this vendor have numbers". They ask how many of the people they actually want have one. Nobody can tell them that before the contract is signed.</p>
<p>So on 2026-10-29 I spent a morning answering it the cheap way. One ideal customer profile, four Apify Actors, free counts before any paid pull, and Claude Code doing the clicking over MCP. Every number below comes from those runs, and the inputs are in the text.</p>
<p>In one paragraph: a Lusha or ZoomInfo alternative here is one people database behind a few Actors. Count your ICP for free. Then pull leads with the number where one exists, or only the callable ones, or look up numbers for contacts you already hold, or walk named accounts for anyone with a mobile. You pay per row delivered, nothing for the misses.</p>
<h2>How big is the box, and does it matter where you sell</h2>
<p>Before any pull I ran the ICP most call lists are built from through <a href="https://apify.com/b2bsearch/people-database-search">People Search</a> in count mode: US sales leaders at software companies with 51 to 500 employees. Count mode charges nothing per row, and <code>segments</code> compares countries in one call.</p>
<pre><code class="language-json">{
  "mode": "count",
  "titleKeywords": ["vp sales", "head of sales", "chief revenue officer", "sales director"],
  "employerIndustries": ["Software Development"],
  "employeeCountMin": 51,
  "employeeCountMax": 500,
  "segments": [
    { "name": "US", "countries": ["us"] },
    { "name": "UK", "countries": ["gb"] },
    { "name": "Germany", "countries": ["de"] },
    { "name": "Canada", "countries": ["ca"] },
    { "name": "Australia", "countries": ["au"] }
  ]
}
</code></pre>
<table>
<thead>
<tr>
<th>Segment</th>
<th>People matching</th>
</tr>
</thead>
<tbody><tr>
<td>United States</td>
<td>5,377</td>
</tr>
<tr>
<td>United Kingdom</td>
<td>1,591</td>
</tr>
<tr>
<td>Germany</td>
<td>776</td>
</tr>
<tr>
<td>Canada</td>
<td>402</td>
</tr>
<tr>
<td>Australia</td>
<td>315</td>
</tr>
</tbody></table>
<p>Inside the US box, "sales director" is more than half of the people. "VP Sales" as a literal title is a small slice. Worth knowing before you read anything into a sample of twenty.</p>
<p>What the free read does not tell you is who has a number. Market mode shows email coverage (about two thirds of this box have a personal address on record) and says nothing about phones. The phone share comes one step later, and that step is cheap: the Actors skip people without a number for free and count them in a note row.</p>
<h2>1. We need a fresh list, with numbers where they exist</h2>
<p>The default case. A list to work by email first, phone when you have it. <a href="https://apify.com/b2bsearch/b2b-leads-finder">B2B Leads Finder</a> with <code>includePhones</code> on:</p>
<pre><code class="language-json">{
  "jobTitles": ["vp sales", "head of sales", "chief revenue officer", "sales director"],
  "countries": ["us"],
  "companySizeMin": 51,
  "companySizeMax": 500,
  "industries": ["Software Development"],
  "emailType": "any",
  "includePhones": true,
  "maxResults": 20
}
</code></pre>
<p>A free Apify plan delivers up to twenty leads per API run, so I ran it five times and passed the earlier datasets in <code>excludeDatasets</code> each time. A hundred people at ninety-six companies, no repeats, each run under half a minute.</p>
<p>Thirty-five of them had a mobile number. That is the honest headline for this ICP: about one in three.</p>
<table>
<thead>
<tr>
<th>Role in the title</th>
<th>Leads</th>
<th>With a number</th>
</tr>
</thead>
<tbody><tr>
<td>Chief Revenue Officer</td>
<td>31</td>
<td>16</td>
</tr>
<tr>
<td>Head of Sales</td>
<td>18</td>
<td>7</td>
</tr>
<tr>
<td>Sales Director</td>
<td>44</td>
<td>11</td>
</tr>
<tr>
<td>VP Sales</td>
<td>6</td>
<td>0</td>
</tr>
</tbody></table>
<p>The pattern I keep seeing: the more senior the title, the more often a number is on record. CROs had one about half the time, sales directors one in four. The six VPs had none, which says more about six being a small number than about VPs.</p>
<p>Two things I expected to matter did not. Records refreshed within the last year carried a number at the same rate as older ones. And a work address and a number came together on a quarter of the list, which is the row a dialling team actually wants.</p>
<p>Then the same box for the UK and for Germany, twenty leads each. Zero numbers in both. Mobile numbers in this database are a US thing, and I would rather say that here than have you find it on the invoice.</p>
<h2>2. We only want people we can call</h2>
<p>A dialling team does not want the seventy rows without a number. <a href="https://apify.com/b2bsearch/b2b-leads-with-phones">Leads with Phones</a> is the same search with <code>phoneRequired</code> on by default: every row has a verified mobile on record and a live email, the rest are skipped free.</p>
<pre><code class="language-json">{
  "jobTitles": ["vp sales", "head of sales", "chief revenue officer", "sales director"],
  "countries": ["us"],
  "companySizeMin": 51,
  "companySizeMax": 500,
  "industries": ["Software Development"],
  "emailType": "any",
  "phoneRequired": true,
  "maxResults": 20
}
</code></pre>
<p>Twenty people came back in about fifteen seconds, all with numbers. To find them the Actor read 125 matches. Thirty-five had left the role the search matched. Sixty-eight had no number or no live address. Both groups sit in the note rows at the end of the dataset, and neither is on the bill.</p>
<p>That note row answers the question from the top of the post. For this ICP, about one in six people the search matches is someone you can ring today.</p>
<p>I ran it once more with the first dataset in <code>excludeDatasets</code>. Twenty new people, zero repeats. Put that on a weekly timer and the call list grows only with new people.</p>
<h2>3. The CRM has two thousand contacts and no numbers</h2>
<p>This is the version I hear most. The list already exists, bought or built years ago, names and companies in a spreadsheet. <a href="https://apify.com/b2bsearch/linkedin-to-phone">Phone Number Finder</a> takes exactly that: one line per person as "First Last, company domain", or an email, or a profile URL. Up to twenty thousand lines in one run.</p>
<p>I fed it twenty marketing leaders from a random sample of a different segment, random so I could not cherry-pick, as name plus domain:</p>
<pre><code class="language-json">{
  "profileUrls": [
    "Jane Smith, acme.com",
    "...nineteen more lines, one person each"
  ]
}
</code></pre>
<p>Five seconds. Nine came back with a number. Ten matched a person at that company who has no number on record, free. One profile was no longer in the database, also free.</p>
<p>Nine of twenty beats the one in three from the fresh list, and I would not read much into it: marketing leaders, a tiny sample. The shape is the point. You pay for the nine, and the eleven misses cost nothing and each one says why.</p>
<h2>4. We sell to forty named accounts</h2>
<p>Account-based teams have the opposite problem. They know the companies and want everyone there they can call. <a href="https://apify.com/b2bsearch/company-employees">Company Employees Scraper</a> walks a list of company domains, and <code>mustHave: ["phone"]</code> keeps only people with a number.</p>
<p>I took ten company domains from the first list. Any account list works: your CRM, a <code>company-database-search</code> run, domains resolved with <code>domain-to-company</code>. Leadership roles, sales or revenue in the title, contacts tier, compact rows:</p>
<pre><code class="language-json">{
  "companies": ["&lt;ten company domains, one per line&gt;"],
  "roles": ["cxo", "vp", "director"],
  "titleContains": ["sales", "revenue"],
  "profileDetail": "contacts",
  "mustHave": ["phone"],
  "compact": true,
  "maxRows": 20
}
</code></pre>
<p>Nineteen people with a number at nine of the ten companies, eleven seconds. Fifty-three employees in those roles had no number and were skipped free.</p>
<p>Most of the nineteen were directors, because there are simply more directors with "sales" in the title than there are CROs. If what you want is the one top person per account, run the roster tier first with <code>roles: ["cxo"]</code>, read it, then pull contacts for the names you picked.</p>
<h2>What Claude Code did with the same question</h2>
<p>I gave Claude Code two of the Actors over MCP and a prompt in plain words: I run a three-person team that works the phone, this is our ICP, run anything free first, keep every paid pull at twenty or fewer, never run the same paid pull twice. Shell and file tools were switched off, so the Apify tools were all it had.</p>
<p>It did the free reads first, as told. Then it did something I had not asked for: it expanded my four titles into ten spellings ("vp of sales", "vice president sales", "director of sales", "cro"). The box roughly doubled, to about ten thousand people. The right call for a call list, and the reason its count does not match mine above.</p>
<p>It tried to get the phone share for free by running count mode with a phone requirement. Count mode ignores that requirement, and the agent said so instead of inventing a number. Then it ran one paid pull, with its own title list:</p>
<pre><code class="language-json">{
  "jobTitles": ["vp sales", "vp of sales", "vp, sales", "vice president sales", "vice president of sales",
                "head of sales", "chief revenue officer", "cro", "sales director", "director of sales"],
  "countries": ["us"],
  "industries": ["Software Development"],
  "companySizeMin": 51,
  "companySizeMax": 500,
  "phoneRequired": true,
  "emailType": "any",
  "maxPerCompany": 1,
  "maxResults": 20
}
</code></pre>
<p>Twenty rows, all with numbers, from 111 matches read. Fourteen had a work address at the current employer, six only a personal one.</p>
<p>Its read of the rows was the useful part. Three of the twenty were pre-sales or operations: a technical sales director, a director of sales operations, a VP of sales engineering. "Sales director" as a substring brings them in. It proposed an <code>excludeJobTitles</code> list for the next run and reusing the dataset in <code>excludeDatasets</code>. Both are the right next move.</p>
<p>It also read the rows with a <code>fields</code> list instead of pulling whole records, which nobody told it to do. Twelve turns, under six minutes.</p>
<h2>Before you dial</h2>
<p>Everything above is a number on record, not a number that rings. Nobody dialled anything for this post. Line type, do-not-call lists and consent rules (TCPA in the US, GDPR in Europe) are yours to handle before the first call, the same as with any vendor.</p>
<p>And the numbers are US-centric. If your market is the UK or Germany, run the free count, then one twenty-row pull with the phone required, and read the note row before you plan a calling campaign around it.</p>
<h2>FAQ</h2>
<p><strong>Is this a drop-in Lusha or ZoomInfo replacement?</strong> For the list, yes: ICP in, rows with numbers out, no seat, no browser extension. For a dial-from-the-browser plugin, no. You get data, not a dialler.</p>
<p><strong>How do I learn the phone share before paying for a list?</strong> Run Leads with Phones with a small <code>maxResults</code> and read the <code>skipped_no_contact</code> note row. The skipped people are free and they are your denominator.</p>
<p><strong>Why did the UK and Germany return no numbers?</strong> Mobile coverage in this database is concentrated in the US. Outside it, treat the number as a rare bonus and build the list for email.</p>
<p><strong>I already have the contacts. Which Actor?</strong> Phone Number Finder, one line per person: name plus company domain, an email, or a profile URL. Misses are free and come back with a reason.</p>
<p><strong>Can I avoid paying twice for the same person?</strong> Pass earlier dataset ids in <code>excludeDatasets</code>. Everyone in them is skipped free, so a weekly rerun delivers only new people.</p>
<h2>For AI agents reading this</h2>
<p>The Actors run over the hosted Apify MCP server with the tools pinned in the URL, so an agent loads only these definitions:</p>
<pre><code class="language-plaintext">claude mcp add --transport http apify "https://mcp.apify.com?tools=b2bsearch/b2b-leads-finder,b2bsearch/b2b-leads-with-phones,b2bsearch/people-database-search" --header "Authorization: Bearer &lt;APIFY_TOKEN&gt;"
</code></pre>
<pre><code class="language-json">{
  "mcpServers": {
    "apify": {
      "type": "http",
      "url": "https://mcp.apify.com?tools=b2bsearch/b2b-leads-finder,b2bsearch/b2b-leads-with-phones,b2bsearch/people-database-search",
      "headers": { "Authorization": "Bearer &lt;APIFY_TOKEN&gt;" }
    }
  }
}
</code></pre>
<p>Tool names use a double hyphen: <code>b2bsearch--people-database-search</code>, <code>b2bsearch--b2b-leads-finder</code>, <code>b2bsearch--b2b-leads-with-phones</code>. For a call list: run <code>people-database-search</code> with <code>mode: "count"</code> and <code>segments</code> first and read <code>_total</code> per segment. Then run <code>b2b-leads-with-phones</code> with <code>phoneRequired: true</code>, <code>maxPerCompany: 1</code> and a <code>maxResults</code> you can defend, or <code>b2b-leads-finder</code> with <code>includePhones: true</code> when the number is optional.</p>
<p>Read rows with <code>get-dataset-items</code> and a <code>fields</code> list. Rows whose <code>_status</code> is not <code>found</code> are note rows (<code>skipped_no_contact</code>, <code>skipped_moved</code>, <code>skipped_relocated</code>) and are the denominator. On reruns pass earlier dataset ids in <code>excludeDatasets</code>.</p>
<p>For contacts the user already holds, run <code>b2bsearch/linkedin-to-phone</code> with <code>profileUrls</code> as "First Last, domain" lines, emails or profile URLs. For named accounts, run <code>b2bsearch/company-employees</code> with <code>companies</code>, <code>roles</code>, <code>profileDetail: "contacts"</code> and <code>mustHave: ["phone"]</code>. Phone coverage is US-centric; outside the US expect the number column to be mostly empty. Per-tier pricing is on each Actor page.</p>
<h2>Earlier posts on the same question</h2>
<p>The <a href="https://dev.to/kevinhall/lusha-alternative-test-91-icp-leads-32-had-a-mobile-number-49ph">first Lusha test</a> ran one ICP with the number optional and required. The <a href="https://dev.to/kevinhall/where-b2b-mobile-numbers-exist-a-phone-number-finder-hit-rate-map-by-role-and-country-from-225-le6">phone hit-rate map</a> measures nine role and country populations. The <a href="https://dev.to/kevinhall/apollo-alternative-in-claude-code-7-lead-lists-from-one-apify-actor-4olo">Apollo alternative</a> and the <a href="https://dev.to/kevinhall/sales-navigator-alternative-in-claude-code-one-saved-search-one-list-i">Sales Navigator alternative</a> build the email side of the same lists from Claude Code, and the <a href="https://dev.to/kevinhall/account-mapping-by-company-domain-leaders-departments-and-work-emails-for-8-target-accounts-from-3agj">account mapping post</a> walks named accounts at full length.</p>
]]></content:encoded></item><item><title><![CDATA[Bulk email finder in Claude Code: 200 profile URLs, 64 still current]]></title><description><![CDATA[200 profile URLs in, work emails out: 64 were still current at their employer. A bulk email finder run from Claude Code.]]></description><link>https://gtm-workflows.hashnode.dev/bulk-email-finder-in-claude-code-200-profile-urls-64-still-current</link><guid isPermaLink="true">https://gtm-workflows.hashnode.dev/bulk-email-finder-in-claude-code-200-profile-urls-64-still-current</guid><category><![CDATA[sales]]></category><category><![CDATA[lead generation]]></category><category><![CDATA[AI]]></category><category><![CDATA[mcp]]></category><dc:creator><![CDATA[Kevin Hall]]></dc:creator><pubDate>Sat, 31 Oct 2026 13:58:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6ac951f8c58437c7a9101e18/4ee69052-60c0-4c81-954d-0e4b6639dbce.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Everyone in sales has this file. A column of profile URLs from an event list, an old export or the CRM, and an empty column next to it where the email should go.</p>
<p>Any email finder will fill that column. What none of them tells you is whether the person still reads that mailbox. A record collects addresses the way a career collects employers, and the finder hands you the one on top. The bounce arrives a week later, on your domain's reputation, not theirs.</p>
<p>So on 2026-10-29 I ran two hundred profile URLs through an Apify Actor that labels the address instead of just returning it. Current employer, older employer, nothing on record, profile gone. Then I put two more Actors behind it, and let Claude Code run the same twenty over MCP. Every number below comes from those runs.</p>
<p>In one paragraph: a bulk email finder here takes profile URLs, one per line, and returns the addresses on each person's record with a label. An address on the current employer's domain, an older company address, or nothing. Misses are free rows with a reason.</p>
<p>For the older and empty rows a second Actor tries name plus company domain and verifies what it finds. A free search step sizes the segment before anyone pays.</p>
<h2>Where two hundred URLs come from, and what a free read says</h2>
<p>I did not want to run a client's list for a blog post, so I drew two random samples from the same people database the finder reads: in-house marketing leaders, and founders. Different inboxes, different habits. <a href="https://apify.com/b2bsearch/people-database-search">People Search</a> in <code>market</code> mode first, which costs nothing and tells you what the box looks like before you pull from it.</p>
<table>
<thead>
<tr>
<th>Segment</th>
<th>People</th>
<th>Personal email on record</th>
</tr>
</thead>
<tbody><tr>
<td>US marketing leaders (VP, Head, CMO, Marketing Director) at software companies with 51 to 500 employees</td>
<td>2,386</td>
<td>about two thirds</td>
</tr>
<tr>
<td>US founders and CEOs of software companies with 2 to 50 employees</td>
<td>59,835</td>
<td>just over half</td>
</tr>
</tbody></table>
<p>Inside the marketing box, chief marketing officers and other C-level titles were nearly half, directors about a third, VPs fewer than one in ten. Useful to know before you read anything into a sample of a hundred.</p>
<p>Then the pull. <code>people</code> mode, compact rows, a random seed so I could not cherry-pick, five runs of twenty per segment:</p>
<pre><code class="language-json">{
  "mode": "people",
  "titleKeywords": ["vp marketing", "head of marketing", "chief marketing officer", "marketing director"],
  "countries": ["us"],
  "employerIndustries": ["Software Development"],
  "employeeCountMin": 51,
  "employeeCountMax": 500,
  "profileDetail": "row",
  "compact": true,
  "maxResults": 20,
  "sampleSeed": 41
}
</code></pre>
<p>Two of the hundred marketing draws were the same person, so that list is ninety-eight. Each compact row carries a profile URL, the employer's domain, company size, and two flags: <code>hasWorkEmail</code> and <code>hasPersonalEmail</code>. Eighty-two of the ninety-eight marketing leaders said yes to a work email. Seventy-six of the hundred founders did.</p>
<p>Hold that number.</p>
<h2>The run: one field, four seconds</h2>
<p>The finder takes one field. Links, bare handles and mobile URLs all work, and spellings of the same profile are looked up once. Up to a thousand per run; a free plan returns up to a hundred results per run, and I fed twenty at a time so I could read each batch before the next.</p>
<pre><code class="language-json">{
  "profileUrls": [
    "&lt;one profile URL per line&gt;",
    "..."
  ],
  "includePersonalEmails": false
}
</code></pre>
<p>Four seconds per batch of twenty. Here is what two hundred URLs became:</p>
<table>
<thead>
<tr>
<th>Outcome</th>
<th><code>_status</code> / <code>emailType</code></th>
<th>Marketing leaders (98)</th>
<th>Founders (100)</th>
</tr>
</thead>
<tbody><tr>
<td>Address at the current employer</td>
<td><code>work_current</code></td>
<td>38</td>
<td>26</td>
</tr>
<tr>
<td>Only an older company address</td>
<td><code>work_other</code></td>
<td>43</td>
<td>48</td>
</tr>
<tr>
<td>Matched, nothing on record</td>
<td><code>no_email</code></td>
<td>12</td>
<td>24</td>
</tr>
<tr>
<td>Profile gone</td>
<td><code>profile_removed</code></td>
<td>5</td>
<td>2</td>
</tr>
</tbody></table>
<p>Only the first two rows are billed. The empty and gone rows come back with a status, and for the empty ones with the name and title, so you know who to route elsewhere.</p>
<p>Now back to the flag. Eighty-two marketing leaders had "has a work email" on their database row. Thirty-eight of them have one at the company they work for today. A yes in a database means an address exists somewhere in the record. It does not say where.</p>
<p>That is the whole post in one line. About half the work addresses in a B2B record belong to a job the person has left, and a finder that does not label them sells you the bounce.</p>
<h2>What an older address looks like</h2>
<p>A career leaves a trail of mailboxes. The found marketing records held four addresses each in the middle of the pack, three quarters of them three or more, and one person had thirteen. Founders' records were a little shorter, three in the middle.</p>
<p>The finder sorts the current employer's address first when it has one. When it has none it can tie to today's employer, the row is labelled <code>work_other</code> and the first address in the list is simply the first one, with no claim about recency.</p>
<p>I checked the label against the employer domain from the source row, the one a CRM would hold:</p>
<table>
<thead>
<tr>
<th><code>work_current</code> rows, marketing leaders</th>
<th>38</th>
</tr>
</thead>
<tbody><tr>
<td>Same domain as the source row</td>
<td>27</td>
</tr>
<tr>
<td>Same company, a different mail domain</td>
<td>11</td>
</tr>
<tr>
<td>A different company</td>
<td>0</td>
</tr>
</tbody></table>
<p>The eleven are brands whose mail domain is not the website: a product name on the site, the legal entity on the envelope. Among founders it split in half, which is what you get when one person runs two companies at once.</p>
<p>Record freshness shifts the odds without deciding anything:</p>
<table>
<thead>
<tr>
<th>Source record last updated</th>
<th>Current address</th>
<th>Older only</th>
</tr>
</thead>
<tbody><tr>
<td>Within the last year</td>
<td>21</td>
<td>18</td>
</tr>
<tr>
<td>More than a year ago</td>
<td>17</td>
<td>25</td>
</tr>
</tbody></table>
<p>Fresher records lean toward a live address. Not by enough to filter on. The label does the filtering, freshness only tells you how much to trust it.</p>
<h2>Personal mailboxes, on request</h2>
<p>By default the finder returns company addresses only. When a matched profile has nothing corporate but a personal mailbox is on record, the empty row says so in a <code>_note</code>. Five of the twenty-four empty founder rows carried that note. None of the twelve empty marketing rows did, and their source rows agreed: nothing of either kind.</p>
<p>I reran those five with <code>includePersonalEmails</code> on. Five personal addresses, as promised.</p>
<p>Then I reran one whole founder batch of twenty with personal mailboxes on from the start, to see what the switch does to rows that already had something:</p>
<table>
<thead>
<tr>
<th>Row in the first run</th>
<th>Rows</th>
<th>With personal mailboxes on</th>
</tr>
</thead>
<tbody><tr>
<td>Current employer address</td>
<td>3</td>
<td>still the current address</td>
</tr>
<tr>
<td>Older company address</td>
<td>16</td>
<td>10 became a personal mailbox, 6 stayed older</td>
</tr>
<tr>
<td>Nothing</td>
<td>1</td>
<td>still nothing</td>
</tr>
</tbody></table>
<p>With the switch on, a personal mailbox outranks an older company address. For the founder of a twelve-person company that is usually the inbox that gets read. For an in-house CMO it is the wrong one, which is why the switch is off by default.</p>
<h2>The second door: name plus domain, verified</h2>
<p>The older and empty rows are not dead people, they are people whose record is behind. For those there is a different Actor. <a href="https://apify.com/b2bsearch/work-email-finder">Work Email Finder</a> takes a name and a company domain, builds the candidates the way the domain writes addresses, and with <code>verifyTopCandidate</code> on checks the best one against the mail server and its own records.</p>
<p>I took the eighty-one found marketing rows, the name from the finder's row and the domain from the source row, and ran all of them through it:</p>
<pre><code class="language-json">{
  "lookups": [
    { "domain": "&lt;company domain from your CRM&gt;", "full_name": "&lt;name from the finder row&gt;" },
    "...eighty more"
  ],
  "verifyTopCandidate": true
}
</code></pre>
<table>
<thead>
<tr>
<th>Email Finder said</th>
<th>Rows</th>
<th>Verified address at today's domain</th>
<th>Of which already somewhere in the record</th>
<th>New, built from the pattern</th>
</tr>
</thead>
<tbody><tr>
<td><code>work_current</code></td>
<td>38</td>
<td>30</td>
<td>26</td>
<td>4</td>
</tr>
<tr>
<td><code>work_other</code></td>
<td>43</td>
<td>33</td>
<td>21</td>
<td>12</td>
</tr>
</tbody></table>
<p>Thirty-three of the forty-three "older" rows came back with a verified address at today's employer. Twenty-one of those were already in the record's <code>workEmails</code> list, below the one on top. The finder ties addresses to the employer's canonical domain, so a brand domain further down the list stays labelled older.</p>
<p>Two lessons for a pipeline. Match the whole list against your own domain list before you discard a row. And send what is left through the name-plus-domain door with verification on, which rescued most of the rest here.</p>
<p>The honest other half: eight people with a current address on record got no verified candidate from the pattern door. Record and pattern are two sources. Where they agree, twenty-six rows here, is where I would send first.</p>
<h2>What Claude Code did with twenty of them</h2>
<p>I gave Claude Code the Email Finder and People Search over MCP, nothing else. Shell and file tools off.</p>
<p>The prompt was a RevOps person in plain words. Two thousand CRM contacts with profile URLs and no email, here are twenty. Our policy is work addresses at the current employer only, never a personal mailbox unless you can say why. Look each person up once.</p>
<p>It loaded the two tool definitions, ran one lookup on all twenty with personal mailboxes off, read the twenty rows with <code>get-dataset-items</code>, and wrote its answer. Four tool calls, one of them paid, under four minutes. It did not run the free search, and said so: nothing in the task needed it.</p>
<p>Its ledger for the twenty: seven current, eight older, two with nothing, three profiles gone. Then it did the reading I would have skipped on a Friday.</p>
<p>One "older" row held an address on the current employer's domain further down the list. It proposed matching the whole list against the CRM's domain before discarding anything, the same fix I arrived at with eighty-one rows and a second Actor.</p>
<p>One row's only address sat on a domain made of the person's own name. A one-person consultancy mailbox in practice, it said, and excluded under the policy whatever the label says.</p>
<p>It noticed that <code>profile_removed</code> is not in the tool's description, read the status correctly anyway, and said it could not confirm the meaning with the tools it had. That gap in the listing is mine to close.</p>
<p>It also noted that <code>_freshness</code> did not vary across its fifteen found rows, so on that sample the field filtered nothing. True, and worth knowing before you build a rule on it.</p>
<p>Before the remaining two thousand it asked for five things: normalise and dedupe the URLs on the handle, compare <code>work_current</code> employers with the CRM's, verify deliverability before trusting hundreds, watch the share of gone profiles, and poll the run instead of waiting inline. I would sign all five.</p>
<h2>Before you send</h2>
<p>Addresses from the first Actor are on record, not verified at run time. The second Actor's check says a mailbox accepts mail. Neither says the person reads it.</p>
<p>The label can lag. A job change in the last few months can still show as current. Compare the row's employer with the one you hold.</p>
<p>Brands with a second mail domain show up as "older". Match on your domain list, not on the label alone.</p>
<p>Seven of the hundred and ninety-eight URLs pointed at a profile that no longer exists. A CRM full of URLs from three years ago will have more. Keep that share in view, it is free to measure.</p>
<p>Consent and the law are yours: CAN-SPAM, GDPR, PECR, whatever applies where the person sits. A verified address is not permission.</p>
<h2>FAQ</h2>
<p><strong>Is this a Hunter or Apollo email finder replacement?</strong> For turning profile URLs into addresses in bulk, yes: no seat, no browser extension, you pay per found row and the misses are free. For a Chrome plugin that fills an address while you browse, no.</p>
<p><strong>Does it guess addresses?</strong> The first Actor does not: it returns only addresses that exist in the person's record and says so when there are none. The second one builds candidates from name and domain and can verify the best one. That is the guessing door, and it is labelled as such.</p>
<p><strong>What does</strong> <code>work_other</code> <strong>mean?</strong> A company address the finder cannot tie to the current employer. Usually a previous job, sometimes the same company under a brand domain. Check the <code>workEmails</code> list against your own domains before you decide.</p>
<p><strong>Why were some addresses personal when I did not ask?</strong> They were not, unless <code>includePersonalEmails</code> is on. With it on, a personal mailbox outranks an older company address on the row.</p>
<p><strong>How big can one run be?</strong> Up to a thousand URLs per run on this Actor. A free Apify plan returns up to a hundred results per run and five hundred rows a month; any paid plan removes that.</p>
<h2>For AI agents reading this</h2>
<p>The three Actors run over the hosted Apify MCP server with the tools pinned in the URL, so an agent loads only these definitions:</p>
<pre><code class="language-plaintext">claude mcp add --transport http apify "https://mcp.apify.com?tools=b2bsearch/linkedin-email-finder,b2bsearch/work-email-finder,b2bsearch/people-database-search" --header "Authorization: Bearer &lt;APIFY_TOKEN&gt;"
</code></pre>
<pre><code class="language-json">{
  "mcpServers": {
    "apify": {
      "type": "http",
      "url": "https://mcp.apify.com?tools=b2bsearch/linkedin-email-finder,b2bsearch/work-email-finder,b2bsearch/people-database-search",
      "headers": { "Authorization": "Bearer &lt;APIFY_TOKEN&gt;" }
    }
  }
}
</code></pre>
<p>Tool names use a double hyphen: <code>b2bsearch--linkedin-email-finder</code>, <code>b2bsearch--work-email-finder</code>, <code>b2bsearch--people-database-search</code>. For a list of profile URLs: dedupe on the handle, run the email finder with <code>profileUrls</code> and <code>includePersonalEmails: false</code>, then read rows with <code>get-dataset-items</code> and a <code>fields</code> list (<code>_status</code>, <code>emailType</code>, <code>email</code>, <code>workEmails</code>, <code>emailCount</code>, <code>jobTitle</code>, <code>companyName</code>, <code>_freshness</code>, <code>_note</code>).</p>
<p>Rows with <code>_status</code> other than <code>found</code> are free: <code>no_email</code> (a <code>_note</code> says when a personal mailbox is on record), <code>not_found</code>, <code>profile_removed</code>. For <code>work_other</code> rows, match every entry of <code>workEmails</code> against the user's company domain first.</p>
<p>For what is left, run <code>work-email-finder</code> with <code>lookups</code> of <code>{ "domain", "full_name" }</code> and <code>verifyTopCandidate: true</code>, and read <code>email_validity</code>. To size a segment before any paid pull, run <code>people-database-search</code> with <code>mode: "market"</code> or <code>mode: "count"</code>. Per-tier pricing is on each Actor page.</p>
<h2>Earlier posts on the same question</h2>
<p>The <a href="https://dev.to/kevinhall/243-of-417-leads-in-a-typical-icp-box-were-dead-before-the-first-email-epo">lead list autopsy</a> counts how many leads in an ordinary ICP box were dead before the first email. The <a href="https://dev.to/kevinhall/5860-fractional-cfos-by-headline-2631-by-title-the-gap-is-the-list-3p88">fractional CFO post</a> is where I first saw work addresses that belonged to former employers.</p>
<p>The <a href="https://dev.to/kevinhall/apollo-alternative-in-claude-code-7-lead-lists-from-one-apify-actor-4olo">Apollo alternative</a> builds the lists these URLs usually come from, and the <a href="https://dev.to/kevinhall/which-b2b-data-actor-to-run-on-apify-by-the-key-you-already-hold-domains-profile-urls-emails-5g8a">Actor map by key</a> says which door to use when you hold domains, emails or names instead of URLs.</p>
]]></content:encoded></item><item><title><![CDATA[MCP tool cost: 20 job Actors on Apify, 2.1K to 32.7K tokens each]]></title><description><![CDATA[tools/list on the hosted Apify MCP server for 20 job Actors: median 3.8K tokens per tool, one at 32.7K, 98% of it an inferred schema.]]></description><link>https://gtm-workflows.hashnode.dev/mcp-tool-cost-20-job-actors-on-apify-2-1k-to-32-7k-tokens-each</link><guid isPermaLink="true">https://gtm-workflows.hashnode.dev/mcp-tool-cost-20-job-actors-on-apify-2-1k-to-32-7k-tokens-each</guid><category><![CDATA[mcp]]></category><category><![CDATA[AI]]></category><category><![CDATA[claude-code]]></category><category><![CDATA[apify]]></category><dc:creator><![CDATA[Kevin Hall]]></dc:creator><pubDate>Sat, 31 Oct 2026 13:56:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6ac951f8c58437c7a9101e18/e98c8473-2454-49c8-a1b3-deb76d354924.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Here is what an agent pays before it asks a job-search tool anything. I pinned 20 job-search Actors from the Apify Store, one at a time, on the hosted Apify MCP server and read what <code>tools/list</code> sends back. The smallest tool definition was 2,139 tokens. The median was 3,829. One was 32,737, and 32,136 of those tokens were an output schema the server had inferred from the Actor's results; the author's own input schema was 406 tokens.</p>
<p>Three of the twenty are mine, so this is also a check on my own work. The numbers are from 28 October 2026, and the method is a curl handshake you can rerun on any Actor in a minute.</p>
<h2>Why the tool definition is the first cost</h2>
<p>An MCP client loads every tool's name, description and JSON schema before the model reads your question. On the hosted Apify server each Actor becomes one tool, and the tool carries three parts: the description (the Actor's Store description after a one-line preamble), the input schema (the Actor's input fields, as the author wrote them), and an output schema that the server infers from what the Actor's runs return (<a href="https://docs.apify.com/platform/integrations/mcp">documented here</a>; the local stdio server does not add it). The third part is the one most Actor authors have never looked at, because they never wrote it.</p>
<p><a href="https://dev.to/aws-builders/mcp-token-cost-i-cut-my-mcp-server-from-15-tools-to-4-cek">A post two days ago</a> measured a hand-built server at 15 tools and rebuilt it to 4. I wanted the same number for tools I did not build and cannot rebuild: the Actors an agent meets when a user says "find me backend jobs in Germany".</p>
<h2>How I measured</h2>
<p>Hosted server, one Actor pinned per session with <code>?tools=</code>, then the plain <code>tools/list</code> after the initialize handshake:</p>
<pre><code class="language-bash">U='https://mcp.apify.com?tools=curious_coder/linkedin-jobs-scraper'
H=(-H "Authorization: Bearer $APIFY_TOKEN" -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream')

# 1. initialize; the session id comes back as a response header
curl -s -D hdr -o /dev/null "$U" "${H[@]}" -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"probe","version":"0"}}}'
SID=$(grep -i '^mcp-session-id' hdr | awk '{print $2}' | tr -d '\r')

# 2. initialized notification, 3. the list (server-sent events; the data: line holds the JSON-RPC result)
curl -s -o /dev/null "$U" "${H[@]}" -H "Mcp-Session-Id: $SID" -d '{"jsonrpc":"2.0","method":"notifications/initialized"}'
curl -s "$U" "${H[@]}" -H "Mcp-Session-Id: $SID" -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' &gt; tools.sse
</code></pre>
<p>I serialized each tool definition as compact JSON and counted tokens with tiktoken's <code>o200k_base</code>. Claude's tokenizer is not public, so treat the counts as a scale, not an invoice. The relative sizes are the point, and those do not depend on the tokenizer.</p>
<p>The twenty are the Actors the Store returns for "linkedin jobs", "job search", "job postings", "indeed jobs" and "glassdoor jobs", ranked by users in the last 30 days, plus my three (they are new and would not have made the cut on users).</p>
<h2>The table</h2>
<table>
<thead>
<tr>
<th>Actor</th>
<th align="right">tokens per tool</th>
<th align="right">description</th>
<th align="right">input schema</th>
<th align="right">output schema</th>
<th align="right">input fields</th>
</tr>
</thead>
<tbody><tr>
<td><code>valig/indeed-jobs-scraper</code></td>
<td align="right">32,737</td>
<td align="right">52</td>
<td align="right">406</td>
<td align="right">32,136</td>
<td align="right">6</td>
</tr>
<tr>
<td><code>cheap_scraper/linkedin-job-scraper</code></td>
<td align="right">7,723</td>
<td align="right">87</td>
<td align="right">6,061</td>
<td align="right">1,440</td>
<td align="right">38</td>
</tr>
<tr>
<td><code>bebity/linkedin-jobs-scraper</code></td>
<td align="right">6,769</td>
<td align="right">57</td>
<td align="right">5,029</td>
<td align="right">1,551</td>
<td align="right">18</td>
</tr>
<tr>
<td><code>fantastic-jobs/advanced-linkedin-job-search-api</code></td>
<td align="right">6,569</td>
<td align="right">86</td>
<td align="right">4,312</td>
<td align="right">2,018</td>
<td align="right">41</td>
</tr>
<tr>
<td><code>fantastic-jobs/career-site-job-listing-api</code></td>
<td align="right">6,324</td>
<td align="right">93</td>
<td align="right">4,083</td>
<td align="right">1,999</td>
<td align="right">39</td>
</tr>
<tr>
<td><code>b2bsearch/linkedin-job-search-api (mine)</code></td>
<td align="right">5,770</td>
<td align="right">92</td>
<td align="right">3,410</td>
<td align="right">2,123</td>
<td align="right">38</td>
</tr>
<tr>
<td><code>b2bsearch/linkedin-jobs-scraper (mine)</code></td>
<td align="right">4,389</td>
<td align="right">107</td>
<td align="right">2,403</td>
<td align="right">1,729</td>
<td align="right">25</td>
</tr>
<tr>
<td><code>agentx/all-jobs-scraper</code></td>
<td align="right">4,041</td>
<td align="right">69</td>
<td align="right">2,060</td>
<td align="right">1,780</td>
<td align="right">11</td>
</tr>
<tr>
<td><code>epctex/google-jobs-scraper</code></td>
<td align="right">3,981</td>
<td align="right">72</td>
<td align="right">2,708</td>
<td align="right">1,068</td>
<td align="right">14</td>
</tr>
<tr>
<td><code>harvestapi/linkedin-job-search</code></td>
<td align="right">3,911</td>
<td align="right">43</td>
<td align="right">1,492</td>
<td align="right">2,217</td>
<td align="right">19</td>
</tr>
<tr>
<td><code>openclawai/job-board-scraper</code></td>
<td align="right">3,748</td>
<td align="right">82</td>
<td align="right">2,054</td>
<td align="right">1,461</td>
<td align="right">21</td>
</tr>
<tr>
<td><code>borderline/indeed-scraper</code></td>
<td align="right">3,416</td>
<td align="right">78</td>
<td align="right">1,096</td>
<td align="right">2,094</td>
<td align="right">15</td>
</tr>
<tr>
<td><code>curious_coder/linkedin-jobs-scraper</code></td>
<td align="right">3,343</td>
<td align="right">52</td>
<td align="right">1,711</td>
<td align="right">1,444</td>
<td align="right">14</td>
</tr>
<tr>
<td><code>b2bsearch/hiring-companies-finder (mine)</code></td>
<td align="right">3,303</td>
<td align="right">101</td>
<td align="right">1,329</td>
<td align="right">1,723</td>
<td align="right">15</td>
</tr>
<tr>
<td><code>fast_data_asap/career-site-jobs-api</code></td>
<td align="right">3,235</td>
<td align="right">106</td>
<td align="right">1,777</td>
<td align="right">1,277</td>
<td align="right">13</td>
</tr>
<tr>
<td><code>fast_data_asap/job-search-api</code></td>
<td align="right">2,562</td>
<td align="right">92</td>
<td align="right">1,108</td>
<td align="right">1,291</td>
<td align="right">13</td>
</tr>
<tr>
<td><code>misceres/indeed-scraper</code></td>
<td align="right">2,509</td>
<td align="right">62</td>
<td align="right">925</td>
<td align="right">1,394</td>
<td align="right">9</td>
</tr>
<tr>
<td><code>valig/linkedin-jobs-scraper</code></td>
<td align="right">2,180</td>
<td align="right">54</td>
<td align="right">682</td>
<td align="right">1,310</td>
<td align="right">14</td>
</tr>
<tr>
<td><code>valig/glassdoor-jobs-scraper</code></td>
<td align="right">2,140</td>
<td align="right">59</td>
<td align="right">599</td>
<td align="right">1,340</td>
<td align="right">13</td>
</tr>
<tr>
<td><code>apimaestro/linkedin-jobs-scraper-api</code></td>
<td align="right">2,139</td>
<td align="right">59</td>
<td align="right">564</td>
<td align="right">1,375</td>
<td align="right">12</td>
</tr>
</tbody></table>
<img src="https://raw.githubusercontent.com/keyvwin/workflows/main/assets/charts/mcp-tool-cost-20-job-actors-2026-10.png" alt="Tokens an agent loads per job-search Actor on Apify MCP, 20 Actors, stacked by description, input schema and output schema" style="display:block;margin:0 auto" />

<h2>Where the tokens go</h2>
<p>The input schema tracks the number of fields, not the quality of the Actor. Six fields cost 406 tokens; 38 to 41 fields cost 3,410 to 6,061. Every filter an author adds is a line the agent reads on every turn it has the tool loaded. Mine sits in that band on purpose: 38 fields, 3,410 tokens, because the Actor exposes the database's filters (boolean title and description search, exclusions, employer size, industry, salary, applicants, expiry) instead of hiding them behind one free-text box. That is a trade, and the number is its price.</p>
<p>The output schema is the larger half in nine of the twenty tools. The server generates it from the Actor's dataset, so a scraper that returns a deeply nested row pays for it on every turn, and the author never sees it unless they run <code>tools/list</code> themselves. The Indeed scraper at the top of the table returns rows that produced a 3,400-node schema; the tool definition for my jobs Actor has 229 nodes. The other nineteen output schemas came in between 1,068 and 2,217 tokens, which looks like the floor for a flat row with a few dozen keys.</p>
<p>The description is cheap, 43 to 107 tokens. The server takes the Store description verbatim after a preamble ("This tool calls the Actor ... and retrieves its output results. Actor description: ..."). So the one text an author writes for humans is also the text the agent reads to choose between tools, and it is under three percent of the tool's cost. Spend the words there.</p>
<h2>What the server does to a listing</h2>
<p>Three transformations were visible in the payloads.</p>
<p>Long field descriptions are cut. On the Actor with the longest ones, four of 38 fields ended in "[Description truncated]"; their raw descriptions ran 518 to 810 characters and came through at 371 to 495 plus the marker. Whatever you wrote after the cut, the agent never reads.</p>
<p>Prefill values are appended to the description as "Example values: ...". If the prefill is a realistic example, that is free documentation. If it is a placeholder, the agent copies the placeholder.</p>
<p><code>required</code> is passed through as written. Four of the twenty require at least one field. A required field that also has a default is a small trap: the agent must supply it even though the Actor would run without it.</p>
<h2>The fixed overhead you pay even for one Actor</h2>
<p>Pin one Actor and the server still sends four helper tools: get-actor-run (1,437 tokens), abort-actor-run (1,311), get-dataset-items (667) and get-key-value-store-record (373). That is 3,788 tokens before the Actor itself; for the median Actor, half the bill.</p>
<p>Without <code>?tools=</code>, the default server sends 12 tools and 18,391 tokens: the three discovery tools (search-actors 2,315, fetch-actor-details 1,755, call-actor 2,009), the four helpers, two docs tools, a problem reporter and two web tools (rag-web-browser 4,508, web-fetch 2,647). <code>?tools=actors</code> keeps discovery and cuts the rest: 7 tools, 9,867 tokens.</p>
<h2>What Claude Code does with it</h2>
<p>I have five recorded <code>claude -p</code> sessions over the pinned jobs tools from the last three posts, two or three Actors pinned each time. In all five, the agent's first action was <code>ToolSearch</code> with <code>select:mcp__apify__b2bsearch--linkedin-job-search-api,...</code>. Claude Code defers MCP tool definitions above a size threshold: the model sees the names at the start and pulls a definition into context when it decides to use the tool. The first turn of the Java-in-Berlin session carried 43,743 tokens of context before any Actor definition had been fetched.</p>
<p>So in Claude Code the tool tax is not paid on turn one. It is paid from the turn the definition is loaded, on every turn after. The session that ran 18 free previews took 39 turns and read 523,825 cached input tokens in eleven minutes, and the tool definitions were part of every one of those reads. Across the five sessions the agent used between 9 and 16 of the Actor's 38 input fields; the other 22 to 29 rode along in every turn as schema the agent had read and did not need.</p>
<p>A host without deferral puts the whole <code>tools/list</code> into the system prompt on turn one, and pays the table above on every turn from the first. The 15-to-4 post I linked measured exactly that case.</p>
<h2>Discovery versus pinning: two sessions, one question</h2>
<p>Same prompt, two Claude Code sessions, one difference in the MCP URL. The prompt is a backend developer in Berlin with six years of Java asking which technology to learn next, judged by what German employers ask for: count the base, count the current stack in descriptions, count eight candidates, count two of them together with Java.</p>
<p>Pinned, <code>?tools=b2bsearch/linkedin-job-search-api,b2bsearch/hiring-companies-finder</code> (the recorded session from the third post): 39 turns, eleven minutes, 18 free previews, 523,825 cached input tokens read. The agent had exact counts for the base and for every technology, by employer, and picked Kubernetes with AWS close behind.</p>
<p>Discovery, <code>?tools=actors</code> (search, details, call, plus the four helpers: 9,867 tokens of definitions), with money rules added to the prompt: read an Actor's pricing before calling it, cap every call, keep the total small, prefer free counts. 33 turns, fifteen minutes, 32 tool calls: 5 Store searches, 12 listing fetches, 3 paid runs on two scrapers, 11 reads of run state and rows. 220,114 tokens written to cache and 1,571,639 read, three times the pinned session. The agent never saw my Actor: the five searches came back led by the most-used scrapers, and a tool with a handful of users in its first week was in none of the lists. It chose the most-used LinkedIn scraper and an Indeed scraper, pulled 225 rows, filtered them to 82 backend postings, and answered with shares of that sample, labelled as a sample with a ten-point margin, and picked AWS with Kubernetes close behind. The three runs all stayed under the cap the agent had set on each call.</p>
<p>Where the discovery turns went is the part worth keeping. Two of the twelve listing fetches came back over Claude Code's tool-result limit (61,678 and 68,510 characters: a README plus both schemas), one hit a 502, and the rest were 14,000 to 24,000 characters each. Reading listings cost the session more context than running them.</p>
<p>My first attempt at the discovery session had to be killed, and the reason belongs in the post. I had left the shell tool allowed. When a listing fetch overflowed and Claude Code wrote it to a file, the agent read the file with the shell, then started checking the environment and my credentials file for an Apify token so it could call the API directly. Nothing was sent anywhere and the transcript holds no token, but an agent with a shell and a blocked tool result will look for another road. The second run had <code>--permission-mode default</code> and the shell in <code>--disallowedTools</code>, and stayed on the MCP tools.</p>
<h2>What I keep in my own schema, and what I changed</h2>
<p>The three b2bsearch Actors follow rules I had set before this measurement, and the payloads confirm they survive the server:</p>
<ul>
<li>The two fields that change what a run costs, <code>previewOnly</code> and <code>compact</code>, are the fourth and fifth fields in the schema, before any filter, so a client that shows the first N fields shows them.</li>
<li>Field descriptions are capped at 350 characters by a test in the build (the longest live one is 342). None of my fields carries the "[Description truncated]" marker.</li>
<li>No required fields. <code>{"previewOnly": true, "titleSearch": ["backend"], "locationSearch": ["de"]}</code> is a complete call.</li>
<li>Rows nest two levels at most (a company card, a recruiter), which gives an output schema of 2,123 tokens and 229 schema nodes. The 3,400-node schema at the top of the table cost 32,136.</li>
</ul>
<p>What this measurement changed: I had been treating the input schema as the tool's cost. It is a third to a half of it. The helper tools and the inferred output schema are the rest, and only the second of those is under an author's control, through the shape of the rows the Actor writes.</p>
<p>What I did not do is cut fields. The recorded sessions used nine to sixteen of them, different ones each time, and a database-backed search with fewer filters would just move the filtering into the agent's own loop, which is far more expensive than a schema line.</p>
<h2>Three calls to try on the jobs tools</h2>
<p>The table is about cost. Here is what the three pinned tools of mine return when an agent asks a plain question. Every call below is a free preview: no rows, no charge, just the count and a breakdown. I ran them on 31 October 2026 and the data runs through 21 October.</p>
<p><a href="https://apify.com/b2bsearch/linkedin-job-search-api"><strong>LinkedIn Jobs Database API</strong></a><strong>: "how many backend postings in Germany, and who answers them?"</strong></p>
<pre><code class="language-json">{"previewOnly": true, "titleSearch": ["backend engineer"], "locationSearch": ["de"], "timeRange": "30d"}
</code></pre>
<p>155 open postings at 101 employers. Berlin has 41 of them and Munich 17. 18 state a salary and 14 name a recruiter. 55 had fewer than 25 applicants and 29 had over 200, which is the number I would show a candidate before they pick where to apply. My first try used the default <code>7d</code> window and came back with 0, because the data stops on 21 October; the note on the preview row says so, which is what lets an agent widen the window by itself.</p>
<p><a href="https://apify.com/b2bsearch/hiring-companies-finder"><strong>Companies Hiring Now</strong></a><strong>: "which employers are hiring data engineers?"</strong></p>
<pre><code class="language-json">{"previewOnly": true, "jobTitles": ["data engineer"], "locations": ["Germany"], "postedLimit": "month"}
</code></pre>
<p>292 employers posted 540 matching jobs in the last month, agencies and job boards already left out (that filter is on by default here). 192 of the postings went up in the week of 12 October, the busiest of the four. The same call with <code>previewOnly</code> off and <code>decisionMakers</code> on returns one row per employer with the people who would own the hire, which is the version a recruiter or a salesperson wants.</p>
<p><a href="https://apify.com/b2bsearch/linkedin-jobs-scraper"><strong>Advanced LinkedIn Jobs Scraper</strong></a><strong>: "is there a market for SREs in the Netherlands?"</strong></p>
<pre><code class="language-json">{"previewOnly": true, "jobTitles": ["site reliability engineer"], "locations": ["Netherlands"], "postedLimit": "month", "excludeAgencies": true}
</code></pre>
<p>18 open jobs at 14 employers, 12 of them mid-senior. 5 name a recruiter and 4 state a salary, all in euros. Small enough that the next call can pull every row with <code>compact: true</code> and still stay cheap for the agent's context.</p>
<p>In an agent session I do not write the JSON. I pin the tool as in the section below and ask in words: "count backend engineer postings in Germany over the last 30 days, free preview only, and tell me which cities lead". The agent fills <code>titleSearch</code>, <code>locationSearch</code> and <code>timeRange</code> from the schema, reads the preview row and answers in one call.</p>
<h2>For AI agents reading this</h2>
<p>To use the jobs tool from Claude Code with the smallest tool set, pin it:</p>
<pre><code class="language-bash">claude mcp add --transport http apify "https://mcp.apify.com?tools=b2bsearch/linkedin-job-search-api" --header "Authorization: Bearer &lt;APIFY_TOKEN&gt;"
</code></pre>
<p>or in any MCP client config:</p>
<pre><code class="language-json">{"mcpServers": {"apify": {"type": "http", "url": "https://mcp.apify.com?tools=b2bsearch/linkedin-job-search-api", "headers": {"Authorization": "Bearer &lt;APIFY_TOKEN&gt;"}}}}
</code></pre>
<p>The tool is named <code>b2bsearch--linkedin-job-search-api</code> (<code>mcp__apify__b2bsearch--linkedin-job-search-api</code> inside Claude Code). Call it first with <code>previewOnly: true</code>: it returns the count of matching postings and a breakdown by country, seniority, employment type, industry, top employers, titles and cities, no rows and no charge. Then turn <code>previewOnly</code> off and set <code>limit</code> to pull rows; rows come back through <code>get-dataset-items</code>. Filters that matter most: <code>titleSearch</code> (one line per title variant, boolean allowed), <code>locationSearch</code> (country codes or cities), <code>timeRange</code> (<code>7d</code>, <code>30d</code>, <code>6m</code>), <code>descriptionSearch</code> (one line per technology, lines are OR, quote phrases), <code>removeAgency: true</code> to drop staffing agencies and job boards, <code>status</code> (<code>active</code>, <code>expired</code>, <code>any</code>).</p>
<p>To measure any Actor's tool cost: run the handshake above with <code>?tools=&lt;owner/actor&gt;</code>, take the <code>data:</code> line of the <code>tools/list</code> response, and count the compact JSON of each entry in <code>result.tools</code>. Expect four helper tools in the list besides the Actor.</p>
<h2>What this does not measure</h2>
<p>Token counts are tiktoken's, not Claude's; the ranking holds, the absolute numbers are a scale. I measured one host, Claude Code, for deferral; other hosts load the list differently. The twenty Actors are a Store search on one day, not the whole category. And none of this says whether a bigger schema makes an agent worse at the task; the post I linked measured call counts for one server, and the discovery session above is one run, not a benchmark.</p>
<p>This post is a companion to the series on developer job search with Claude Code: <a href="https://dev.to/kevinhall/job-search-in-claude-code-half-of-us-backend-postings-were-copies-1f9j">the first post</a> searches the market by stack, <a href="https://dev.to/kevinhall/ghost-jobs-measured-58-of-uk-backend-postings-came-from-middlemen-nhl">the second</a> measures ghost jobs, <a href="https://dev.to/kevinhall/backend-skills-employers-ask-for-aws-and-kubernetes-beat-go-and-rust-4gk9">the third</a> counts the technologies employers ask for, all over the same pinned tool. The Actors are <a href="https://apify.com/b2bsearch/linkedin-job-search-api">LinkedIn Jobs Database API</a>, <a href="https://apify.com/b2bsearch/hiring-companies-finder">Companies Hiring Now</a> and <a href="https://apify.com/b2bsearch/linkedin-jobs-scraper">Advanced LinkedIn Jobs Scraper</a>; per-tier pricing is on each Actor page.</p>
]]></content:encoded></item><item><title><![CDATA[B2B data API for AI agents: 7 Apify Actors I use in day-to-day tasks]]></title><description><![CDATA[Seven Apify Actors pinned as MCP tools, 23,222 tokens for all seven: count first, pay per result, no scraping.]]></description><link>https://gtm-workflows.hashnode.dev/b2b-data-api-for-ai-agents-7-apify-actors-i-use-in-day-to-day-tasks</link><guid isPermaLink="true">https://gtm-workflows.hashnode.dev/b2b-data-api-for-ai-agents-7-apify-actors-i-use-in-day-to-day-tasks</guid><category><![CDATA[AI]]></category><category><![CDATA[mcp]]></category><category><![CDATA[api]]></category><category><![CDATA[lead generation]]></category><dc:creator><![CDATA[Kevin Hall]]></dc:creator><pubDate>Sat, 31 Oct 2026 13:55:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6ac951f8c58437c7a9101e18/156bd951-e62e-417a-9c66-103314fa9b67.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Most of my week is lists. Someone sends a filter, a pile of domains or a column of profile URLs, and wants people with a working address on the other side. The question that comes with it is always the same: which one do I run.</p>
<p>This is the answer I now send back. Seven Actors on Apify, all reading one database of professional profiles and companies: links, what each one takes and returns, what I measured on them on 30 October, and how an AI agent calls them over MCP.</p>
<p>If your last week included a 403 page, a CAPTCHA, or a scraping account that stopped working, start with the next section. It explains why these are lookups and not scrapes.</p>
<h2>In one paragraph</h2>
<p>The <a href="https://apify.com/b2bsearch">b2bsearch Actors on Apify</a> are a B2B data API over a database of 800M+ professional profiles and 115M companies. Filters, domains, profile URLs or emails go in; leads, employees, company cards, emails and phone numbers come out. Nothing is scraped at run time, misses and counts are free, and every Actor is an MCP tool for Claude Code and other agents.</p>
<h2>Why a database and not a scraper</h2>
<p>Most listings in this corner of the Store are live scrapers. They need a session cookie or a login, they run through proxies, a run takes minutes, and when the site answers 403 or 429 the scraper retries or stops. The account carrying the risk is yours.</p>
<p>Pricing follows the same logic. Many charge per returned row whether or not the row has what you asked for. Some add a start fee per run, a minimum budget per run, or a lifetime run cap for free accounts.</p>
<p>I read 86 listings in this category in October: about a third have a hard limit for free users and 16 charge a start fee of a cent or more.</p>
<p>The seven Actors below answer from a database. No cookies, no login, no proxies, and a run finishes in seconds. The things that are never charged are the part I would defend anywhere:</p>
<table>
<thead>
<tr>
<th>Never charged</th>
<th>How you see it</th>
</tr>
</thead>
<tbody><tr>
<td>a miss</td>
<td>a free row with <code>_status</code> and a note saying why</td>
</tr>
<tr>
<td>a count or a market breakdown</td>
<td><code>previewOnly</code> or <code>mode: "count"</code></td>
</tr>
<tr>
<td>a repeat of a person already delivered</td>
<td><code>excludeDatasets</code>, or inside the same run</td>
</tr>
<tr>
<td>a row that fails your <code>mustHave</code></td>
<td>one free note row with the number skipped</td>
</tr>
</tbody></table>
<p>Two honest limits. A database lags a live page: rows about people carry a refreshed-on stamp and a <code>_freshness</code> bucket, and the people and leads searches take <code>updatedAfter</code>, so you can insist on fresh records and pay only for those.</p>
<p>And on price I will not claim the cheapest in every niche. In some of them a listing with more users charges less per row; on company search and company employees these sit below the category leader on every plan. The list of what is never charged is where they differ most.</p>
<h2>The seven, by what you hold</h2>
<table>
<thead>
<tr>
<th>You hold</th>
<th>Run</th>
<th>You get</th>
</tr>
</thead>
<tbody><tr>
<td>a filter box (industry, country, size, funding)</td>
<td><a href="https://apify.com/b2bsearch/company-database-search">Company Search</a></td>
<td>companies, with a free count first</td>
</tr>
<tr>
<td>a website domain</td>
<td><a href="https://apify.com/b2bsearch/domain-to-company">Company Enrichment by Domain</a></td>
<td>the company card</td>
</tr>
<tr>
<td>a website domain</td>
<td><a href="https://apify.com/b2bsearch/company-employees">Company Employees</a></td>
<td>the roster, full profiles, or people with contacts</td>
</tr>
<tr>
<td>a filter box or an Apollo URL</td>
<td><a href="https://apify.com/b2bsearch/b2b-leads-finder">B2B Leads Finder</a></td>
<td>people with a live email</td>
</tr>
<tr>
<td>profile URLs</td>
<td><a href="https://apify.com/b2bsearch/linkedin-email-finder">Email Finder</a></td>
<td>addresses on record, labelled</td>
</tr>
<tr>
<td>profile URLs, emails or names</td>
<td><a href="https://apify.com/b2bsearch/linkedin-to-phone">Phone Number Finder</a></td>
<td>verified mobile numbers</td>
</tr>
<tr>
<td>email addresses</td>
<td><a href="https://apify.com/b2bsearch/email-to-linkedin">Email to Profile Finder</a></td>
<td>the person and their profile</td>
</tr>
</tbody></table>
<h2>How to read the sections below</h2>
<p>Every section starts the same way: the Actor link, a short overview, what it takes (the filters that matter) and what it returns. Then come scenarios you can repeat, each with the exact input and a table of the fields that come back.</p>
<p>All runs are from 30 October 2026. They go through the same three addresses, so one example is enough to learn the pattern:</p>
<table>
<thead>
<tr>
<th>Address</th>
<th>Looks like</th>
</tr>
</thead>
<tbody><tr>
<td>Store page</td>
<td><code>https://apify.com/b2bsearch/&lt;slug&gt;</code></td>
</tr>
<tr>
<td>REST endpoint</td>
<td><code>https://api.apify.com/v2/acts/b2bsearch~&lt;slug&gt;/run-sync-get-dataset-items?token=&lt;APIFY_TOKEN&gt;</code>, POST the input JSON as the body</td>
</tr>
<tr>
<td>MCP tool</td>
<td><code>b2bsearch--&lt;slug&gt;</code> on <code>https://mcp.apify.com?tools=b2bsearch/&lt;slug&gt;</code></td>
</tr>
</tbody></table>
<p>The slug is the last part of the link in each section's first line. The headline segment, where I use one, is fintech companies in London with 51 to 200 employees that raised a round since January 2024. Everything else is a fresh example you can paste.</p>
<h2>1. Company Search: filters to companies, count first</h2>
<p><a href="https://apify.com/b2bsearch/company-database-search">Company Search</a> · slug <code>company-database-search</code></p>
<p>You give it a filter box and it returns company rows. The first call should always be a free count: <code>previewOnly: true</code> answers with the size of the segment and charges nothing.</p>
<p><strong>Takes</strong> (the filters that matter):</p>
<ul>
<li><code>countries</code>, or <code>countryGroups</code> such as <code>dach</code>, <code>nordics</code>, <code>eu</code>, <code>gcc</code>, <code>latam</code></li>
<li><code>industries</code> and <code>hqLocations</code> (city or state)</li>
<li><code>employeesMin</code> and <code>employeesMax</code>, <code>foundedFrom</code> and <code>foundedTo</code></li>
<li><code>hasFunding</code>, <code>fundingRounds</code> (<code>seed</code>, <code>series_a</code> and on), <code>fundedAfter</code>, <code>fundingAmountMin</code></li>
<li><code>companyDomains</code> to look up a known list, <code>previewOnly</code>, <code>maxResults</code></li>
</ul>
<p><strong>Returns:</strong> one row per company.</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>What it holds</th>
<th>Why it helps</th>
</tr>
</thead>
<tbody><tr>
<td><code>name</code>, <code>domain</code>, <code>website</code></td>
<td>the company and where it lives online</td>
<td>the key you feed into every other Actor</td>
</tr>
<tr>
<td><code>linkedinUrl</code></td>
<td>the company page URL</td>
<td>a stable id for joins</td>
</tr>
<tr>
<td><code>employeeCount</code>, <code>industry</code>, <code>founded</code></td>
<td>size, sector, founding year</td>
<td>segment and prioritise offline</td>
</tr>
<tr>
<td><code>hq</code>, <code>countryCode</code></td>
<td>headquarters place</td>
<td>route by region</td>
</tr>
<tr>
<td><code>funding</code></td>
<td>last round type, date and amount</td>
<td>"raised a Series A in 2025" is a filter, not research</td>
</tr>
<tr>
<td><code>description</code></td>
<td>what the company says it does</td>
<td>a line for an opener or an LLM prompt</td>
</tr>
</tbody></table>
<h3>Scenario A: Series A software companies in Germany</h3>
<p>Count first, free:</p>
<pre><code class="language-json">{
  "countries": ["de"],
  "industries": ["Software Development"],
  "fundingRounds": ["series_a"],
  "fundedAfter": "2025-01-01",
  "employeesMin": 20,
  "employeesMax": 200,
  "previewOnly": true
}
</code></pre>
<p>One row back in 4 seconds: 16 companies match. Swap <code>previewOnly</code> for <code>"maxResults": 5</code> and the same call returns five company rows in 5 seconds, each with its round type, date and, when it is public, the amount.</p>
<h3>Scenario B: a whole region with one word</h3>
<p><code>countryGroups: ["dach"]</code> stands for Germany, Austria and Switzerland, so one input covers three markets:</p>
<pre><code class="language-json">{
  "countryGroups": ["dach"],
  "industries": ["Financial Services"],
  "employeesMin": 11,
  "employeesMax": 50,
  "hasFunding": true,
  "previewOnly": true
}
</code></pre>
<p>Free answer: 236 companies. If that is too many, add <code>fundingRounds</code> or a city before you pay for a single row.</p>
<h3>Scenario C: the headline segment</h3>
<pre><code class="language-json">{
  "countries": ["gb"],
  "hqLocations": ["London"],
  "industries": ["Financial Services"],
  "employeesMin": 51,
  "employeesMax": 200,
  "fundedAfter": "2024-01-01",
  "maxResults": 20
}
</code></pre>
<table>
<thead>
<tr>
<th>Step</th>
<th>Result</th>
</tr>
</thead>
<tbody><tr>
<td>count, free</td>
<td>79 companies</td>
</tr>
<tr>
<td>rows pulled</td>
<td>20, largest first</td>
</tr>
<tr>
<td>run time</td>
<td>about 5 seconds</td>
</tr>
</tbody></table>
<p>The trap is headcount: about half of all companies on file carry none, and a size filter drops them unless you switch on <code>includeCompaniesWithoutHeadcount</code>. For a funded fintech list that is fine. For long-tail segments, run the count both ways.</p>
<h2>2. Company Enrichment by Domain: one domain, one card</h2>
<p><a href="https://apify.com/b2bsearch/domain-to-company">Company Enrichment by Domain</a> · slug <code>domain-to-company</code></p>
<p>The simplest Actor in the family and the one where agents outnumber humans in my request logs. A list of domains in, a company card per domain out.</p>
<p><strong>Takes:</strong> one input, <code>domains</code>, a list of site domains.</p>
<p><strong>Returns:</strong> a card per domain.</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>What it holds</th>
<th>Why it helps</th>
</tr>
</thead>
<tbody><tr>
<td><code>companyName</code>, <code>domain</code>, <code>website</code></td>
<td>identity</td>
<td>clean up a CRM column</td>
</tr>
<tr>
<td><code>linkedinUrl</code></td>
<td>the company page URL</td>
<td>join key</td>
</tr>
<tr>
<td><code>employeeCount</code>, <code>employeeRange</code></td>
<td>headcount and its bucket</td>
<td>route small accounts and big ones differently</td>
</tr>
<tr>
<td><code>industry</code>, <code>description</code></td>
<td>sector and a plain-language line</td>
<td>tag accounts without reading sites</td>
</tr>
<tr>
<td><code>country</code>, <code>hqLocality</code>, <code>hqRegion</code></td>
<td>where the headquarters is</td>
<td>territory routing</td>
</tr>
<tr>
<td><code>founded</code></td>
<td>founding year</td>
<td>age as a signal</td>
</tr>
<tr>
<td><code>hasFunding</code>, <code>lastRoundType</code>, <code>lastRoundDate</code></td>
<td>funding state</td>
<td>"has money" in one column</td>
</tr>
</tbody></table>
<h3>Scenario A: three domains, three cards</h3>
<pre><code class="language-json">{ "domains": ["stripe.com", "wise.com", "notion.so"] }
</code></pre>
<p>Three cards in 8 seconds, each with a headcount, a sector and a funding flag.</p>
<h3>Scenario B: what it does when it is not sure</h3>
<p>I sent the eight domains from the London list in Company Search. Six came back as cards. Two came back as <code>ambiguous</code>, each with a note that two unrelated company pages list that domain and neither carries its name, and no charge. A guessed card would have cost more than a free "I do not know".</p>
<h3>Scenario C: sort a column of form fills</h3>
<p>Drop the domain part of every address on your sign-up list into <code>domains</code>, dedupe first, and read <code>employeeRange</code> and <code>hasFunding</code>. You get the account behind each form fill without opening a tab. The full inbound version is <a href="https://dev.to/kevinhall/inbound-lead-qualification-from-the-email-domain-company-record-decision-makers-and-the-gmail-1p5d">inbound lead qualification from the email domain</a>.</p>
<h2>3. Company Employees: the roster, the profiles, or the people you can write to</h2>
<p><a href="https://apify.com/b2bsearch/company-employees">Company Employees</a> · slug <code>company-employees</code></p>
<p>The account-based door. One company domain per line, and <code>profileDetail</code> picks how much each person row carries. Everything that narrows the list runs before anything is charged.</p>
<p><strong>Takes:</strong></p>
<ul>
<li><code>companies</code>: domains, one per line</li>
<li><code>profileDetail</code>: <code>roster</code> (staff list), <code>profile</code> (career record) or <code>contacts</code> (adds emails and phones)</li>
<li><code>roles</code>: <code>cxo</code>, <code>founder</code>, <code>vp</code>, <code>director</code>, <code>manager</code>, <code>other</code></li>
<li><code>titleContains</code> and <code>titleExcludes</code></li>
<li><code>mustHave</code>: <code>email</code>, <code>workEmail</code>, <code>currentWorkEmail</code>, <code>personalEmail</code>, <code>phone</code>, <code>github</code>, <code>twitter</code>, <code>facebook</code></li>
<li><code>compact: true</code> for agents, <code>maxRows</code> to cap</li>
</ul>
<p><strong>Returns:</strong> one row per person, wider with each tier.</p>
<table>
<thead>
<tr>
<th>Tier</th>
<th>Adds</th>
<th>Use it for</th>
</tr>
</thead>
<tbody><tr>
<td><code>roster</code></td>
<td>name, title, seniority, location, headline, start year, connections, flags <code>hasWorkEmail</code> and <code>hasPersonalEmail</code>, profile URL</td>
<td>building the org chart and picking who to look up</td>
</tr>
<tr>
<td><code>profile</code></td>
<td>everything above plus <code>about</code>, <code>positions</code>, <code>education</code>, <code>skills</code>, <code>interests</code>, <code>industry</code>, <code>followersCount</code></td>
<td>research before a call, personalisation</td>
</tr>
<tr>
<td><code>contacts</code></td>
<td>everything above plus <code>email</code>, <code>emailType</code>, <code>workEmails</code>, <code>personalEmails</code>, <code>emailCount</code>, <code>employerMatch</code>, <code>socialLinks</code></td>
<td>the send list</td>
</tr>
</tbody></table>
<h3>Scenario A: who runs this company</h3>
<pre><code class="language-json">{
  "companies": ["stripe.com"],
  "roles": ["founder", "cxo"],
  "profileDetail": "roster",
  "compact": true,
  "maxRows": 5
}
</code></pre>
<p>Five people in 4 seconds with title, seniority and the two email flags. The sixth row is a free note, <code>skipped_row_budget</code>, telling you more people matched and <code>maxRows</code> cut the list.</p>
<h3>Scenario B: the sales leaders of one company, with an address</h3>
<pre><code class="language-json">{
  "companies": ["wise.com"],
  "titleContains": ["head of sales", "sales director"],
  "profileDetail": "contacts",
  "mustHave": ["email"],
  "compact": true,
  "maxRows": 5
}
</code></pre>
<p>Four people in 6 seconds, each with an address, its label and the profile. People without an address are skipped for free and counted in one note row.</p>
<h3>Scenario C: leaders across a list of accounts</h3>
<p>I ran the two largest companies from the London list twice.</p>
<table>
<thead>
<tr>
<th>Run</th>
<th>Delivered</th>
<th>Skipped, free</th>
<th>Time</th>
</tr>
</thead>
<tbody><tr>
<td>roster, compact rows</td>
<td>25 rows</td>
<td>the rest of the roster, one note row</td>
<td>8 seconds</td>
</tr>
<tr>
<td>leaders (C-level, VP, founder, director) with an address on record, contacts tier</td>
<td>20 people</td>
<td>9 without an address, one note row</td>
<td>13 seconds</td>
</tr>
</tbody></table>
<p>Of the 20 leaders, 12 came with a company address and 7 with a personal one. The row says which tier it could actually answer: <code>_detail</code> reads <code>contacts</code> when a contact reaches the person today and <code>profile</code> when it does not, and the price follows the row, not the request.</p>
<h3>Scenario D: read before you write</h3>
<p>Take <code>companies: ["wise.com"]</code>, <code>roles: ["founder", "cxo"]</code>, <code>"profileDetail": "profile"</code>, <code>"compact": true</code> and <code>"maxRows": 3</code>. Three rows come back in 5 seconds with <code>about</code>, <code>positions</code>, <code>education</code>, <code>skills</code> and <code>followersCount</code>. That is enough context for an LLM to draft an opener that mentions something real.</p>
<p>Two more notes. A repeat of an employee already delivered never charges, so re-running a domain next month costs only the new people. And <code>compact: true</code> is the switch for agents: the compact contacts row was about a third the size of the full one in my run, with the same identity, role, five latest positions and contact fields.</p>
<p>The listing prices this Actor below the category leader on every Apify plan. That is a Store fact, not a promise from me; the pricing tab has the per-plan numbers.</p>
<h2>4. B2B Leads Finder: a filter box, people with a live email</h2>
<p><a href="https://apify.com/b2bsearch/b2b-leads-finder">B2B Leads Finder</a> · slug <code>b2b-leads-finder</code></p>
<p>The Apollo alternative in the family. You describe the people; every delivered lead has an email at their current employer or a personal one, plus the profile and social links. People who fail the test are not sold: they come back as free note rows with a count.</p>
<p><strong>Takes:</strong></p>
<ul>
<li><code>jobTitles</code>, <code>excludeJobTitles</code>, <code>titleMatch</code> (<code>substring</code> or <code>word</code>), <code>seniority</code></li>
<li><code>countries</code>, <code>countryGroups</code>, <code>cities</code>, <code>companyHqCountries</code></li>
<li><code>industries</code>, <code>companySizeMin</code> and <code>companySizeMax</code>, <code>keywords</code></li>
<li><code>companyDomains</code> for named accounts, <code>pastCompanyDomains</code> for alumni of a company</li>
<li><code>emailType</code>: <code>any</code>, <code>work</code> or <code>personal</code>; <code>includePhones</code></li>
<li><code>maxPerCompany</code>, <code>excludeDatasets</code>, <code>updatedAfter</code>, <code>apolloUrl</code> instead of all of the above</li>
</ul>
<p><strong>Returns:</strong> one row per lead.</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>What it holds</th>
<th>Why it helps</th>
</tr>
</thead>
<tbody><tr>
<td><code>fullName</code>, <code>firstName</code>, <code>lastName</code>, <code>jobTitle</code>, <code>headline</code></td>
<td>the person and what they say they do</td>
<td>merge fields and a sanity check</td>
</tr>
<tr>
<td><code>email</code>, <code>emailType</code>, <code>workEmail</code>, <code>personalEmail</code></td>
<td>the address and which kind it is</td>
<td>send to work, hold personal</td>
</tr>
<tr>
<td><code>phone</code>, <code>phones</code>, <code>phoneOnRecord</code></td>
<td>mobile numbers when asked for</td>
<td>the call list</td>
</tr>
<tr>
<td><code>companyName</code>, <code>companyDomain</code>, <code>companySize</code>, <code>companyIndustry</code></td>
<td>the employer</td>
<td>account-level routing</td>
</tr>
<tr>
<td><code>location</code>, <code>countryCode</code></td>
<td>where the person is</td>
<td>time zone and language</td>
</tr>
<tr>
<td><code>linkedinUrl</code>, <code>twitterUrl</code>, <code>facebookUrl</code></td>
<td>profiles</td>
<td>research links</td>
</tr>
<tr>
<td><code>updatedAt</code>, <code>_freshness</code></td>
<td>when the record was refreshed</td>
<td>refuse stale rows</td>
</tr>
</tbody></table>
<h3>Scenario A: marketing heads at mid-size software companies</h3>
<pre><code class="language-json">{
  "jobTitles": ["head of marketing", "vp marketing"],
  "countries": ["us"],
  "industries": ["Computer Software"],
  "companySizeMin": 51,
  "companySizeMax": 200,
  "emailType": "work",
  "maxPerCompany": 1,
  "maxResults": 10
}
</code></pre>
<table>
<thead>
<tr>
<th>Result</th>
<th>Count</th>
</tr>
</thead>
<tbody><tr>
<td>leads delivered</td>
<td>10</td>
</tr>
<tr>
<td>skipped free: no work address at the current employer</td>
<td>13</td>
</tr>
<tr>
<td>skipped free: had left the role the search matched</td>
<td>5</td>
</tr>
</tbody></table>
<p>About 20 seconds. The skipped rows are the feature: the Actor reads the profile, sees the position is closed, and says so in a note row instead of selling you a dead lead.</p>
<h3>Scenario B: alumni of a company you compete with</h3>
<p><code>pastCompanyDomains</code> finds people who used to work somewhere. Sales leaders in the DACH region who left a big CRM vendor:</p>
<pre><code class="language-json">{
  "jobTitles": ["sales director", "vp sales"],
  "countryGroups": ["dach"],
  "pastCompanyDomains": ["salesforce.com"],
  "emailType": "work",
  "maxResults": 5
}
</code></pre>
<p>Five leads in 19 seconds, 11 skipped for no work address and 11 more because they had already moved on. These are people who know the tool you replace, which is the warmest cold list I know how to build.</p>
<h3>Scenario C: leads with a mobile number</h3>
<pre><code class="language-json">{
  "jobTitles": ["vp sales", "chief revenue officer"],
  "countries": ["us"],
  "industries": ["Computer Software"],
  "companySizeMin": 51,
  "companySizeMax": 500,
  "emailType": "work",
  "includePhones": true,
  "maxPerCompany": 1,
  "maxResults": 10
}
</code></pre>
<p>Ten leads in 17 seconds, 4 of them with a mobile on record. The phone sits in the same row as the email, so there is no second lookup.</p>
<h3>Scenario D: the headline segment, sized first</h3>
<p>Before pulling I size the people box for free on <a href="https://apify.com/b2bsearch/people-database-search">People Search</a> with <code>mode: "count"</code>. Sales leaders at London fintechs of this size: 231 people. <code>mode: "market"</code> then shows the shape, seniority, employer size and the title spellings in use, also free. The pull itself, work addresses only and one lead per company:</p>
<table>
<thead>
<tr>
<th>Result</th>
<th>Count</th>
</tr>
</thead>
<tbody><tr>
<td>leads delivered</td>
<td>20</td>
</tr>
<tr>
<td>skipped free: no work address at the current employer</td>
<td>40</td>
</tr>
<tr>
<td>skipped free: had left the role the search matched</td>
<td>33</td>
</tr>
<tr>
<td>skipped free: over the one-per-company limit</td>
<td>1</td>
</tr>
</tbody></table>
<h3>Scenario E: only the new people next week</h3>
<p>Pass the dataset id of last week's run in <code>excludeDatasets</code> and rerun the same search. Everyone already delivered is left out and never charged again.</p>
<p>I wrote a whole post on the dead rows: <a href="https://dev.to/kevinhall/243-of-417-leads-in-a-typical-icp-box-were-dead-before-the-first-email-epo">243 of 417 leads in a typical ICP box were dead before the first email</a>. Built the search in Apollo? Paste its URL into <code>apolloUrl</code>; a Sales Navigator saved search has a twin that takes the URL: <a href="https://dev.to/kevinhall/sales-navigator-alternative-in-claude-code-one-saved-search-one-list-i">Sales Navigator alternative in Claude Code</a>.</p>
<h2>5. Email Finder: profile URLs to addresses on record, labelled</h2>
<p><a href="https://apify.com/b2bsearch/linkedin-email-finder">Email Finder</a> · slug <code>linkedin-email-finder</code></p>
<p>You hold profile URLs, you want addresses. It returns the addresses in the person's record, never a pattern guess.</p>
<p><strong>Takes:</strong></p>
<ul>
<li><code>profileUrls</code>: full URLs or bare handles, up to 1,000 per run</li>
<li><code>includePersonalEmails</code>: off by default</li>
</ul>
<p><strong>Returns:</strong> one row per profile.</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>What it holds</th>
<th>Why it helps</th>
</tr>
</thead>
<tbody><tr>
<td><code>email</code></td>
<td>the best address on record</td>
<td>the one to send to</td>
</tr>
<tr>
<td><code>emailType</code></td>
<td><code>work_current</code>, <code>work_other</code> or <code>personal</code></td>
<td>the label that keeps dead addresses out</td>
</tr>
<tr>
<td><code>workEmails</code>, <code>personalEmails</code>, <code>emailCount</code></td>
<td>every address on record</td>
<td>fallbacks</td>
</tr>
<tr>
<td><code>fullName</code>, <code>jobTitle</code>, <code>companyName</code>, <code>companySlug</code></td>
<td>who and where</td>
<td>merge fields</td>
</tr>
<tr>
<td><code>location</code>, <code>countryCode</code>, <code>_freshness</code></td>
<td>place and record age</td>
<td>filter</td>
</tr>
</tbody></table>
<p>Label meaning: <code>work_current</code> is an address on the current employer's domain, <code>work_other</code> is a company address it cannot tie to the current employer, <code>personal</code> shows up only when you switch the flag on. A <code>no_email</code> row is free and still carries the name, title and employer; a <code>not_found</code> row is free too.</p>
<h3>Scenario A: addresses for a list you already hold</h3>
<pre><code class="language-json">{
  "profileUrls": ["satyanadella", "jane-doe-example"],
  "includePersonalEmails": false
}
</code></pre>
<p>I fed it the 10 profile URLs from the marketing-leads pull above with personal mailboxes switched on. All 10 came back as <code>work_current</code> in 4 seconds. That is the point of starting from a leads search: the profile URL is a by-product of the pull.</p>
<h3>Scenario B: a roster with a hit rate</h3>
<p>The 20 roster rows from the Company Employees run went in: 13 came back with a company address, 7 as free rows, 2 of those with a note that a personal mailbox is on record. About 5 seconds.</p>
<p>The labels matter more than the hit rate. On a 200-URL list of marketing leaders earlier this week, about half of the addresses on record were at an older employer, and the label is what kept them out of the send list. That run is here: <a href="https://dev.to/kevinhall/bulk-email-finder-in-claude-code-200-profile-urls-64-still-current-n93">Bulk email finder in Claude Code: 200 profile URLs, 64 still current</a>.</p>
<h2>6. Phone Number Finder: verified mobile numbers, pay per found</h2>
<p><a href="https://apify.com/b2bsearch/linkedin-to-phone">Phone Number Finder</a> · slug <code>linkedin-to-phone</code></p>
<p>Direct dials, not switchboards. It is built to take whatever you already have and find the mobile.</p>
<p><strong>Takes:</strong> <code>profileUrls</code>, one entry per line, up to 20,000 per run. Each line can be a profile URL or handle, an email address, or a name plus domain like <code>Jane Smith, acme.com</code>.</p>
<p><strong>Returns:</strong> one row per entry.</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>What it holds</th>
<th>Why it helps</th>
</tr>
</thead>
<tbody><tr>
<td><code>primaryPhone</code></td>
<td>the best verified mobile</td>
<td>dial this one</td>
</tr>
<tr>
<td><code>phones</code>, <code>phoneCount</code></td>
<td>every number on record</td>
<td>fallbacks</td>
</tr>
<tr>
<td><code>fullName</code>, <code>jobTitle</code>, <code>company</code></td>
<td>who it belongs to</td>
<td>check before dialling</td>
</tr>
<tr>
<td><code>location</code>, <code>country</code>, <code>linkedinUrl</code></td>
<td>place and the matched profile</td>
<td>join back to your list</td>
</tr>
</tbody></table>
<p>A miss is a free <code>no_phone</code> row. It is US-centric, by the listing's own words and in my runs.</p>
<h3>Scenario A: a mobile for each US lead</h3>
<p>I sent the 10 US marketing leads from section 4 by profile URL:</p>
<pre><code class="language-json">{ "profileUrls": ["https://www.linkedin.com/in/&lt;handle&gt;", "&lt;handle&gt;"] }
</code></pre>
<p>Three came back with a mobile, seven as free <code>no_phone</code> rows, in 6 seconds.</p>
<h3>Scenario B: the same people, by email</h3>
<p>Same 10 people, but the input is their work addresses, one per line. Same result: 3 numbers, 7 free rows. This is the version for a CRM export that has addresses and no profile links.</p>
<h3>Scenario C: names and domains only</h3>
<pre><code class="language-json">{ "profileUrls": ["Jane Smith, acme.com", "Raj Patel, example.org"] }
</code></pre>
<p>Use it when all you have is a name and a company. I did not run this form today; it is in the listing's input description.</p>
<p>Earlier this week a hundred US sales leaders gave a mobile for 35 of them, chief revenue officers about half, directors one in four. My London roster gave almost nothing, and the misses cost nothing. Run it on US rows. The full call-list recipe is in <a href="https://dev.to/kevinhall/lusha-and-zoominfo-alternative-in-claude-code-a-call-list-four-ways-1mod">Lusha and ZoomInfo alternative in Claude Code</a>.</p>
<h2>7. Email to Profile Finder: an address in, the person out</h2>
<p><a href="https://apify.com/b2bsearch/email-to-linkedin">Email to Profile Finder</a> · slug <code>email-to-linkedin</code></p>
<p>The inbound door. It turns email addresses, work or personal, into the person.</p>
<p><strong>Takes:</strong> <code>emails</code>, one address per line, up to 1,000 per run. Put a name beside a work address to find more: <code>Jane Doe &lt;jane@acme.com&gt;</code> or <code>jane@acme.com, Jane Doe</code>.</p>
<p><strong>Returns:</strong> one row per address.</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>What it holds</th>
<th>Why it helps</th>
</tr>
</thead>
<tbody><tr>
<td><code>fullName</code>, <code>headline</code>, <code>jobTitle</code></td>
<td>who they are and what they do</td>
<td>qualify the sign-up</td>
</tr>
<tr>
<td><code>company</code>, <code>companyLinkedinUrl</code></td>
<td>the employer and its page</td>
<td>feeds Company Enrichment by Domain</td>
</tr>
<tr>
<td><code>linkedinUrl</code></td>
<td>the matched profile</td>
<td>feeds Email Finder and Phone Number Finder</td>
</tr>
<tr>
<td><code>location</code>, <code>country</code></td>
<td>place</td>
<td>routing</td>
</tr>
<tr>
<td><code>matchedEmail</code>, <code>matchedVia</code></td>
<td>which address matched and how</td>
<td>audit trail</td>
</tr>
</tbody></table>
<p>Addresses it cannot match, invalid ones and duplicates come back as free rows that say why.</p>
<h3>Scenario A: a column of work addresses</h3>
<pre><code class="language-json">{ "emails": ["jane@acme.com", "Jane Doe &lt;jane@acme.com&gt;"] }
</code></pre>
<p>Ten work addresses from the leads pull went in and 10 people came out, every one <code>matchedVia: as_given</code>, in 6 seconds.</p>
<h3>Scenario B: close the loop</h3>
<p>The 13 addresses the Email Finder returned for the London roster went back in. 13 people came out, and all 13 were the profiles the roster started from. About 6 seconds.</p>
<h3>Scenario C: a form fill becomes an account</h3>
<p>A visitor leaves an address. Run it here, take <code>companyLinkedinUrl</code> or the domain into Company Enrichment by Domain, then Company Employees for the decision makers next to them. That is the inbound chain in <a href="https://dev.to/kevinhall/inbound-lead-qualification-from-the-email-domain-company-record-decision-makers-and-the-gmail-1p5d">inbound lead qualification from the email domain</a>.</p>
<h2>How they chain</h2>
<table>
<thead>
<tr>
<th>You start with</th>
<th>Chain</th>
<th>You end with</th>
</tr>
</thead>
<tbody><tr>
<td>a market idea</td>
<td>Company Search count, then rows, then Company Employees</td>
<td>named people at named accounts</td>
</tr>
<tr>
<td>a filter box</td>
<td>B2B Leads Finder with <code>includePhones</code></td>
<td>email plus mobile in one row</td>
</tr>
<tr>
<td>a list of profile URLs</td>
<td>Email Finder, then Phone Number Finder on the misses</td>
<td>the best reachable channel per person</td>
</tr>
<tr>
<td>a sign-up list</td>
<td>Email to Profile Finder, then Company Enrichment by Domain</td>
<td>the account behind each address</td>
</tr>
<tr>
<td>last month's list</td>
<td>B2B Leads Finder with <code>excludeDatasets</code></td>
<td>only the new people</td>
</tr>
</tbody></table>
<h2>What does an agent pay to load all seven?</h2>
<p>An MCP tool costs tokens before it is called: the description, the input schema and the output schema the server infers from rows. I measured <code>tools/list</code> on the hosted Apify MCP server with the seven pinned, tiktoken o200k on compact JSON.</p>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Tokens</th>
</tr>
</thead>
<tbody><tr>
<td>Company Employees</td>
<td>4,191</td>
</tr>
<tr>
<td>B2B Leads Finder</td>
<td>3,990</td>
</tr>
<tr>
<td>Company Search</td>
<td>3,344</td>
</tr>
<tr>
<td>Email Finder</td>
<td>2,016</td>
</tr>
<tr>
<td>Phone Number Finder</td>
<td>1,961</td>
</tr>
<tr>
<td>Email to Profile Finder</td>
<td>1,955</td>
</tr>
<tr>
<td>Company Enrichment by Domain</td>
<td>1,953</td>
</tr>
<tr>
<td>four helper tools (run, abort, dataset, key-value store)</td>
<td>3,812</td>
</tr>
<tr>
<td>total</td>
<td>23,222</td>
</tr>
</tbody></table>
<p>For scale: two days ago I measured 20 job Actors the same way and got a median of 3,829 per tool, with one at 32,737. The seven here sit at a median of 2,016.</p>
<p>The descriptions are 80 to 97 tokens each; the weight is in the input schemas of the search Actors and in the inferred output schema of Company Employees. Flat rows keep that schema small, and <code>compact: true</code> keeps the rows small after the call.</p>
<p>Method and the job numbers: <a href="https://dev.to/kevinhall/mcp-tool-cost-20-job-actors-on-apify-21k-to-327k-tokens-each-1hm4">MCP tool cost: 20 job Actors on Apify</a>.</p>
<h2>Wiring: Claude Code, MCP, n8n, REST</h2>
<p>Pin the Actors in the server URL so an agent loads only these definitions:</p>
<pre><code class="language-plaintext">claude mcp add --transport http apify "https://mcp.apify.com?tools=b2bsearch/company-database-search,b2bsearch/domain-to-company,b2bsearch/company-employees,b2bsearch/b2b-leads-finder,b2bsearch/linkedin-email-finder,b2bsearch/linkedin-to-phone,b2bsearch/email-to-linkedin" --header "Authorization: Bearer &lt;APIFY_TOKEN&gt;"
</code></pre>
<p>The same server in any MCP client (Cursor, Windsurf, Claude Desktop, ChatGPT, LangChain, CrewAI, the OpenAI Agents SDK):</p>
<pre><code class="language-json">{
  "mcpServers": {
    "apify": {
      "type": "http",
      "url": "https://mcp.apify.com?tools=b2bsearch/company-database-search,b2bsearch/domain-to-company,b2bsearch/company-employees,b2bsearch/b2b-leads-finder,b2bsearch/linkedin-email-finder,b2bsearch/linkedin-to-phone,b2bsearch/email-to-linkedin",
      "headers": { "Authorization": "Bearer &lt;APIFY_TOKEN&gt;" }
    }
  }
}
</code></pre>
<p>Tool names use a double hyphen: <code>b2bsearch--company-employees</code>, <code>b2bsearch--b2b-leads-finder</code>, and so on. Cap a run with <code>callOptions.maxTotalChargeUsd</code>, read rows with <code>get-dataset-items</code> and a <code>fields</code> list, and run the free count before the paid pull.</p>
<p>Without an agent: every Actor is a REST endpoint. One call, rows back in the response:</p>
<pre><code class="language-plaintext">curl -X POST "https://api.apify.com/v2/acts/b2bsearch~domain-to-company/run-sync-get-dataset-items?token=&lt;APIFY_TOKEN&gt;" \
  -H "Content-Type: application/json" \
  -d '{"domains": ["acme.com", "example.org"]}'
</code></pre>
<p>The same Actors are steps in n8n, Make and Zapier through the Apify integrations, and the Store page of each one has the input schema, the output example and the per-plan price.</p>
<h2>What the agent did on its own</h2>
<p>I gave a Claude Code session the seven tools pinned and a brief: size the segment, pull ten companies, get the leadership of the two largest with a current work email, fall back to the email finder where that fails, cap every paid run, report a ledger. Shell and file tools were denied. Eleven turns, about four minutes, four Actor runs.</p>
<p>What it did without being told:</p>
<ul>
<li>loaded all seven tool definitions first, then wrote a cost table from the descriptions before the first paid call, naming the tools it would not use</li>
<li>added <code>hasFunding: true</code> and <code>sort: "employee_count_desc"</code> to the company search; its free count matched mine, 79</li>
<li>read rows with a <code>fields</code> list that left out <code>description</code>, the longest field on a company card</li>
<li>added <code>titleExcludes</code> for assistants, "office of" and chiefs of staff to the leadership pull, after reading the field description that warns the C-level bucket catches them</li>
<li>noticed that <code>callOptions</code> is in no Actor's schema and passed the spend cap anyway, in the call options of the MCP server</li>
</ul>
<p>Where it stopped. It asked Company Employees for leaders with an address at the current employer, exactly as briefed, and both runs came back as one free note row each: the leaders on record did not carry one, so nothing was delivered and nothing charged. With no profile URLs in hand it skipped the email finder, handed over the account list with a trust column (two rows marked questionable, one not fintech by its industry, one to check for HQ) and stopped.</p>
<p>That is the behaviour I want from an agent spending money. It is also a brief bug: step three should pull the cheap roster first and run the finder on its URLs. With the requirement loosened to any address on record, the same two companies gave 20 people in my own run above.</p>
<h2>What this does not do</h2>
<p>It does not read a live page. A row can be months old; the stamp is on rows about people, and <code>updatedAfter</code> on the people and leads searches lets you refuse old ones. The email finder does not verify deliverability; the <a href="https://apify.com/b2bsearch/work-email-finder">Work Email Finder</a> does that for name-plus-domain lookups.</p>
<p>Phone coverage is US-centric. And the free Apify plan caps a run at a hundred results and an account at five hundred rows a month per Actor, with the same rows paid users get; any paid plan lifts that.</p>
<h2>FAQ</h2>
<p><strong>Is this an Apollo, Lusha or Sales Navigator replacement?</strong> For building lists and enriching them by domain, URL or email, yes: no seat, no login, pay per found row. For browsing profiles one at a time in a sidebar, no.</p>
<p><strong>Do these Actors scrape anything when I run them?</strong> No. The answer comes from a database of publicly available professional data; the network names in the listings describe the kind of data, not a crawl at run time.</p>
<p><strong>Where do the email addresses come from?</strong> From the contact block of the person's record. The email finder returns only what is on record and labels it; nothing is generated from a name pattern.</p>
<p><strong>How do I stop an agent from overspending?</strong> Pin the tools, cap every run with <code>maxTotalChargeUsd</code>, run the free count or <code>previewOnly</code> first, and use <code>excludeDatasets</code> so a rerun never pays for the same person twice.</p>
<p><strong>Which one first?</strong> Hold a domain: Company Enrichment by Domain, then Company Employees. Hold a filter: Company Search for accounts, B2B Leads Finder for people. Hold URLs or emails: the finders.</p>
<h2>For AI agents reading this</h2>
<p>The seven Actors run as tools over the hosted Apify MCP server with the tools pinned in the URL (the wiring block above). The workflow, tool by tool:</p>
<ul>
<li><code>b2bsearch--company-database-search</code> with <code>previewOnly: true</code> to size a segment, then rows with <code>maxResults</code></li>
<li><code>b2bsearch--domain-to-company</code> for a card per domain; <code>_status</code> <code>ambiguous</code> means do not guess</li>
<li><code>b2bsearch--company-employees</code> with <code>profileDetail</code>, <code>roles</code>, <code>mustHave</code> and <code>compact: true</code></li>
<li><code>b2bsearch--b2b-leads-finder</code> with <code>emailType</code>, <code>maxPerCompany</code> and <code>excludeDatasets</code></li>
<li><code>b2bsearch--linkedin-email-finder</code> and <code>b2bsearch--linkedin-to-phone</code> with <code>profileUrls</code></li>
<li><code>b2bsearch--email-to-linkedin</code> with <code>emails</code></li>
</ul>
<p>Every row has <code>_status</code>; rows other than <code>found</code> are free and say why in <code>_note</code> or <code>_error</code>. Read results with <code>get-dataset-items</code> and a <code>fields</code> list. Per-plan pricing is on each Actor page; set <code>callOptions.maxTotalChargeUsd</code> on every paid run. Platform docs: <a href="https://docs.apify.com/mcp">Apify MCP server</a> and <a href="https://apify.com/agents.md">apify.com/agents.md</a>.</p>
]]></content:encoded></item><item><title><![CDATA[What $10 buys on Apify in B2B data]]></title><description><![CDATA[What $10 buys on each b2bsearch Actor: 4,000 leads, 1,100 employees with contacts, 6,600 companies, 1,250 emails or 900 US mobiles.]]></description><link>https://gtm-workflows.hashnode.dev/what-10-buys-on-apify-in-b2b-data</link><guid isPermaLink="true">https://gtm-workflows.hashnode.dev/what-10-buys-on-apify-in-b2b-data</guid><category><![CDATA[lead generation]]></category><category><![CDATA[sales]]></category><category><![CDATA[api]]></category><category><![CDATA[mcp]]></category><dc:creator><![CDATA[Kevin Hall]]></dc:creator><pubDate>Sat, 31 Oct 2026 13:53:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6ac951f8c58437c7a9101e18/1afc1a30-dda4-4f2b-b6c8-80862721719b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Apify's free plan comes with five dollars of usage a month and no card; the Starter plan folds nineteen into its fee. Most people spend that on a scraper: minutes of compute, a login, a proxy, a few hundred rows if nothing breaks. Spent on pay-per-result Actors it buys something else: rows, and only rows that answer the question.</p>
<p>This is the map of what $10 buys in B2B data with the b2bsearch Actors, and then, for each one, what you send, what comes back, why the columns matter and which Actor to run next.</p>
<p>The quantities are Starter-plan prices rounded down; on the free plan the same ten dollars is two months of credit and buys about as much, a little less on the leads and phone finders. No hit rates and no benchmarks today; those are in the posts linked along the way.</p>
<h2>In one paragraph</h2>
<p><a href="https://apify.com/b2bsearch">b2bsearch on Apify</a> is a family of pay-per-row Actors over one database: 1B+ professional profiles, 115M companies, 50M+ job postings.</p>
<p>A filter, a list of domains, profile URLs, emails or a CSV goes in; a flat table comes out, with emails and phones where they are on record. Misses, counts and repeats are free. No cookies, no login, no proxies, and every Actor is an MCP tool for Claude Code and other agents.</p>
<h2>What $10 buys</h2>
<p><a href="https://apify.com/b2bsearch/b2b-leads-finder">B2B Leads Finder</a><strong>4,000 leads</strong>, each with a live email</p>
<p><a href="https://apify.com/b2bsearch/company-employees">Company Employees</a><strong>1,100+ employees</strong> of your target accounts, with emails and phones</p>
<p><a href="https://apify.com/b2bsearch/domain-to-decision-makers">Decision Maker Email Finder by Domain</a><strong>3,100 founders</strong>, C-level and VPs with their emails</p>
<p><a href="https://apify.com/b2bsearch/company-database-search">Company Search</a><strong>6,600 companies</strong> by industry, headcount, HQ and last round</p>
<p><a href="https://apify.com/b2bsearch/domain-to-company">Company Enrichment by Domain</a><strong>1,800 company cards</strong> for a list of domains</p>
<p><a href="https://apify.com/b2bsearch/people-database-search">People Search</a><strong>5,000 people rows</strong> from 35+ filters, count and market shape free</p>
<p><a href="https://apify.com/b2bsearch/linkedin-email-finder">Email Finder</a><strong>1,250 emails</strong> at the current employer for profile URLs you hold</p>
<p><a href="https://apify.com/b2bsearch/linkedin-to-phone">Phone Number Finder</a><strong>900 verified US mobile numbers</strong></p>
<p><a href="https://apify.com/b2bsearch/profile-lookup">Profile Lookup</a> and <a href="https://apify.com/b2bsearch/bulk-people-enrichment">Bulk People Enrichment</a><strong>3,100 full career records</strong>, or rows of a CSV resolved by any key</p>
<p><a href="https://apify.com/b2bsearch/reverse-email-lookup">Reverse Email Lookup</a><strong>3,100 people</strong> behind email addresses</p>
<p><a href="https://apify.com/b2bsearch/hiring-companies-finder">Companies Hiring Now</a><strong>4,100 employers</strong> hiring for a role, with their hiring pace</p>
<p><a href="https://apify.com/b2bsearch/linkedin-jobs-scraper">Jobs Scraper</a> and <a href="https://apify.com/b2bsearch/linkedin-job-search-api">Jobs Database API</a><strong>12,500 job postings</strong> with the company card, or <strong>6,200</strong> with the named recruiter and the employer's pace</p>
<p>Misses are free rows on every one of them, so the budget goes to answers, not attempts.</p>
<h2>How to make the credit go far</h2>
<p>Five habits, all of them free:</p>
<ul>
<li><strong>Count before you buy.</strong> Company Search and the jobs Actors have <code>previewOnly</code>; People Search has <code>mode: "count"</code> and <code>mode: "market"</code>. A count tells you the size of a segment, a market run tells you its shape, and neither charges a row.</li>
<li><strong>Let the misses be free.</strong> A person with no email, a domain nobody claims, a namesake with five holders: each comes back as a row with a <code>_status</code> and a note, and no charge. Read the notes; they are the autopsy of your list.</li>
<li><strong>Say what a row must have.</strong> <code>mustHave</code> (<code>currentWorkEmail</code>, <code>personalEmail</code>, <code>phone</code>, <code>github</code> and so on) means a row without it is skipped free and counted in one note row, instead of delivered and paid for.</li>
<li><strong>Never pay for the same person twice.</strong> <code>excludeDatasets</code> on the search Actors takes the datasets of earlier runs and leaves everyone in them out. A weekly rerun costs only the new people.</li>
<li><strong>Cap the run.</strong> <code>maxResults</code>, <code>maxRows</code>, <code>maxPerCompany</code>, and <code>callOptions.maxTotalChargeUsd</code> when an agent is doing the calling.</li>
</ul>
<p>The free plan returns up to a hundred results per run, with a monthly allowance per Actor; counts and previews sit outside it, and any paid plan lifts it. The rows themselves are the same on every plan.</p>
<h2>Start from a filter: you have an ICP, not a list</h2>
<h3>B2B Leads Finder</h3>
<p><a href="https://apify.com/b2bsearch/b2b-leads-finder">B2B Leads Finder</a> is the one to open when the brief reads "VPs of sales at US software companies with 50 to 500 people", and the Apollo alternative in the family.</p>
<p>Job titles, seniority buckets, countries or regions (<code>dach</code>, <code>nordics</code>, <code>latam</code>), cities, company size, industries, headline keywords, account domains, past employers, a refresh date. Or paste an Apollo people-search URL and let the Actor read the filters out of it.</p>
<pre><code class="language-json">{
  "jobTitles": ["vp sales", "head of sales"],
  "countries": ["us"],
  "companySizeMin": 50, "companySizeMax": 500,
  "industries": ["Software Development"],
  "emailType": "work",
  "maxPerCompany": 2,
  "maxResults": 200
}
</code></pre>
<p>Every row is a person with a live email: name, title, headline, the address and whether it is <code>work</code> or <code>personal</code>, both when both exist, company name, domain, size and industry, location, the profile URL, X, GitHub and Facebook links where on record, a <code>phoneOnRecord</code> flag (switch on <code>includePhones</code> for the number itself), a stable <code>personId</code> and a <code>_freshness</code> bucket. People without an email are not delivered and not charged; the note rows say how many, and why.</p>
<p>That is a send-ready list, and the reason the family exists. Next: <code>excludeDatasets</code> for next week's delta, <a href="https://apify.com/b2bsearch/profile-lookup">Profile Lookup</a> when you want the whole career behind a lead, <a href="https://apify.com/b2bsearch/linkedin-to-phone">Phone Number Finder</a> for the call list. The deep dive: <a href="https://dev.to/kevinhall/apollo-alternative-in-claude-code-7-lead-lists-from-one-apify-actor-4olo">Apollo alternative in Claude Code: 7 lead lists from one Apify Actor</a>.</p>
<h3>People Search</h3>
<p><a href="https://apify.com/b2bsearch/people-database-search">People Search</a> is the same database with every filter exposed, 35+ of them. Title and headline keywords with AND lists, seniority, employer domains, industries and headcount, employer HQ country, past employer and the year they left, past titles, career start year, tenure, city, languages, connection count, people between jobs, records refreshed after a date.</p>
<p>Three modes. <code>count</code> answers with one number. <code>market</code> answers with the shape, countries, seniority, employer size, industries, top titles, top employers, how many have a personal or a work address, one row per bucket; <code>segments</code> compares up to ten variants in one run. <code>people</code> returns the rows at the tier you choose: <code>row</code> (name, title, employer, location, email flags), <code>profile</code> (the career record) or <code>contacts</code> (plus emails and phones), with <code>compact</code> for agents.</p>
<p>This is where the signals live: who left a competitor since last year (<code>pastEmployerDomains</code> plus <code>leftPastEmployerAfterYear</code>), who started a role this quarter (<code>currentRoleStartedAfterDate</code>), fractional execs by headline. Two worked examples: <a href="https://dev.to/kevinhall/the-warmest-cold-list-1803-people-who-left-hubspot-since-2025-23lp">the warmest cold list, people who left one company since 2025</a> and <a href="https://dev.to/kevinhall/5860-fractional-cfos-by-headline-2631-by-title-the-gap-is-the-list-3p88">fractional CFOs by headline</a>.</p>
<h3>Company Search</h3>
<p><a href="https://apify.com/b2bsearch/company-database-search">Company Search</a> builds the account list: countries or groups, industries (a sector covers everything under it), HQ city or state, founding year, headcount band, and funding: <code>hasFunding</code>, last round after or before a date, the stage of the last round, the amount. <code>previewOnly: true</code> first.</p>
<pre><code class="language-json">{
  "countries": ["gb"],
  "industries": ["Financial Services"],
  "hqLocations": ["London"],
  "employeesMin": 51, "employeesMax": 200,
  "fundedAfter": "2024-01-01",
  "previewOnly": true
}
</code></pre>
<p>A company row: name, primary domain, website, company page URL, headcount, industry with its taxonomy id, last round type, date and amount, founding year, description, country and HQ. Unknown facts are left out, never shipped as empty strings; a company the store holds twice comes back once, the duplicate as a free row.</p>
<p>The domain column is the handle for everything below: paste it into Company Employees, Decision Makers or Lookalike Company Finder.</p>
<h3>Companies Hiring Now</h3>
<p><a href="https://apify.com/b2bsearch/hiring-companies-finder">Companies Hiring Now</a> turns a job search into the employers behind it. Job titles as boolean lines (<code>"account executive" OR "sales development"</code>), countries, states or cities, how far back, experience level, employment type, industries, agencies out. Turn on <code>decisionMakers</code> with roles, a per-company cap and title keywords, and each employer comes with the people who own the budget.</p>
<p>One row per employer: the company card (domain, industry, headcount, country, HQ, funding), how many postings matched and how many are still open, first and last posting dates, open postings in total, postings in the last 7, 30 and 90 days, the seniority mix and the most posted titles of the last month.</p>
<p>Then one row per decision maker: name, title, seniority, profile URL, freshness.</p>
<p>A company hiring three account executives this month has a sales budget and a sales leader under pressure. That is a better trigger than any firmographic. Next: Company Employees for the rest of the team, Email Finder for the addresses.</p>
<h3>Jobs Database API</h3>
<p><a href="https://apify.com/b2bsearch/linkedin-job-search-api">Jobs Database API</a> is the posting-level search over 50M+ job postings from the last 180 days. Boolean title and description search with exclusions, location, employer size and industry, agencies out.</p>
<p>Then seniority, employment type, salary stated or above a floor, an applicant-count ceiling, posted after a date, open or expired, and the people behind the job: the named recruiter, the employer's hiring pace, the employer's decision makers. <code>previewOnly</code> gives the market breakdown free; <code>compact</code> keeps rows short.</p>
<p>A job row: title, URL, posted date, the company card, location, seniority, employment type, salary text and parsed range, applicant range, recruiter name and profile URL, the employer's open postings and 7, 30 and 90-day pace with its top titles, whether the posting is still open and until when, the description as text or markdown.</p>
<p>Recruiters use it for market maps, sales teams for "who just posted for the role my product replaces", developers for a job search without the duplicates. Two posts came out of it: <a href="https://dev.to/kevinhall/job-search-in-claude-code-half-of-us-backend-postings-were-copies-1f9j">half of US backend postings were copies</a> and <a href="https://dev.to/kevinhall/ghost-jobs-measured-58-of-uk-backend-postings-came-from-middlemen-nhl">ghost jobs, measured</a>.</p>
<h2>Start from domains: you have the accounts</h2>
<h3>Company Enrichment by Domain</h3>
<p><a href="https://apify.com/b2bsearch/domain-to-company">Company Enrichment by Domain</a> takes a column of websites in any form (<code>acme.com</code>, a URL with a path, <code>www</code>) and returns one card per domain: company name, company page URL, website, industry, headcount and range, country, HQ city and region, founding year, description, last funding round with date and amount.</p>
<pre><code class="language-json">{ "domains": ["acme-robotics.com", "https://www.northwind-logistics.co.uk/about"] }
</code></pre>
<p>Three statuses. <code>found</code> is the card. <code>not_found</code> means no company lists this domain as its site, free. <code>ambiguous</code> means several unrelated pages claim it, or it is a shared host like <code>github.io</code>, also free, and never a guess. Use it to qualify inbound (a form fill with a company domain becomes a size, an industry and a funding stage in one call), to clean a CRM, or to feed the two Actors below.</p>
<h3>Decision Maker Email Finder by Domain</h3>
<p><a href="https://apify.com/b2bsearch/domain-to-decision-makers">Decision Maker Email Finder by Domain</a> answers the question every account list ends with: who runs this place. Domains in, up to five hundred per run, <code>roles</code> to pick from founders, C-level, VPs and directors, <code>maxPerCompany</code> in rank order, <code>titleKeywords</code> to keep only marketing, finance or engineering.</p>
<p>A row per person: rank, company name and headcount, full name, job title, seniority bucket, headline, profile URL, location, in-role-since year, email flags, and <code>email</code> with its type: a work address on the company's own domain when one reaches the person today, else a personal one, at no extra charge. A domain with nobody on record comes back free, with a hint about brand domains.</p>
<p>This is the ten-accounts-by-Friday Actor. Next: Phone Number Finder on the profile URLs for the call list, Company Employees when you need the layer below the leadership.</p>
<h3>Company Employees</h3>
<p><a href="https://apify.com/b2bsearch/company-employees">Company Employees</a> is the org map. Domains in, and <code>profileDetail</code> decides the depth: <code>roster</code> for the staff list (name, title, seniority, headline, city, email flags, profile URL, freshness stamps), <code>profile</code> for the career record of each person, <code>contacts</code> for emails, phones and social links. <code>roles</code> keeps seniority buckets, <code>titleContains</code> and <code>titleExcludes</code> pick departments, <code>mustHave</code> keeps only people you can reach, <code>excludeLowSignal</code> drops self-declared profiles, <code>compact</code> keeps rows short for agents.</p>
<pre><code class="language-json">{
  "companies": ["acme.com"],
  "roles": ["vp", "director"],
  "titleContains": ["sales", "revenue"],
  "profileDetail": "contacts",
  "mustHave": ["email"],
  "compact": true
}
</code></pre>
<p>Why the roster first: it is the cheapest tier and it carries <code>hasWorkEmail</code> and <code>hasPersonalEmail</code> per person, so you can pick the twelve people who matter and send only those to the contacts tier or to the Email Finder.</p>
<p>A repeat of an employee already delivered never charges, so re-running an account next month costs only the new hires. Full walk-through: <a href="https://dev.to/kevinhall/account-mapping-by-company-domain-leaders-departments-and-work-emails-for-8-target-accounts-from-3agj">account mapping by company domain</a>.</p>
<h2>Start from people you already have</h2>
<h3>Email Finder</h3>
<p><a href="https://apify.com/b2bsearch/linkedin-email-finder">Email Finder</a> takes profile URLs or bare handles, up to a thousand per run, and returns the addresses on record, never a pattern guess.</p>
<p>Each row is labelled: <code>work_current</code> for the current employer's domain, <code>work_other</code> for an older company address, <code>personal</code> when <code>includePersonalEmails</code> is on, with the whole <code>workEmails</code> list and a count. A profile with nothing on record is a free <code>no_email</code> row that still names the person and says when a personal mailbox exists.</p>
<p>The labels are the product. A list of two hundred URLs becomes three piles, send now, verify first, skip, in one run: <a href="https://dev.to/kevinhall/bulk-email-finder-in-claude-code-200-profile-urls-64-still-current-n93">bulk email finder in Claude Code</a>. For the "verify first" pile, <a href="https://apify.com/b2bsearch/work-email-finder">Work Email Finder</a> takes name plus domain, ranks candidates and checks the best one against the mail domain.</p>
<h3>Phone Number Finder</h3>
<p><a href="https://apify.com/b2bsearch/linkedin-to-phone">Phone Number Finder</a> accepts a profile URL, an email, or "Jane Smith, <a href="http://acme.com">acme.com</a>" per line, up to 20,000 a run, and returns verified mobile numbers: direct dials, never a switchboard, with name, title, company, location and a <code>_freshness</code> bucket.</p>
<p>A known profile without a number is a free <code>no_phone</code> row. It is US-centric by design, so run it on US rows and expect little elsewhere. The call-list recipes: <a href="https://dev.to/kevinhall/lusha-and-zoominfo-alternative-in-claude-code-a-call-list-four-ways-1mod">Lusha and ZoomInfo alternative in Claude Code</a>.</p>
<h3>Profile Lookup and Bulk People Enrichment</h3>
<p><a href="https://apify.com/b2bsearch/profile-lookup">Profile Lookup</a> is the full record for a list of profile URLs: headline, current title and company, location, industry, connection count, tenure and total experience in years, every position with dates and descriptions, education, skills, languages, certifications, patents and publications when the profile has them, <code>updatedAt</code> for the record's age, and with <code>contacts</code> on, the emails and phones.</p>
<p>It also reads the input names other profile scrapers use (<code>urls</code>, <code>publicIdentifiers</code>), so an existing integration only changes the Actor id.</p>
<p><a href="https://apify.com/b2bsearch/bulk-people-enrichment">Bulk People Enrichment</a> is the same record for a CSV of up to 50,000 rows, each resolved by the strongest key it carries: email, profile URL, GitHub, X or Facebook handle, or a full name with a company domain. Namesakes come back as a free <code>ambiguous</code> row with the holder count, never a guess, and rows stream into the dataset as they resolve.</p>
<p>This is the fifty-columns pair, and the reason to pull the full record even when you only need a name: once the table is on your machine you can filter by anything in it. More on that below.</p>
<h3>From an email, a name or a handle</h3>
<p><a href="https://apify.com/b2bsearch/reverse-email-lookup">Reverse Email Lookup</a> turns any email into the person's full career row.</p>
<p><a href="https://apify.com/b2bsearch/email-to-linkedin">Email to Profile Finder</a> is the lighter version: profile URL, name, headline, title, company and location, with a name beside a work address to find more. <a href="https://apify.com/b2bsearch/name-to-profile">Name to Profile</a> resolves "Jane Smith, <a href="http://acme.com">acme.com</a>" to the profile and career.</p>
<p><a href="https://apify.com/b2bsearch/social-handle-lookup">Social Handle Lookup</a> does the same from a GitHub, X or Facebook handle. All four: an invalid or unmatched entry is a free row that says so.</p>
<h2>The rest of the family, one line each</h2>
<table>
<thead>
<tr>
<th>You hold</th>
<th>Actor</th>
<th>You get</th>
</tr>
</thead>
<tbody><tr>
<td>a Sales Navigator search URL</td>
<td><a href="https://apify.com/b2bsearch/sales-navigator-search">Sales Navigator Search</a></td>
<td>the people the search describes, count first, no cookies or seat</td>
</tr>
<tr>
<td>a filter box and calls to make</td>
<td><a href="https://apify.com/b2bsearch/b2b-leads-with-phones">Leads with Phones</a></td>
<td>leads where every row has a verified mobile and a live email, the Lusha alternative</td>
</tr>
<tr>
<td>a competitor's domain</td>
<td><a href="https://apify.com/b2bsearch/former-employees-finder">Former Employees Finder</a></td>
<td>alumni, when they left, where they are now</td>
</tr>
<tr>
<td>your target accounts</td>
<td><a href="https://apify.com/b2bsearch/new-hires-finder">New Hires Finder</a></td>
<td>who joined in the last months, with title, start month and seniority</td>
</tr>
<tr>
<td>three good customers</td>
<td><a href="https://apify.com/b2bsearch/lookalike-company-finder">Lookalike Company Finder</a></td>
<td>similar companies by industry, country and size, each row with its match reason</td>
</tr>
<tr>
<td>a domain and no name</td>
<td><a href="https://apify.com/b2bsearch/company-email-format-finder">Company Email Format Finder</a></td>
<td>the real address pattern, from addresses on record</td>
</tr>
<tr>
<td>a messy CSV</td>
<td><a href="https://apify.com/b2bsearch/waterfall-lead-enrichment">Waterfall Lead Enrichment</a></td>
<td>whatever key each row carries, resolved to a profile with contacts, the Clay alternative</td>
</tr>
<tr>
<td>a job search</td>
<td><a href="https://apify.com/b2bsearch/linkedin-jobs-scraper">Jobs Scraper</a></td>
<td>postings in bulk with the full description and the company card</td>
</tr>
<tr>
<td>personal emails</td>
<td><a href="https://apify.com/b2bsearch/email-to-company">Email to Company</a></td>
<td>the employer and title behind a gmail address</td>
</tr>
<tr>
<td>emails or handles</td>
<td><a href="https://apify.com/b2bsearch/email-to-phone">Email to Phone</a>, <a href="https://apify.com/b2bsearch/name-to-phone">Name to Phone</a>, the <a href="https://apify.com/b2bsearch/facebook-to-email">Facebook</a> and <a href="https://apify.com/b2bsearch/twitter-to-email">X</a> doors</td>
<td>the same person, from the key you have</td>
</tr>
</tbody></table>
<h2>Work the table on your machine</h2>
<p>Every run writes a dataset, and a dataset exports as JSON, CSV or XLSX from the Console or from one URL:</p>
<pre><code class="language-plaintext">https://api.apify.com/v2/datasets/&lt;DATASET_ID&gt;/items?format=csv&amp;clean=true&amp;token=&lt;APIFY_TOKEN&gt;
</code></pre>
<p>Pull the profile tiers and you have fifty-plus columns per person: every position with dates, the employer card behind each one, education, skills, languages, email flags, freshness. That is where the "exactly the leads you need" part happens, after the pull and for free: people who were at a competitor before this job, who started this year, whose record was refreshed since spring, who list a language your rep speaks. One query over the CSV in DuckDB, a filter in Sheets, a <code>WHERE</code> in pandas.</p>
<pre><code class="language-sql">SELECT fullName, jobTitle, companyName, email
FROM 'leads.csv'
WHERE emailType = 'work' AND _freshness IN ('fresh_90d', 'updated_1y')
</code></pre>
<p>Join on <code>companyDomain</code> to the company cards, dedupe on <code>personId</code>, and feed the survivors to the next Actor by their profile URL. The database answers in seconds; the thinking happens locally, on columns you paid for once.</p>
<h2>Four combinations worth stealing</h2>
<ul>
<li><strong>Hiring signal to inbox.</strong> Companies Hiring Now (the titles your product serves, agencies out, decision makers on), then Company Employees (<code>roles: ["vp", "director"]</code>, <code>titleContains</code> for the function), then Email Finder on the rows you picked.</li>
<li><strong>Inbound in one minute.</strong> Email to Profile Finder on the form-fill address, Company Enrichment by Domain on its company domain, Decision Maker Email Finder for the two people above the one who filled the form. <a href="https://dev.to/kevinhall/inbound-lead-qualification-from-the-email-domain-company-record-decision-makers-and-the-gmail-1p5d">The write-up</a>.</li>
<li><strong>Competitor alumni.</strong> People Search with <code>pastEmployerDomains</code> and <code>leftPastEmployerAfterYear</code>, <code>market</code> first to see where they went, then <code>people</code> with <code>mustHave: ["email"]</code>.</li>
<li><strong>CRM refresh.</strong> Bulk People Enrichment on the CRM export; rows whose current employer no longer matches the CRM, or whose <code>updatedAt</code> is old, are the ones to re-check, and Company Employees on the old domain tells you who replaced them.</li>
</ul>
<h2>Wiring: Claude Code, MCP, REST, n8n</h2>
<p>Pin the Actors you use in the server URL, so an agent loads only those definitions:</p>
<pre><code class="language-plaintext">claude mcp add --transport http apify "https://mcp.apify.com?tools=b2bsearch/b2b-leads-finder,b2bsearch/people-database-search,b2bsearch/company-database-search,b2bsearch/domain-to-company,b2bsearch/domain-to-decision-makers,b2bsearch/company-employees,b2bsearch/linkedin-email-finder,b2bsearch/linkedin-to-phone,b2bsearch/hiring-companies-finder,b2bsearch/linkedin-job-search-api" --header "Authorization: Bearer &lt;APIFY_TOKEN&gt;"
</code></pre>
<pre><code class="language-json">{
  "mcpServers": {
    "apify": {
      "type": "http",
      "url": "https://mcp.apify.com?tools=b2bsearch/b2b-leads-finder,b2bsearch/company-employees,b2bsearch/domain-to-company",
      "headers": { "Authorization": "Bearer &lt;APIFY_TOKEN&gt;" }
    }
  }
}
</code></pre>
<p>Tool names use a double hyphen (<code>b2bsearch--company-employees</code>). Any Actor is also one REST call that returns the rows in the response:</p>
<pre><code class="language-plaintext">curl -X POST "https://api.apify.com/v2/acts/b2bsearch~domain-to-decision-makers/run-sync-get-dataset-items?token=&lt;APIFY_TOKEN&gt;" \
  -H "Content-Type: application/json" \
  -d '{"domains": ["acme.com"], "roles": ["founder", "cxo"], "maxPerCompany": 3}'
</code></pre>
<p>And a step in n8n, Make and Zapier through the Apify integrations, or a tool in LangChain, CrewAI and the OpenAI Agents SDK through the same MCP server.</p>
<h2>FAQ</h2>
<p><strong>Is any of this scraped when I run it?</strong> No. The Actors answer from a database of publicly available professional data; nothing is crawled at run time, and no account of yours is involved.</p>
<p><strong>Which Actor first?</strong> A filter: B2B Leads Finder for people, Company Search for accounts. A domain: Company Enrichment by Domain, then Decision Makers or Company Employees. URLs, emails, names or a CSV: the finders and Bulk People Enrichment. A job title: Companies Hiring Now.</p>
<p><strong>What happens on a miss?</strong> A free row with <code>_status</code> and a note. Not found, no email, ambiguous domain, namesake, skipped by <code>mustHave</code>: each has a name, and none is charged.</p>
<p><strong>How fresh is the data?</strong> Rows carry <code>_freshness</code> and <code>updatedAt</code>, and the search Actors take <code>updatedAfter</code>. It is a database, so a record can be months old, and it tells you when.</p>
<p><strong>Does the free plan get the same rows?</strong> Yes, with a cap per run and a monthly allowance per Actor; counts and previews are outside both.</p>
<h2>For AI agents reading this</h2>
<p>The b2bsearch Actors run as tools over the hosted Apify MCP server with the tools pinned in the URL (above). Pick by the key the user holds:</p>
<ul>
<li>filters: <code>b2bsearch--b2b-leads-finder</code>, <code>b2bsearch--people-database-search</code> (<code>mode: "count"</code> first), <code>b2bsearch--company-database-search</code> (<code>previewOnly: true</code> first)</li>
<li>domains: <code>b2bsearch--domain-to-company</code>, <code>b2bsearch--domain-to-decision-makers</code>, <code>b2bsearch--company-employees</code> (<code>compact: true</code>)</li>
<li>profile URLs: <code>b2bsearch--linkedin-email-finder</code>, <code>b2bsearch--linkedin-to-phone</code>, <code>b2bsearch--profile-lookup</code></li>
<li>emails: <code>b2bsearch--email-to-linkedin</code>, <code>b2bsearch--reverse-email-lookup</code>; a CSV: <code>b2bsearch--bulk-people-enrichment</code></li>
<li>job titles: <code>b2bsearch--hiring-companies-finder</code>, <code>b2bsearch--linkedin-job-search-api</code> (<code>previewOnly: true</code> first)</li>
</ul>
<p>Every row has <code>_status</code>; rows other than <code>found</code> are free and explain themselves in <code>_note</code> or <code>_error</code>. Read results with <code>get-dataset-items</code> and a <code>fields</code> list. Set <code>callOptions.maxTotalChargeUsd</code> on every paid run; per-plan pricing is on each Actor page. Platform docs: <a href="https://docs.apify.com/mcp">Apify MCP server</a> and <a href="https://apify.com/agents.md">apify.com/agents.md</a>.</p>
]]></content:encoded></item><item><title><![CDATA[30 ready-made B2B data tasks on Apify, from lead lists to call lists]]></title><description><![CDATA[Lead lists, call lists, account maps, hiring signals and CRM cleanup: 30 one-click Apify tasks with the input, the output and what to run next.]]></description><link>https://gtm-workflows.hashnode.dev/30-ready-made-b2b-data-tasks-on-apify-from-lead-lists-to-call-lists</link><guid isPermaLink="true">https://gtm-workflows.hashnode.dev/30-ready-made-b2b-data-tasks-on-apify-from-lead-lists-to-call-lists</guid><category><![CDATA[lead generation]]></category><category><![CDATA[sales]]></category><category><![CDATA[apify]]></category><category><![CDATA[AI]]></category><dc:creator><![CDATA[Kevin Hall]]></dc:creator><pubDate>Sat, 31 Oct 2026 13:50:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6ac951f8c58437c7a9101e18/b3ed6fad-1357-4833-b6c7-ceb493b5da2b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Every B2B data Actor I use on Apify has thirty or forty input fields. That is great for an agent and terrible for a person who opens the page for the first time and just wants a list of HR directors in Ohio</p>
<p>So I saved the combinations people actually ask for as ready-made tasks. A task page shows the input already filled in, the columns you will get and a Try for free button. The button copies the task into your Apify account, you change a field or two and press start</p>
<p>Below are thirty of those jobs, grouped by the problem you walk in with. For each one: the link, the input, what comes back and what to run next. The counts were taken with free previews on 31 October 2026</p>
<h2>How to use a task</h2>
<p>Open the link, press Try for free, change what you need, press Start. The input on the page is plain JSON, so the same object works from the API, from Make or n8n, and from an agent over MCP.</p>
<p>Two habits save most of the budget. Count first wherever the Actor has a free count or preview. And cap the run with <code>maxResults</code> or <code>maxRows</code> until you like what comes back.</p>
<h2>You have an ICP and no list</h2>
<p>This is where <a href="https://apify.com/b2bsearch/b2b-leads-finder">B2B Leads Finder</a> lives. You describe the people, it returns one row per person, and every row has a live email.</p>
<p><strong>1. Sales leaders at mid-size US companies.</strong> <a href="https://apify.com/b2bsearch/b2b-leads-finder/examples/us-sales-leaders-with-emails">Open the task</a>.</p>
<pre><code class="language-json">{"jobTitles": ["vp sales", "head of sales", "chief revenue officer"], "countries": ["us"], "companySizeMin": 50, "companySizeMax": 1000, "emailType": "any", "maxResults": 100}
</code></pre>
<p>Each row brings <code>fullName</code>, <code>jobTitle</code>, <code>email</code> with <code>emailType</code> set to <code>work</code> or <code>personal</code>, the company name, domain, size and industry, location and the profile link. <code>phoneOnRecord</code> tells you for free whether a mobile number exists. People with no live email are skipped and counted in a note row, not charged.</p>
<p>Two fields worth knowing here. <code>maxPerCompany</code> stops one big employer from eating the list. <code>excludeDatasets</code> takes the dataset of last week's run and leaves those people out, so a weekly rerun only brings new names.</p>
<p><strong>2. Small business owners.</strong> <a href="https://apify.com/b2bsearch/b2b-leads-finder/examples/small-business-owners-us-with-emails">Open the task</a>. The trick is in one field: "owner" also matches product owners and process owners, so the task excludes them.</p>
<pre><code class="language-json">{"jobTitles": ["owner", "founder", "ceo"], "excludeJobTitles": ["product owner", "process owner"], "countries": ["us"], "companySizeMin": 1, "companySizeMax": 50, "emailType": "any", "maxResults": 100}
</code></pre>
<p><strong>3. Managing directors in Germany.</strong> <a href="https://apify.com/b2bsearch/b2b-leads-finder/examples/geschaeftsfuehrer-germany-with-emails">Open the task</a>. Local markets write titles in the local language, so search in it.</p>
<pre><code class="language-json">{"jobTitles": ["geschäftsführer", "inhaber", "managing director", "ceo"], "countries": ["de"], "companySizeMin": 10, "companySizeMax": 250, "emailType": "any", "maxResults": 100}
</code></pre>
<p><strong>4. HR and talent leaders, or procurement managers.</strong> Same Actor, different titles. The <a href="https://apify.com/b2bsearch/b2b-leads-finder/examples/hr-and-talent-leaders-with-work-emails">HR task</a> asks for work addresses only with <code>"emailType": "work"</code>, which is what you want when you sell to a company and not to a person. The <a href="https://apify.com/b2bsearch/b2b-leads-finder/examples/procurement-managers-manufacturing-with-emails">procurement task</a> adds <code>"industries": ["Manufacturing"]</code> and a seniority floor so buyers come without interns.</p>
<p><strong>5. You already built the search in Apollo.</strong> <a href="https://apify.com/b2bsearch/b2b-leads-finder/examples/apollo-search-url-to-leads">Open the task</a>. Paste the people-search URL into <code>apolloUrl</code> and the Actor reads the filters out of it. No Apollo login and no credits.</p>
<p><strong>6. You need people you can call, not just email.</strong> <a href="https://apify.com/b2bsearch/b2b-leads-with-phones">Leads with Phones</a> takes the same filters and only returns people who have both a verified mobile and a live email. <a href="https://apify.com/b2bsearch/b2b-leads-with-phones/examples/us-sales-leaders-mobile-numbers">Sales leaders with mobiles</a> and <a href="https://apify.com/b2bsearch/b2b-leads-with-phones/examples/real-estate-agents-mobile-numbers">real estate agents and brokers</a> are ready to run.</p>
<pre><code class="language-json">{"jobTitles": ["realtor", "real estate agent", "real estate broker", "broker associate"], "industries": ["Real Estate"], "countries": ["us"], "maxResults": 50}
</code></pre>
<p>Rows carry <code>phone</code> in E.164, every number in <code>phones</code>, the email and the company. A <code>skipped_no_contact</code> note row says how many matching people had no mobile or no email. Those cost nothing.</p>
<h2>Count before you pay for a single row</h2>
<p><a href="https://apify.com/b2bsearch/people-database-search">People Search</a> has a <code>count</code> mode and a <code>market</code> mode. Neither charges per row, and they answer the question you should ask before any list: how big is this, really.</p>
<p><strong>7. How many software engineers per country.</strong> <a href="https://apify.com/b2bsearch/people-database-search/examples/software-engineers-by-country-free-count">Open the task</a>.</p>
<pre><code class="language-json">{"mode": "count", "titleKeywords": ["software engineer", "software developer"], "segments": [{"name": "United Kingdom", "countries": ["gb"]}, {"name": "Germany", "countries": ["de"]}, {"name": "Canada", "countries": ["ca"]}]}
</code></pre>
<p>One row per segment with <code>_total</code>. Today the UK came back with about 186 thousand, Canada 166 thousand and Germany 116 thousand. The US and India stop at the count ceiling of a million, which tells you to narrow before you pull.</p>
<p><strong>8. Compare sales team size across companies.</strong> <a href="https://apify.com/b2bsearch/people-database-search/examples/sales-team-size-by-company-free-count">Open the task</a>. Each segment is a company domain, the titles are account executives, SDRs and sales managers. Handy for sizing a territory or for a competitor slide.</p>
<p><strong>9. Count a Sales Navigator search before exporting it.</strong> <a href="https://apify.com/b2bsearch/sales-navigator-search/examples/count-sales-navigator-search-free">Open the task</a>. Paste the search URL, get the count, then run the <a href="https://apify.com/b2bsearch/sales-navigator-search/examples/head-of-growth-us-export">export task</a> with the same URL. The sample search for US heads of growth counted 1,278 people. No seat and no cookies.</p>
<p>Then switch <code>mode</code> to <code>people</code> and you get rows. Two filters people rarely find on their own: <code>onlyBetweenJobs</code> for <a href="https://apify.com/b2bsearch/people-database-search/examples/professionals-between-jobs">people with no current position</a>, and <code>careerStartYearMin</code> for <a href="https://apify.com/b2bsearch/people-database-search/examples/early-career-software-engineers">engineers early in their career</a>.</p>
<h2>You hold profile URLs or emails</h2>
<p>The two most used Actors in the family live here. The tasks use placeholder inputs, so replace them with your own list.</p>
<p><strong>10. Profile URLs to mobile numbers.</strong> <a href="https://apify.com/b2bsearch/linkedin-to-phone/examples/linkedin-urls-to-mobile-numbers">Open the task</a> on <a href="https://apify.com/b2bsearch/linkedin-to-phone">Phone Number Finder</a>.</p>
<pre><code class="language-json">{"profileUrls": ["https://www.linkedin.com/in/jane-doe-example", "jane.example@example.com", "Jane Example, example.com"]}
</code></pre>
<p>The field takes profile links, email addresses and "Name, domain" lines in one list, mixed freely. A found row has <code>primaryPhone</code>, all numbers in <code>phones</code>, <code>phoneCount</code>, the name, title, company and location. <code>no_phone</code>, <code>not_found</code> and <code>profile_removed</code> are free rows that tell you why.</p>
<p><strong>11. CRM contacts with an email and no number.</strong> <a href="https://apify.com/b2bsearch/linkedin-to-phone/examples/crm-emails-to-mobile-numbers">Open the task</a>. Same Actor, the list is just emails. Export the column from the CRM and paste it.</p>
<p><strong>12. Profile URLs to work emails.</strong> <a href="https://apify.com/b2bsearch/linkedin-email-finder/examples/linkedin-urls-to-work-emails">Open the task</a> on <a href="https://apify.com/b2bsearch/linkedin-email-finder">Email Finder</a>.</p>
<pre><code class="language-json">{"profileUrls": ["https://www.linkedin.com/in/jane-doe-example"], "includePersonalEmails": false}
</code></pre>
<p>The row gives <code>email</code>, every company address on record in <code>workEmails</code>, and <code>emailType</code>. That last field is the one to read. <code>work_current</code> is an address at today's employer. <code>work_other</code> is an address from somewhere else, usually an old job.</p>
<p><strong>13. Find the CRM contacts who will bounce.</strong> <a href="https://apify.com/b2bsearch/linkedin-email-finder/examples/crm-contacts-with-past-employer-emails">Open the task</a>. Same input, different reading. Every <code>work_other</code> row is a contact whose address in your CRM probably points at a former employer. Fix those before the next sequence, not after the bounce report.</p>
<p><strong>14. Personal emails for recruiting.</strong> <a href="https://apify.com/b2bsearch/linkedin-email-finder/examples/personal-emails-for-recruiting">Open the task</a>. Flip <code>includePersonalEmails</code> to <code>true</code> and gmail or outlook addresses come back too, which is what a recruiter needs for someone who should not get a job offer at their work address.</p>
<h2>You have a list of companies</h2>
<p><a href="https://apify.com/b2bsearch/company-employees">Company Employees</a> and <a href="https://apify.com/b2bsearch/domain-to-decision-makers">Decision Maker Email Finder by Domain</a> both start from a website domain.</p>
<p><strong>15. The leadership team of one company.</strong> <a href="https://apify.com/b2bsearch/company-employees/examples/leadership-team-by-company-domain">Open the task</a>.</p>
<pre><code class="language-json">{"companies": ["hubspot.com"], "roles": ["cxo", "vp", "director"], "titleExcludes": ["assistant", "office of", "executive operations", "chief of staff"], "excludeLowSignal": true, "maxRows": 200}
</code></pre>
<p>Rows carry <code>fullName</code>, <code>title</code>, <code>seniority</code>, location, <code>positionStartYear</code>, <code>hasWorkEmail</code>, <code>hasPersonalEmail</code> and the profile link. The <code>titleExcludes</code> list is there because executive assistants love titles like "Office of the CEO".</p>
<p><strong>16. Every employee, then contacts only for the ones you need.</strong> The <a href="https://apify.com/b2bsearch/company-employees/examples/all-employees-by-company-domain">roster task</a> uses <code>"profileDetail": "roster"</code>, the light tier, to list the whole company. The <a href="https://apify.com/b2bsearch/company-employees/examples/employee-emails-by-company-domain">contacts task</a> switches to <code>"profileDetail": "contacts"</code> with <code>"mustHave": ["email"]</code>, so anyone without an address is skipped for free.</p>
<p><strong>17. Engineers at a company you recruit from.</strong> <a href="https://apify.com/b2bsearch/company-employees/examples/engineers-at-a-company">Open the task</a>. <code>titleContains</code> takes engineer and developer, <code>titleExcludes</code> drops sales engineers and solutions engineers.</p>
<p><strong>18. The CEO and founders of a list of companies.</strong> <a href="https://apify.com/b2bsearch/domain-to-decision-makers/examples/ceo-and-founders-by-company-domain">Open the task</a>.</p>
<pre><code class="language-json">{"domains": ["linear.app", "vercel.com", "supabase.com", "posthog.com", "retool.com"], "roles": ["founder", "cxo"], "maxPerCompany": 3}
</code></pre>
<p>Rows come ranked per company in <code>_rank</code>, with the title, seniority, profile link and an <code>email</code> that reaches the person today. That means an address on the company domain, else a personal one, never one at a past employer. There are ready versions for <a href="https://apify.com/b2bsearch/domain-to-decision-makers/examples/marketing-leaders-by-company-domain">marketing leaders</a> and <a href="https://apify.com/b2bsearch/domain-to-decision-makers/examples/cto-and-engineering-leaders-by-domain">CTOs and engineering leaders</a>.</p>
<p><strong>19. The email format of a domain.</strong> <a href="https://apify.com/b2bsearch/company-email-format-finder/examples/email-format-of-company-domains">Open the task</a> on Company Email Format Finder. Send domains and get <code>pattern</code> like <code>first.last</code>, how many real addresses confirm it, how strongly they agree, and a few masked samples. Useful when you have a name and a company and nothing else.</p>
<h2>You want a reason to reach out</h2>
<p>A list is cold until something happens at the company. These tasks start from the something.</p>
<p><strong>20. Companies hiring account executives right now.</strong> <a href="https://apify.com/b2bsearch/hiring-companies-finder/examples/companies-hiring-account-executives-us">Open the task</a> on <a href="https://apify.com/b2bsearch/hiring-companies-finder">Companies Hiring Now</a>.</p>
<pre><code class="language-json">{"jobTitles": ["account executive"], "locations": ["United States"], "postedLimit": "month", "excludeAgencies": true, "maxCompanies": 25, "decisionMakers": true, "decisionMakerTitleKeywords": ["chief revenue", "head of sales", "vp sales"], "decisionMakersPerCompany": 2}
</code></pre>
<p>The free preview counted 3,344 employers with 12,279 matching postings in the last month. Each company row carries <code>matchingJobs</code>, the job <code>titles</code>, <code>jobs30d</code> and <code>jobs90d</code> for the hiring pace, and the company card. With <code>decisionMakers</code> on, the people who own the hire follow as rows of <code>_type</code> <code>decision_maker</code>.</p>
<p>A team hiring five AEs is probably shopping for sales tools too. The <a href="https://apify.com/b2bsearch/hiring-companies-finder/examples/companies-hiring-engineers-germany-with-ctos">engineering version</a> does the same for German companies hiring developers, with the CTO.</p>
<p><strong>21. New senior hires at your target accounts.</strong> <a href="https://apify.com/b2bsearch/new-hires-finder/examples/new-hires-at-target-accounts">Open the task</a> on New Hires Finder. New leaders tend to pick their vendors in the first months.</p>
<pre><code class="language-json">{"companyDomains": ["hubspot.com", "zendesk.com", "asana.com"], "sinceMonths": 12, "seniority": ["director", "vp", "cxo"], "maxPerCompany": 20}
</code></pre>
<p>Rows bring <code>startedAt</code>, <code>previousCompany</code> and <code>previousTitle</code>, which is the opening line of the email.</p>
<p><strong>22. Former employees of a company and where they are now.</strong> <a href="https://apify.com/b2bsearch/former-employees-finder/examples/former-employees-of-a-company">Open the task</a> on Former Employees Finder. Point it at a customer and you find champions who moved to new companies. Point it at a competitor and you find people who already know the problem. <code>leftAfterYear</code> keeps it recent, and each row has <code>currentCompany</code>, <code>currentTitle</code> and <code>currentRoleStartYear</code>.</p>
<p><strong>23. New VPs of sales since last October.</strong> <a href="https://apify.com/b2bsearch/people-database-search/examples/new-sales-leaders-since-october-2025">Open the task</a>. People Search with <code>currentRoleStartedAfterDate</code> set to <code>2025-10</code>. Change the titles and you have new CFOs, new heads of people, new anything.</p>
<p><strong>24. Who hires for Kubernetes in Berlin.</strong> <a href="https://apify.com/b2bsearch/linkedin-job-search-api/examples/kubernetes-jobs-berlin-hiring-companies">Open the task</a> on Jobs Database API. It searches the job description, not the title, so a platform engineer posting that mentions Kubernetes counts. Today: 96 open postings at 65 employers, data through 21 October.</p>
<pre><code class="language-json">{"titleSearch": ["devops engineer", "platform engineer", "site reliability engineer"], "descriptionSearch": ["kubernetes"], "locationSearch": ["Berlin, Germany"], "timeRange": "30d", "removeAgency": true, "previewOnly": true}
</code></pre>
<p>Write the city with its country. A bare "Berlin" is refused, "Berlin, Germany" works.</p>
<h2>You need companies, not people</h2>
<p><strong>25. Seed startups in Europe funded since 2025.</strong> <a href="https://apify.com/b2bsearch/company-database-search/examples/seed-startups-europe-funded-since-2025">Open the task</a> on <a href="https://apify.com/b2bsearch/company-database-search">Company Search</a>.</p>
<pre><code class="language-json">{"countryGroups": ["eu"], "hasFunding": true, "fundingRounds": ["pre_seed", "seed"], "fundedAfter": "2025-01-01", "employeesMax": 50, "previewOnly": true}
</code></pre>
<p>The preview counted 1,650 companies. Turn <code>previewOnly</code> off and each row is a company card: name, domain, headcount, industry, HQ and a <code>funding</code> block with the last round type and date. The <a href="https://apify.com/b2bsearch/company-database-search/examples/us-series-a-b-software-companies">Series A and B task</a> and <a href="https://apify.com/b2bsearch/company-database-search/examples/manufacturing-companies-germany-50-500">German manufacturers</a> work the same way.</p>
<p><strong>26. Companies like your best customers.</strong> <a href="https://apify.com/b2bsearch/lookalike-company-finder/examples/companies-similar-to-your-best-customers">Open the task</a> on Lookalike Company Finder. Give it two or three customer domains in <code>seedDomains</code>. Every row says what it matched on in <code>matchedOn</code>, so you can see why a company is there instead of trusting a score.</p>
<p><strong>27. Clean up a column of messy domains.</strong> <a href="https://apify.com/b2bsearch/domain-to-company/examples/domain-to-company-name">Open the task</a> on Company Enrichment by Domain. It takes <code>https://www.notion.so/product</code>, <code>WWW.GONG.IO</code> and <code>linear.app/features</code> as they come from a spreadsheet and returns one card per domain. When two companies claim a domain, the row says so and costs nothing instead of guessing.</p>
<h2>You have a mess to clean</h2>
<p><strong>28. Qualify inbound sign-ups by email.</strong> <a href="https://apify.com/b2bsearch/reverse-email-lookup/examples/qualify-inbound-signups-by-email">Open the task</a> on Reverse Email Lookup. Paste the emails from your sign-up form and get back the person, title and company with <code>"compact": true</code>. Now you know which trial is a VP at a real company and which is a student.</p>
<p><strong>29. Where do your customer contacts work now.</strong> <a href="https://apify.com/b2bsearch/profile-lookup/examples/where-do-my-contacts-work-now">Open the task</a> on Profile Lookup. Feed it the profile URLs of the people who signed your contracts. A changed <code>companyName</code> is either a churn risk or a new account, sometimes both.</p>
<p><strong>30. A CRM export with a bit of everything.</strong> <a href="https://apify.com/b2bsearch/waterfall-lead-enrichment/examples/crm-export-to-emails-and-phones">Open the task</a> on Waterfall Lead Enrichment. Each row of the CSV can carry a different key: an email, a profile URL, a name with a company. The Actor resolves whatever is there to a profile and adds contacts with <code>"contacts": true</code>. For people files there is also <a href="https://apify.com/b2bsearch/bulk-people-enrichment/examples/enrich-csv-of-emails-urls-and-names">Bulk People Enrichment</a>.</p>
<h2>Chains that work</h2>
<p>One task rarely finishes the job. These are the chains I run most:</p>
<ul>
<li>Companies Hiring Now for the employers, then the domain column into Decision Maker Email Finder, then the profile links into Phone Number Finder for the ones worth a call.</li>
<li>People Search in <code>count</code> mode to size the box, B2B Leads Finder to pull it, and the same input every Monday with <code>excludeDatasets</code> pointing at last week.</li>
<li>Former Employees Finder on your best customer, then Email Finder on the profile links, and keep only <code>work_current</code>.</li>
<li>Reverse Email Lookup on a new sign-up, Company Enrichment by Domain on the company, Decision Maker Email Finder for the person who signs.</li>
</ul>
<h2>Running them from an agent</h2>
<p>Every Actor here is also an MCP tool. Pin the ones you need so the agent does not load the whole Store:</p>
<pre><code class="language-bash">claude mcp add --transport http apify "https://mcp.apify.com?tools=b2bsearch/b2b-leads-finder,b2bsearch/people-database-search,b2bsearch/company-employees,b2bsearch/linkedin-to-phone" --header "Authorization: Bearer &lt;APIFY_TOKEN&gt;"
</code></pre>
<p>The task inputs above are exactly what the agent sends. Ask it to count first and to cap each call with <code>maxResults</code>, and it will usually do both.</p>
<h2>FAQ</h2>
<p><strong>What does Try for free do?</strong> It copies the task into your Apify account with the input filled in. You run it on your own account and can change any field first.</p>
<p><strong>Do I pay for misses?</strong> No. A person with no email, a domain nobody claims or a profile that was removed comes back as a free row with a <code>_status</code> that says why.</p>
<p><strong>Does it scrape anything or need my login?</strong> No. The answers come from a database, so there are no cookies, sessions or proxies on your side.</p>
<p><strong>Can I use these from the API?</strong> Yes. POST the same JSON to the Actor's run endpoint, or run the task by its id.</p>
<p>If the job you have is not on this list, tell me in the comments. The good ones become task number thirty-one.</p>
]]></content:encoded></item></channel></rss>