Apify is the piece of my stack nobody else seems to use for prospecting. It runs one actor for me: a LinkedIn profile search. It costs about half a cent a profile. And it can do two things no data vendor I pay for can do: filter for people who posted recently, and filter for people who just changed jobs.
It also does the single most valuable check in the whole pipeline. Before I pay for anyone's phone number, Apify tells me whether they still work there.
Here is the actor, the exact input, the numbers from last week's build, and the way it fails silently if you are not watching.
The actor, in six steps
-
01
Run the right actor, in Full mode, 25 at a time
The actor is harvestapi/linkedin-profile-search. Pricing is pay per event: $0.10 per search page plus $0.004 per full profile. That works out to about $0.006 a profile, or 50 profiles for 30 cents.
Two settings matter. profileScraperMode must be Full. Short mode returns obfuscated profile URLs with the headline, current position and public identifier all null, which is useless. And maxItems should be 25 or more, because the page fee is flat. Ten items cost two cents a person. Twenty-five cost about one.
Real screenshot goes hereThe actor input JSON in the Apify console, Full mode and maxItems 25
-
02
Use the filters nobody else has
companyHeadcount takes bands: B is 11 to 50, C is 51 to 200. currentJobTitles takes a list. locations takes a list, and a Colombian profile once leaked through United States, so filter again on the result.
Then the two that matter. recentlyPostedOnLinkedIn finds people who are active right now. recentlyChangedJobs finds people in a new seat. excludeCurrentCompanies drops your customers and your client's competitors. Smart, senior, active, and reachable is the list. This is how you build it.
Real screenshot goes hereA run with recentlyPostedOnLinkedIn on and the headline column filled
-
03
Check still-in-role before you pay for a reveal
Why: one client's list had 64 people who had left their company while still carrying a deliverable email address. Deliverable is not employed. So Apify runs before the paid reveal.
Last week's build: 306 rows checked. 281 came back OK. 20 were not returned. 5 were a name match with a different company string, usually a DBA. I re-ran the 22 unconfirmed for eight cents. Most came back under a new vanity URL, so a slug mismatch is not a missing profile. In the end 10 were held out: five profiles gone, three at a new company, two with no current role. Every one had a verified mobile. Batch three did 150 profiles in 76 seconds for about 60 cents.
Real screenshot goes hereThe verdict column: OK, NOT_RETURNED, CHECK, LEFT_ROLE with the new title beside it
-
04
Job changers: dial, do not email
When recentlyChangedJobs is on, LinkedIn shows the new employer. Your email vendor still holds the old one. In one test, 7 of 9 verified emails pointed at the previous company. Mobiles follow the person, so they stay right.
So on any job-changer list the play is a call, not a sequence. The email will bounce, or land with someone who does not work there anymore.
Real screenshot goes hereSide by side: LinkedIn current company versus the vendor's email domain
-
05
Pull ten times what you need and filter hard
Raw yield on a broad pull was about 20 percent through my buyer gate. Consultants, fractionals, investors and the self-employed all come back when you search a title. At half a cent that is fine. One session kept 388 people for $4.19 across about 40 runs, a 56 to 80 percent keep rate after title, vendor and exclusion filters.
Pull wide. Filter in code, never by hand.
Real screenshot goes hereThe funnel: pulled, kept after filters, cost per kept person
-
06
Watch for the hard limit, because it fails quietly
Apify has no overage. The starter plan carries $29 of prepaid usage. When it is gone, every paid run returns 402 not-enough-usage-to-run-paid-actor, then 403 Monthly usage hard limit exceeded. Your account also has its own maxMonthlyUsageUsd, and the lower cap wins.
Mine hid for about 100 rows: the error handler logged the exception name, cached it as that person's verdict, and the progress counter kept climbing while spend sat frozen at $28.97. A flat spend line under a climbing counter is a failure. Check GET /v2/users/me/limits before any big run.
Real screenshot goes hereThe limits response next to a spend chart that went flat
Run this today
- Open the actor harvestapi/linkedin-profile-search. Paste the input below.
- Set profileScraperMode to Full and maxItems to 25. Never lower.
- Turn on recentlyPostedOnLinkedIn for your first pull. Look at who comes back.
- Run your existing list through it. Mark OK, LEFT_ROLE, NO_POSITION, NOT_RETURNED.
- Re-run the NOT_RETURNED rows once by name and company before calling them dead.
- Hold every LEFT_ROLE out of the campaign. Call the job changers, do not email them.
- Check /v2/users/me/limits and write the number down before a big run.
Free
The actor input and the still-in-role check
The input that works, and the verdict rules that turn a profile pull into one answer per row.
{
"locations": ["United States"],
"companyHeadcount": ["B", "C"],
"currentJobTitles": ["Owner", "President", "CEO", "General Manager"],
"searchQuery": "roofing OR hvac OR plumbing",
"recentlyPostedOnLinkedIn": true,
"profileScraperMode": "Full",
"maxItems": 25
}
Actor: harvestapi/linkedin-profile-search ($0.10 per search page + $0.004 per profile)
B = 11 to 50 staff, C = 51 to 200. Drop recentlyPostedOnLinkedIn for a plain pull.
STILL-IN-ROLE CHECK, one verdict per row (name, company, linkedin_url):
OK current position company matches the row, or a known DBA of it
LEFT_ROLE current position is at a different company; record the new title + company
NO_POSITION the profile has no current position
NOT_RETURNED no profile came back (404 or a new vanity URL); re-run once by name + company
CHECK name matched, company string differs; a person looks
Never mark a row OK because the email is deliverable. Deliverable is not employed.
Before a big run: GET https://api.apify.com/v2/users/me/limits (no overage; 402 then 403)