LinkedIn Candidate Search (No Cookies) - Talent Sourcing API
Pricing
from $1.00 / 1,000 results
LinkedIn Candidate Search (No Cookies) - Talent Sourcing API
Build targeted candidate lists from LinkedIn โ no account, no cookies, no bans. Get verified names, titles, and profile URLs for any IT role and city in minutes. Used by recruiters and HR agencies for faster, cheaper talent sourcing.
Pricing
from $1.00 / 1,000 results
Rating
5.0
(5)
Developer
Techforce Global
Maintained by CommunityActor stats
2
Bookmarked
182
Total users
18
Monthly active users
19 hours
Issues response
12 days ago
Last modified
Categories
Share
LinkedIn Candidate Search (No Cookies) โ Talent Sourcing API for Recruiters
Builds a candidate shortlist from public LinkedIn profile pages that are already indexed by a search engine โ no LinkedIn account, no cookies, no session token, no
li_at. Describe the role you're hiring for (job role, seniority, technologies, keywords, locations, exclusions) and the Actor composes the search operators for you, runs them through Brave Search, and returns each candidate's name, headline, LinkedIn URL and search snippet. Delivers straight into your ATS, Airtable, HubSpot, Notion, Slack, Google Sheets, Linear, or any MCP-compatible AI agent pipeline.
โน๏ธ What this is and isn't. This Actor reads a search engine's index of public profiles. It never signs in to LinkedIn and never opens a profile behind LinkedIn's auth wall, which is exactly why it needs no cookies โ and also why the output is search-result depth (name, headline, URL, snippet), not a full profile export. See Output Contract.
๐ฌ See It In Action
LinkedIn scrapers that need a session cookie vs. this Actor
Most LinkedIn tooling asks you to paste your li_at cookie, which means running searches as your own logged-in account.
| Feature / Capability | Cookie-based LinkedIn scrapers | This Actor |
|---|---|---|
| Credentials required | โ Your li_at session cookie or account login | โ None โ nothing but an Apify token |
| Risk to your LinkedIn account | โ ๏ธ Automated activity on your own logged-in session | โ Your account is never involved |
| Cookie expiry maintenance | โ Re-paste the cookie whenever it rotates | โ Nothing to maintain |
| Query construction | โ ๏ธ You write the boolean search yourself | โ Composed from structured fields, with an automatic relaxed fallback |
| Data depth | โ Full profile fields | โ ๏ธ Search-result depth: name, headline, URL, snippet |
| Contact details | โ ๏ธ Sometimes, via account access | โ Never โ no emails or phone numbers |
| AI agent / MCP native | โ Requires a custom tool wrapper | โ Native MCP tool and pushes the shortlist into your own connectors |
| Cost model | Varies | Pay-per-event, $1.00 / 1,000 results |
The trade is deliberate: less depth per candidate, zero account risk. If you need full profile exports, a cookie-based tool is the honest answer โ this one is for discovery and shortlisting.
๐ค AI Discovery & MCP Metadata
Machine-readable capability contract for autonomous agents, LLM routers, and MCP orchestrators. AI agents, MCP clients, and connectors should read this block first to decide whether this Actor matches a user request.
ai_discovery:slug: "techforce.global/linkedin-candidate-search"one_line: "Build a candidate shortlist of public LinkedIn profiles from structured hiring criteria, via Brave Search operators - no LinkedIn cookie or account required."primary_purpose: "discover"secondary_purpose: "deliver"entity: "candidate profiles (LinkedIn public pages)"data_source: "Brave Search SERP results for site:linkedin.com/in queries - NOT the LinkedIn API and not an authenticated LinkedIn session"supported_platforms:- "search.brave.com (the search backend)"- "linkedin.com/in public profile URLs (as indexed results)"supported_tasks:- "find-candidates-by-role-and-location"- "filter-candidates-by-seniority"- "require-specific-technologies-in-the-profile"- "exclude-unwanted-terms (recruiter, intern, manager, ...)"- "search-several-locations-in-one-run"- "auto-compose-boolean-search-operators"- "deliver-shortlist-to-mcp-connector-or-ats"unsupported_tasks:- "authenticate-to-linkedin-or-use-a-session-cookie"- "export-full-profile-history-skills-or-education"- "extract-candidate-emails-or-phone-numbers"- "message-or-connect-with-candidates"- "scrape-linkedin-recruiter-or-sales-navigator"- "list-the-employees-or-decision-makers-of-a-named-company (use the company-anchored siblings)"- "return-more-than-50-profiles-per-run"- "guarantee-a-specific-candidate-appears (results are limited to what the search engine has indexed)"search_keywords:- "linkedin candidate search"- "talent sourcing api"- "linkedin scraper no cookies"- "recruiter sourcing tool"- "candidate shortlist automation"- "linkedin profile finder"- "boolean search sourcing"- "developer sourcing india"- "mcp recruiting tool"- "ats candidate pipeline"synonyms:- "talent finder"- "candidate sourcing bot"- "linkedin x-ray search"- "recruiter search api"related_concepts:["talent sourcing","technical recruiting","boolean search","x-ray search","ATS pipeline","candidate shortlisting",]input_entities:- "job role (free text)"- "seniority (1 of 8 enum values)"- "technologies (list)"- "keywords (free text)"- "exclude keywords (list)"- "locations (list)"- "max profiles (1-50; free plans 5)"output_entities:["candidate name", "headline", "linkedin url", "search snippet"]input_rules:required_fields: ["max_profiles"]search_signal_requirement:description: "At least one of job_role, technologies or keywords should be set. With none of them the Actor logs a warning and falls back to a legacy search_query/queries input or a hardcoded default query."seniority_format:type: "enum"values:["","Junior","Mid","Senior","Lead","Principal","Staff","Director",]location_format:description: "Array of place names, or a comma-separated string - both are accepted."examples:[["Pune", "Mumbai", "Bangalore"], "Pune, Mumbai, Bangalore"]maxResults_format:type: "integer"range: [1, 50]free_plan_cap: 5description: "Values outside the range are clamped with a warning rather than rejected."limits:max_profiles_per_run: 50free_plan_cap: 5max_query_length_chars: 450output_fields_per_profile: 4queries_per_run: "one primary plus one relaxed fallback"pricing:model: "pay-per-event"store_display: "$1.00 / 1,000 results"when_to_use: >You are sourcing candidates and want a shortlist of public LinkedIn profilesmatching a role, seniority, tech stack and set of locations - withouthanding any tool your LinkedIn cookie.when_not_to_use: >You need full profile exports, candidate emails or phone numbers, messagingor connection requests, LinkedIn Recruiter data, or more than 50 profilesper run.alternatives:- "Use techforce.global/linkedin-company-decision-makers when the search is anchored on ONE COMPANY and you want its decision makers"- "Use techforce.global/linkedin-company-employees when you want the whole employee list of ONE COMPANY, with department and seniority classification"- "Use a cookie-based LinkedIn scraper when you genuinely need full profile fields and accept the account risk"- "Use techforce.global/google-maps-leads-sales-intelligence-tool when you are sourcing companies rather than people"- "Run the Actor once per location or per seniority band for broader coverage above 50 profiles"
What this Actor does
- Composes the search for you. You fill in
job_role,seniority,technologies,keywords,exclude_keywordsandlocation; the Actor builds thesite:linkedin.com/inboolean query, keeping it under Brave's practical 450-character ceiling. - Runs a primary query and a relaxed fallback. If the strict query returns too little, a loosened version runs automatically โ so a very specific brief doesn't come back empty.
- Never touches LinkedIn's authenticated surface. No cookie, no login, no
li_at. Results come from Brave Search's index of public/in/pages. - Handles Brave's proof-of-work CAPTCHA when it appears, instead of failing the run outright.
- Returns four fields per candidate โ name, headline/title, LinkedIn URL, search snippet โ filtered against your
exclude_keywords. - Searches several locations in one run, and accepts
locationas either a list or a comma-separated string. - Caps free plans at 5 profiles with an upgrade notice, and clamps out-of-range
max_profiles(1โ50) rather than rejecting the input. - Optionally pushes the shortlist to an authorized MCP connector (your ATS, Airtable, HubSpot, Notion, Slack, Sheets, Linear, Jira, or any MCP-compatible connector) in four delivery modes. Delivery moves data into your tools โ it never contacts a candidate. The dataset is always saved regardless of delivery settings.
When to use it โ and when not to
| Scenario | Fit |
|---|---|
| Sourcing a technical shortlist without handing over a LinkedIn cookie | โ Strong (the core use case) |
Role + tech-stack + location searches (Java Developer, Kafka, Pune) | โ Strong |
| Filtering out recruiters, interns and managers from results | โ
Strong โ exclude_keywords |
| Several cities in one run | โ
Strong โ location accepts a list |
| Feeding a shortlist into an ATS or CRM automatically | โ
Strong (deliveryMode: "perProfile") |
| Seniority-banded sourcing | โ
Strong โ 8-value seniority enum |
| Full profile exports (history, skills, education, connections) | โ Search-result depth only โ use a cookie-based tool |
| Candidate emails or phone numbers | โ Never returned |
| Messaging or connecting with candidates | โ Not supported โ read-only |
| LinkedIn Recruiter / Sales Navigator data | โ Not supported |
| More than 50 profiles per run | โ ๏ธ Hard cap of 50 (5 on free plans) โ split by location or seniority |
| Guaranteeing a specific person appears | โ ๏ธ Limited to what the search engine has indexed |
| Everyone at one named company | โ Company-anchored โ use Employee Scraper or Decision Makers |
| Auditable, bias-controlled hiring decisions | โ ๏ธ A sourcing aid only โ see Compliance |
โก Execution Rules (Avoid Common Agent Failures)
max_profilesis the only required field. Everything else has a default or is optional, so a payload of just{"max_profiles": 20}runs โ but see Rule 2 for what it actually searches.- Give at least one search signal:
job_role,technologiesorkeywords. With all three empty the Actor logsโ ๏ธ No structured search criteria providedand falls back to a legacysearch_query/queriesinput, or โ failing that โ a hardcoded default query ("Python Developer" Ahmedabad). You will get results that have nothing to do with your brief. max_profilesis clamped, not validated. Below 1 becomes 1, above 50 becomes 50, each with a warning. Free Apify plans are capped at 5 regardless.locationaccepts a list or a comma-separated string.["Pune","Mumbai"]and"Pune, Mumbai"both work โ the Actor splits on commas.seniorityis an 8-value enum including the empty string:"",Junior,Mid,Senior,Lead,Principal,Staff,Director. It is a search term, not a hard filter โ a "Senior" search can still surface adjacent titles.- Queries are capped at 450 characters. A very long technology list plus many locations gets truncated, silently narrowing what was actually searched. Keep briefs tight, or split them across runs.
- A relaxed fallback query may run automatically. If the strict query is sparse, the Actor re-runs a loosened version โ so some returned profiles can be weaker matches than your brief implies. Check
snippetbefore trusting a match. - The output has four fields.
name,title,profile_url,snippetโ nothing else. There is no email, phone, company, skills, or experience field, and no amount of configuration adds one. titleis the search-result headline, not a verified current job title, andsnippetis the search engine's excerpt โ both can be stale relative to the live profile.- There is no proxy input. The Actor has no
proxyConfigurationfield and uses the container's direct connection. deliveryModedefaults toperProfile, notnone. With a connector selected, a 50-profile run makes 50 connector calls. For dataset-only runs leavemcpConnectorempty or setdeliveryMode: "none".- The dataset is written in one push at the end of the run โ a run that dies mid-search leaves no partial dataset.
dependencies:- rule: "at least one of job_role / technologies / keywords should be set"on_violation: "the Actor warns and falls back to a legacy query input or a hardcoded default - results will not match your brief"- rule: "max_profiles is clamped to 1-50, and to 5 on free plans"note: "out-of-range values warn rather than fail"- rule: "the composed query is truncated at 450 characters"on_violation: "later technologies or locations are silently dropped from the search"- rule: "a relaxed fallback query runs when the primary is sparse"note: "some results may be looser matches than the brief"- rule: "delivery requires mcpConnector AND mcpTool"on_violation: "delivery step is skipped with a warning; the dataset is still written in full"- rule: "mcpArguments must reference {message} for the rendered template to appear anywhere"on_violation: "message body is empty; the tool is still called"- rule: "mcpConnector only resolves when the Actor runs on the Apify platform"on_violation: "local runs log 'APIFY_MCP_PROXY_URL is not set' and skip delivery; the dataset is unaffected"authentication:actor_input: noneplatform: "Apify API token required for API / SDK / MCP invocation"target_site: "no LinkedIn account, cookie or session token required - and none is accepted"
๐ฅ Input Contract
.actor/input_schema.json is authoritative โ if any table on this page disagrees with the schema files, the schema files win.
Search Parameters
| Field | Type | Required | Default | Allowed Values / Format | Example |
|---|---|---|---|---|---|
max_profiles | integer | โ Yes | 10 | 1โ50, clamped; free plans capped at 5 | 20 |
job_role | string | โ ๏ธ Recommended | โ | Free text โ any role, not a fixed list | Java Developer |
seniority | string (enum) | โฌ No | "" | "" ยท Junior ยท Mid ยท Senior ยท Lead ยท Principal ยท Staff ยท Director | Senior |
technologies | array of string | โฌ No | [] | Skills/tools to require in the profile | ["Spring Boot","Kafka","AWS"] |
keywords | string | โฌ No | "" | Extra free-text terms | microservices |
exclude_keywords | array of string | โฌ No | [] | Terms to exclude from results | ["recruiter","intern","manager"] |
location | array of string | string | โฌ No | ["New York"] | List, or comma-separated string | ["Pune","Mumbai","Bangalore"] |
At least one of job_role, technologies or keywords should be set โ see Execution Rule 2.
Legacy inputs (accepted, undeclared)
| Field | Notes |
|---|---|
search_query | A raw query string, used only when no structured signal is given |
queries | Same, as a list โ the first entry is used |
These exist for backwards compatibility with older saved inputs. Prefer the structured fields.
Examples
Technical sourcing across three cities:
{"job_role": "Java Developer","seniority": "Senior","technologies": ["Spring Boot", "Kafka", "AWS"],"keywords": "microservices","exclude_keywords": ["recruiter", "intern", "manager"],"location": ["Pune", "Mumbai", "Bangalore"],"max_profiles": 20,"deliveryMode": "none"}
Broad role sweep, minimal filtering:
{"job_role": "Data Scientist","location": ["Berlin"],"max_profiles": 50,"deliveryMode": "none"}
Stack-first search with no fixed role title:
{"technologies": ["Rust", "WebAssembly"],"keywords": "systems programming","exclude_keywords": ["recruiter", "student"],"location": ["Remote", "Berlin", "Amsterdam"],"max_profiles": 30,"deliveryMode": "none"}
MCP Delivery Configuration (Optional)
Delivery pushes your shortlist into your own tools. It never messages a candidate.
| Parameter | Type | Default | Description |
|---|---|---|---|
mcpConnector | string | "" | Authorized MCP connector on your Apify account (your ATS, Airtable, HubSpot, Notion, Slack, Sheets, Linear, Jira, โฆ). Leave empty for dataset-only runs. |
deliveryMode | enum | perProfile | perProfile (one call per candidate) ยท chunked (split a long shortlist across calls) ยท summary (one digest) ยท none. |
mcpTool | string | "" | Tool name on the connector: create_record, send_message, create_page, create_issue. Run once with a connector selected โ the log lists the connector's available tools. |
mcpArguments | object | {} | Arguments passed to the tool. String leaves support {placeholders}. |
mcpMessageTemplate | string | "" | Template rendered and exposed to mcpArguments as {message}. |
Placeholders by mode:
| Mode | Placeholders |
|---|---|
perProfile | {name}, {title}, {profileUrl}, {snippet}, {profile} (formatted block), {message} |
summary | {profileCount}, {jobRole}, {location}, {profiles} (the whole shortlist), {message} |
chunked | the above, plus {part} and {partCount} โ and {profiles} holds one part |
Chunked delivery truncates each profile block at ~1,800 characters and groups blocks into parts under ~72,000 characters, so Notion-style block and timeout caps are never hit.
One ATS/Airtable record per candidate:
{"job_role": "DevOps Engineer","location": ["Bangalore"],"max_profiles": 25,"mcpConnector": "<your-authorized-airtable-connector>","deliveryMode": "perProfile","mcpTool": "create_record","mcpArguments": {"fields": {"Name": "{name}","Headline": "{title}","LinkedIn": "{profileUrl}","Context": "{snippet}"}}}
One Slack digest per run:
{"job_role": "Machine Learning Engineer","location": ["Remote"],"max_profiles": 30,"mcpConnector": "<your-authorized-slack-connector>","deliveryMode": "summary","mcpTool": "send_message","mcpArguments": { "channel": "#sourcing", "text": "{message}" },"mcpMessageTemplate": "{profileCount} candidates for {jobRole} in {location}:\n\n{profiles}"}
Chunked into Notion pages:
{"job_role": "Frontend Developer","location": ["Pune", "Hyderabad"],"max_profiles": 50,"mcpConnector": "<your-authorized-notion-connector>","deliveryMode": "chunked","mcpTool": "notion-create-pages","mcpArguments": {"parent": { "page_id": "<your-page-id>" },"pages": [{"properties": {"title": "Frontend shortlist (part {part}/{partCount})"},"content": "{profiles}"}]}}
๐ค Output Contract & Data Structure
Storage: Apify Dataset (one JSON object per candidate), written in a single push at the end of the run.
Pagination: limit & offset on the dataset items endpoint.
Duplicates: one record per profile URL within a run. Cross-run deduplication is the consumer's responsibility โ use profile_url as the key.
| Field | Type | Meaning |
|---|---|---|
name | string | Candidate name as it appears in the search result |
title | string | Headline / job title from the search result, not a verified live title |
profile_url | string | Public LinkedIn /in/ URL โ use as the dedupe key |
snippet | string | The search engine's excerpt for that profile โ your best signal for judging relevance |
That is the whole record. There is no email, phone, company, location, skills, education or experience field. If a downstream system needs those, a human or a cookie-based tool has to add them.
Example output item
{"name": "Aditi Ranganathan","title": "Senior Java Developer at FinServe Systems","profile_url": "https://in.linkedin.com/in/aditi-ranganathan-1a2b3c","snippet": "Senior Java Developer with 8 years building Spring Boot microservices and Kafka pipelines on AWS. Pune, Maharashtra, India."}
What a successful run looks like
{"status": "SUCCEEDED","defaultDatasetId": "<datasetId>","stats": { "itemCount": 18 }}
itemCount below max_profiles is normal โ the search engine may simply not have that many indexed matches for a tight brief, or your exclude_keywords filtered some out. itemCount: 0 with No LinkedIn profiles found. in the log is a valid empty result: loosen the brief, widen the locations, or drop a technology. On free plans, itemCount stops at 5.
โถ๏ธ Invocation & Integration
Lifecycle: Discover โ Understand โ Configure โ Execute โ Retrieve โ Validate. Authenticate every call with Authorization: Bearer <APIFY_TOKEN>.
Apify Console
Open the Actor โ Input tab โ enter a Job Role (and optionally seniority, technologies, keywords, exclusions) โ add one or more Locations โ set Max profiles โ (optional) pick an MCP connector, tool and template โ Start โ export from the Dataset tab (JSON, CSV, Excel, XML).
Model Context Protocol (MCP)
claude mcp add --transport http apify \"https://mcp.apify.com?tools=techforce.global/linkedin-candidate-search"
Python (apify-client)
import osfrom apify_client import ApifyClientclient = ApifyClient(os.getenv("APIFY_TOKEN"))run = client.actor("techforce.global/linkedin-candidate-search").call(run_input={"job_role": "Java Developer","seniority": "Senior","technologies": ["Spring Boot", "Kafka"],"exclude_keywords": ["recruiter", "intern"],"location": ["Pune", "Mumbai"],"max_profiles": 20, # 50 is the ceiling; free plans stop at 5"deliveryMode": "none",})items = client.dataset(run["defaultDatasetId"]).list_items().itemsprint(f"{len(items)} candidates")# Four fields only - judge relevance from the snippet.for candidate in items[:5]:print(candidate["name"], "|", candidate["title"])print(" ", candidate["profile_url"])print(" ", candidate["snippet"][:120])
JavaScript / TypeScript (apify-client)
import { ApifyClient } from "apify-client";const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor("techforce.global/linkedin-candidate-search").call({job_role: "Data Scientist",technologies: ["PyTorch", "MLOps"],exclude_keywords: ["recruiter", "student"],location: ["Berlin", "Amsterdam"],max_profiles: 40,deliveryMode: "none",});const { items } = await client.dataset(run.defaultDatasetId).listItems();// The snippet is the only relevance signal you get - keep it in the loop.const strong = items.filter((i) => /pytorch/i.test(i.snippet ?? ""));console.log(`${items.length} candidates, ${strong.length} mentioning PyTorch`);
cURL โ synchronous (returns items directly; 300s limit)
curl -X POST \-H 'Content-Type: application/json' \-H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \-d '{"job_role":"DevOps Engineer","location":["Bangalore"],"max_profiles":10,"deliveryMode":"none"}' \'https://api.apify.com/v2/acts/techforce.global~linkedin-candidate-search/run-sync-get-dataset-items'
cURL โ asynchronous with a spend cap (recommended for production)
# 1. Start execution with a hard spend limitcurl -X POST -H 'Content-Type: application/json' \-H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \-d '{"job_role":"Frontend Developer","technologies":["React","TypeScript"],"location":["Pune","Hyderabad"],"max_profiles":50,"deliveryMode":"none"}' \'https://api.apify.com/v2/acts/techforce.global~linkedin-candidate-search/runs?maxTotalChargeUsd=0.10'# 2. Pollcurl -H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \'https://api.apify.com/v2/actor-runs/<runId>'# 3. Retrieve when SUCCEEDEDcurl -H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \'https://api.apify.com/v2/datasets/<DATASET_ID>/items?clean=true&format=json&limit=1000&offset=0'
โ ๏ธ Autonomous Agent Error Handling & Resolution Matrix
| Error Code | Detection Condition | Underlying Cause | Deterministic Agent Action |
|---|---|---|---|
AUTH_INVALID | HTTP 401 | Missing or malformed APIFY_TOKEN | Abort & Prompt User: request a valid API token. |
RATE_LIMITED | HTTP 429 | Apify API rate / concurrency limits | Retry: exponential backoff (2s, 4s, 8s). |
SYNC_TIMEOUT | HTTP 408 on the sync endpoint | Run exceeded the 300s synchronous ceiling | Reconfigure: switch to async POST /runs + polling. |
MISSING_MAX_PROFILES | Input validation rejection | max_profiles is the one required field | Modify Input: send max_profiles. |
NO_SEARCH_SIGNAL | Log โ ๏ธ No structured search criteria provided; results unrelated to the brief | job_role, technologies and keywords were all empty, so a legacy or default query ran | Modify Input: set at least one of the three. Discard the results. |
CLAMPED_VOLUME | Log exceeds limit. Setting to 50 / too low. Setting to 1 | max_profiles outside 1โ50 | Not an error. Send an in-range value to be explicit. |
FREE_PLAN_CAP | itemCount stops at 5, upgrade banner in the log | Free Apify plan caps the run at 5 profiles | Prompt User: upgrade for full-volume runs. |
QUERY_TRUNCATED | Later technologies/locations appear ignored | The composed query hit the 450-character ceiling | Reconfigure: shorten the brief, or split it across runs. |
FALLBACK_USED | Log shows the fallback query ran; some matches look loose | The strict query was sparse, so a relaxed query ran automatically | Not an error. Filter downstream on snippet. |
EMPTY_RESULTS | SUCCEEDED, itemCount: 0, log No LinkedIn profiles found. | Nothing indexed matched the brief, or exclusions removed everything | Modify Input: widen locations, drop a technology, or relax exclusions. |
CAPTCHA_ENCOUNTERED | Log mentions CAPTCHA / PoW handling | Brave Search served its proof-of-work challenge | Retry: the Actor attempts to handle it; re-run if results are thin. |
NO_CONTACT_DATA | No email / phone in any item | Never returned โ the output is 4 fields | Fix Expectation: enrich elsewhere; do not retry. |
SHALLOW_PROFILE | No skills / experience / education fields | Search-result depth by design | Fix Expectation: use a cookie-based tool if depth is required. |
STALE_TITLE | title disagrees with the live LinkedIn profile | It is the search-engine headline, which can lag | Verify on the profile before acting on it. |
NO_PARTIAL_DATA | FAILED with an empty dataset | Results are pushed once at the end of the run | Retry with a lower max_profiles to shorten the run. |
DELIVERY_SKIPPED | Log no tool name was provided | mcpConnector set but mcpTool empty | Modify Input: set mcpTool; the dataset is already saved. |
DELIVERY_TOOL_UNKNOWN | Log Tool 'x' is not available on this connector | Wrong tool name for that connector | Modify Input: pick a name from the Available tools: list in the same log line. |
DELIVERY_EMPTY | Connector called, body empty | mcpArguments omitted the {message} placeholder | Modify Input: map {message} inside mcpArguments. |
DELIVERY_FLOOD | Dozens of connector calls / connector rate limits | deliveryMode defaults to perProfile | Reconfigure: use summary or chunked. |
MCP_PROXY_MISSING | Log APIFY_MCP_PROXY_URL is not set | Running locally instead of on the platform | Reconfigure: apify push and run on the platform. |
CHARGE_LIMIT | Run aborted before max_profiles reached | maxTotalChargeUsd spend threshold reached | Prompt User: confirm a higher spend cap. |
๐ฃ๏ธ Natural Language โ Actor Mapping
| User says | Intent | Constructed Actor input |
|---|---|---|
| "Find senior Java developers in Pune" | Technical sourcing | {"job_role":"Java Developer","seniority":"Senior","location":["Pune"],"max_profiles":20} |
| "Sourcing React devs in Bangalore and Hyderabad" | Multi-city sourcing | {"job_role":"Frontend Developer","technologies":["React"],"location":["Bangalore","Hyderabad"],"max_profiles":40} |
| "Anyone doing Kafka and Spring Boot microservices" | Stack-first search | {"technologies":["Kafka","Spring Boot"],"keywords":"microservices","max_profiles":30} |
| "Data scientists, but not recruiters or students" | Filtered sourcing | {"job_role":"Data Scientist","exclude_keywords":["recruiter","student"],"max_profiles":25} |
| "Staff-level SREs, remote" | Seniority band | {"job_role":"SRE","seniority":"Staff","location":["Remote"],"max_profiles":20} |
| "Build me a shortlist in Airtable" | ATS/CRM delivery | {"mcpConnector":"airtable","deliveryMode":"perProfile","mcpTool":"create_record"} |
| "Post today's candidates to Slack" | Digest delivery | {"mcpConnector":"slack","deliveryMode":"summary","mcpTool":"send_message"} |
| "Save 50 candidates into Notion without it timing out" | Chunked delivery | {"deliveryMode":"chunked","mcpTool":"notion-create-pages"} with {part}/{partCount} |
| "Get me their email addresses" | Contact enrichment | โ Out of scope โ the output has no contact fields |
| "Pull their full work history and skills" | Profile export | โ Out of scope โ search-result depth only |
| "Message these candidates for me" | Outreach | โ Out of scope โ read-only, no messaging |
| "Use my LinkedIn cookie for better results" | Authenticated scraping | โ Not supported by design โ no cookie is accepted |
| "Search LinkedIn Recruiter / Sales Navigator" | Premium LinkedIn surfaces | โ Out of scope |
| "Find me 500 candidates in one run" | Bulk sourcing | โ ๏ธ Cap is 50 โ loop by location or seniority band |
| "Who are the decision makers at Acme Corp?" | Company-anchored | โก๏ธ Route to LinkedIn Decision Makers |
| "List everyone who works at Acme Corp" | Org mapping | โก๏ธ Route to LinkedIn Employee Scraper |
| "Which engineers does our competitor employ?" | Competitor headcount | โก๏ธ Route to LinkedIn Employee Scraper โ it classifies department and seniority |
Should NOT route here: the employees or decision makers of a named company (โ Decision Makers ยท Employee Scraper) ยท candidate emails or phone numbers ยท full profile exports (skills, education, experience) ยท messaging, connecting or InMail ยท LinkedIn Recruiter or Sales Navigator data ยท anything requiring a LinkedIn session cookie ยท more than 50 profiles in one run ยท automated hiring or screening decisions.
๐งญ Agent Execution & Routing Logic
[Input User Query]โโผ1. Is the goal finding PEOPLE? NO โ Route to a company/lead Actorโ YESโผ1b. What is the search anchored on?โโโ a NAMED COMPANY's decision makers โ Route to LinkedIn Decision Makers Actorโโโ a NAMED COMPANY's whole employee list โ Route to LinkedIn Employee Scraper Actorโโโ a ROLE / tech stack, any employer โ stay hereโผ2. Needs emails / full profiles / messaging? YES โ Abort (none of these are supported)โ NOโผ3. Does the caller expect authenticated LinkedIn data? YES โ Abort and explain: this Actor is cookie-free by designโ NOโผ4. Extract at least ONE search signal from the requestโโโ a role title? โ job_roleโโโ a tech stack? โ technologies[]โโโ other terms? โ keywordsโโโ none of the above? โ STOP and ask the user. Running without a signalfalls back to a hardcoded default query.โผ5. Add the optional narrowing: seniority, exclude_keywords, location[]โ keep the whole brief short - the composed query truncates at 450 charsโผ6. Set max_profiles (REQUIRED, 1-50; free plans stop at 5)โผ7. Volume > 50 candidates? YES โ Loop by location or seniority bandโผ8. Deliver to a connector? YES โ mcpConnector + mcpTool + {message}โ (delivery writes to YOUR tools, never to a candidate)โ NO โ set deliveryMode "none" (it defaults to perProfile)โผ[Execute Apify Actor]โโโโโบ status == "SUCCEEDED" โโโบ itemCount == 0? YES โ brief too tight / nothing indexed โ widen itโ โ NOโ โโโโบ judge each match on `snippet` (a fallback query may have run);โ dedupe on profile_url; expect NO contact fieldsโโโโบ status == "FAILED" โโโบ expect an EMPTY dataset (results are pushed only at the end)
๐ฐ Cost Control & Pricing Transparency
- Pay-per-event pricing: $1.00 per 1,000 results on the entry tier, discounted at higher Apify subscription tiers. No monthly subscription.
- Free Apify plans are capped at 5 profiles per run.
- Autonomous spend caps: pass
maxTotalChargeUsdas a query parameter on the run endpoint for a hard per-execution ceiling.
| Candidates returned | Cost at $1.00 / 1,000 |
|---|---|
| 5 profiles (free-plan cap) | $0.005 |
| 10 profiles (default run) | $0.01 |
| 50 profiles (per-run ceiling) | $0.05 |
| 500 profiles (10 runs) | $0.50 |
| 1,000 profiles (20 runs) | $1.00 |
Platform usage is billed on top at your plan's rates. This Actor drives a real browser through a search engine, so a run's compute-unit cost is dominated by page loads and any CAPTCHA handling rather than by the number of profiles returned โ a tight brief that returns 3 matches can cost nearly as much in platform usage as one returning 50.
Cost levers: keep max_profiles realistic for the brief; prefer one well-specified run over several speculative ones; and set maxTotalChargeUsd.
โ ๏ธ Verify before publishing: the $1.00 / 1,000 figure is taken from the Store listing, which describes a pay-per-event model. This repo contains no
pay_per_event.jsonand the code makes noActor.charge()calls, so billing is configured entirely on the Store listing โ confirm the live event configuration before publishing.
๐ Companion machine-readable files
| File | Purpose |
|---|---|
.actor/actor.json | Identity, version, and an inline dataset view definition (name, title, profile URL, snippet) |
.actor/input_schema.json | Authoritative typed input contract โ the search fields, seniority enum, max_profiles (1โ50, required), deliveryMode enum |
.actor/dataset_schema.json | Standalone dataset view; note that actor.json declares the dataset inline, so that inline block is what the platform reads |
.actor/output_schema.json | Declares where results are stored |
src/main.py | Query composition, Brave Search navigation, CAPTCHA handling, result filtering, free-plan capping, delivery hand-off |
src/delivery.py | MCP connector delivery modes and {placeholder} rendering |
src/connector.py | MCP connector session handling through the Apify MCP Proxy |
Note: this Actor has no pay_per_event.json โ billing is configured entirely on the Store listing.
If any table on this page disagrees with the schema files, the schema files win.
๐ ๏ธ Troubleshooting
| # | Symptom you see | Most likely cause | Fix |
|---|---|---|---|
| 1 | Results have nothing to do with your brief | All of job_role, technologies, keywords were empty, so a legacy or hardcoded default query ran | Set at least one search signal; check the log for โ ๏ธ No structured search criteria provided. |
| 2 | Input rejected | max_profiles is required | Always send it. |
| 3 | Exactly 5 profiles every run | Free Apify plan cap | Upgrade the plan; the log prints the upgrade banner. |
| 4 | Asked for 200, got 50 | max_profiles is clamped to 50 with a warning | Loop by location or seniority band for more. |
| 5 | Later technologies or locations seem ignored | The composed query hit the 450-character ceiling | Shorten the brief, or split it across runs. |
| 6 | Some matches look weak | A relaxed fallback query ran because the strict one was sparse | Expected. Filter downstream on snippet. |
| 7 | Zero results | Nothing indexed matched, or exclude_keywords removed everything | Widen locations, drop a technology, relax exclusions. |
| 8 | No email or phone anywhere | The output is 4 fields โ contact data is never returned | Enrich with a separate tool; this isn't a bug. |
| 9 | No skills / experience / education | Search-result depth by design | Use a cookie-based tool if you need full profiles. |
| 10 | title disagrees with the live profile | It is the search-engine headline, which can be stale | Verify on the profile before acting. |
| 11 | Some profile_urls are country subdomains (in.linkedin.com) | That is how the search engine indexed them | Normalise downstream if your ATS needs a canonical form. |
| 12 | Log mentions a CAPTCHA | Brave Search served its proof-of-work challenge | The Actor tries to handle it; re-run if results are thin. |
| 13 | Empty dataset after a failed run | Results are pushed once at the end โ nothing is streamed | Lower max_profiles and retry. |
| 14 | Run is slow for few results | A browser plus search navigation and CAPTCHA handling dominates the time | Expected; cost tracks page loads more than result count. |
| 15 | HTTP 408 on run-sync-get-dataset-items | The synchronous endpoint has a hard 300-second ceiling | Use async: POST /runs โ poll โ fetch dataset. |
| 16 | Dozens of connector calls | deliveryMode defaults to perProfile | Switch to summary or chunked. |
| 17 | Slack / Airtable / Notion received nothing | Delivery needs both mcpConnector and mcpTool | Set both. The dataset is still written in full. |
| 18 | Connector fired but the message body is empty | mcpArguments did not reference {message} | Map {message} in mcpArguments and populate mcpMessageTemplate. |
| 19 | Log: APIFY_MCP_PROXY_URL is not set | You ran locally; connectors only resolve on the platform | Deploy with apify push and run on the platform. |
| 20 | HTTP 401 / 403 | Missing, expired, or malformed APIFY_TOKEN | Regenerate in Apify Console โ Settings โ API & Integrations. |
| 21 | HTTP 429 | Apify account concurrency / rate limits | Retry with exponential backoff (2s โ 4s โ 8s). |
Diagnostic checklist before opening an issue
- Baseline run.
job_role= a common title, one broadlocation,max_profiles: 5, no technologies or exclusions,deliveryMode: "none". - Results returned? If yes, re-add technologies, exclusions and volume one step at a time โ over-narrow briefs are the most common cause of thin results.
- Check the log for
โ ๏ธ No structured search criteria provided, the printed primary and fallback queries, and any CAPTCHA mention. The printed query tells you exactly what was searched. - Compare the query against your brief โ if terms are missing, you hit the 450-character ceiling.
- Confirm expectations โ no contact data and no deep profile fields exist at any setting.
If the issue survives all five steps, open an Issues ticket on the Actor page (or email support) with the run ID, the exact input JSON, and the primary/fallback queries from the log.
โ FAQ
Setup & access
Do I need a LinkedIn account, cookie or li_at token?
No โ and none is accepted. That is the point of this Actor: it reads a search engine's index of public profile pages, so your LinkedIn account is never involved and there is nothing to re-paste when a cookie rotates.
Where does the data actually come from?
Brave Search results for site:linkedin.com/in queries. The Actor composes the operators, runs the search in a real browser, and parses the results โ it does not call a LinkedIn API and does not open profiles behind LinkedIn's login.
Is this the official LinkedIn API?
No. This is an independent Actor and is not affiliated with, endorsed by, or sponsored by LinkedIn or Brave.
Can I plug it into Claude, Cursor, or a LangChain agent?
Yes โ it is a native MCP tool:
claude mcp add --transport http apify "https://mcp.apify.com?tools=techforce.global/linkedin-candidate-search"
Search behaviour
Do I have to write boolean search operators?
No. Fill in the structured fields and the Actor composes the query โ including the site: operator and the exclusions. The composed primary and fallback queries are printed in the log so you can see exactly what ran.
What happens if I leave the search fields empty?
The Actor warns and falls back to a legacy search_query input or a hardcoded default query, so you get results unrelated to your brief. Always set at least one of job_role, technologies or keywords.
Why did I get candidates that don't match my brief?
Most likely the relaxed fallback query ran because the strict one returned too little โ or your brief was long enough to hit the 450-character query ceiling. Both appear in the log.
How many candidates can one run return?
Up to 50, and 5 on free plans. Loop by location or seniority band for more.
Output & data
Why are there only four fields?
Because search results only contain four useful things: the name, the headline, the URL and the snippet. Anything deeper lives behind LinkedIn's auth wall, which this Actor deliberately doesn't touch.
Can I get candidate emails or phone numbers?
No, at any setting. Use a dedicated enrichment tool on the profile_url if you need contact data โ and mind the applicable privacy rules when you do.
Is title the candidate's current job?
It is their search-result headline, which can lag the live profile. Verify before acting on it.
Pricing
How is this billed?
Pay-per-event, $1.00 per 1,000 results, less on higher Apify subscription tiers. Apify platform usage is billed separately.
Why did a run with 3 results cost about as much as one with 50?
Because platform usage tracks browser page loads and CAPTCHA handling, not the number of matches. A very narrow brief still costs a full search.
How do I guarantee an agent never overspends?
Pass maxTotalChargeUsd as a query parameter on the run endpoint โ a hard per-execution ceiling.
๐ Related Actors
LinkedIn people, cookie-free โ the same family
All three read public search results rather than an authenticated LinkedIn session. The difference is what you anchor the search on: this Actor starts from a role, the other two start from a company.
| Actor | Anchored on | Best for | Why pick it over this one |
|---|---|---|---|
| This Actor | A role / tech stack, across companies | "Find senior Java developers in Pune" | Candidate sourcing when you don't care where they work |
| LinkedIn Decision Makers | One named company | "Who decides at Acme Corp?" | Account-based sales and partnership outreach โ returns name, URL, location, job title and headline. 1โ20 per run (free plans 2), from $5.00 / 1,000 |
| LinkedIn Employee Scraper | One named company | "Map everyone at Acme Corp" | Org mapping and competitor headcount โ adds department (Technology, Sales, Marketing, Finance, HR, Product, Operations, Legal, Business) and seniority level classification. 1โ50 per run (free plans 2), $8.40 / 1,000 |
Pick by intent: a role, any employer โ this Actor ยท the buyers at one company โ Decision Makers ยท the whole org chart of one company โ Employee Scraper.
โน๏ธ Both company-anchored siblings return a couple more fields than this Actor (location and headline; the Employee Scraper also derives department and seniority). If you need those on a role search, run this Actor for discovery and one of them for the companies worth mapping.
Other lead sources
| Actor | Use it for |
|---|---|
| Google Maps Business Leads & Sales Intelligence | Sourcing companies rather than people โ with emails, website grades and a pitch playbook per lead |
| TopDevelopers Scraper โ IT Agency Leads | Agency and vendor sourcing with scraped company emails and client reviews |
| Advanced Website Crawling Actor | Crawl a candidate's portfolio site or a company careers page for context |
Suggested pipeline patterns
๐ฏ Sourcing sprint
One run per city for the same role โ merge and dedupe on profile_url โ deliveryMode: "perProfile" into your ATS โ a recruiter reviews each snippet before any outreach.
๐ Talent-market mapping Same role across several locations and seniority bands โ compare how many indexed profiles each combination returns โ prioritise the markets with real supply.
๐ง Shortlist triage
deliveryMode: "summary" into Slack for the hiring manager โ they pick the profiles worth a real look โ only those get manual research.
Browse all Actors by Techforce Global at scraper.techforce.global.
๐ Compliance & Data Privacy
This is an independent Actor. It is not affiliated with, endorsed by, or sponsored by LinkedIn or Brave Search. "LinkedIn" is a trademark of LinkedIn Corporation.
This Actor reads publicly indexed search results for public LinkedIn profile pages. It does not log in, does not use or accept a session cookie, and does not access anything behind LinkedIn's authentication.
This output is personal data about identifiable individuals, and recruitment is one of the most regulated uses of it. Three things follow:
- You are a data controller the moment you store this. GDPR, UK GDPR and CCPA/CPRA apply to a candidate shortlist. Have a lawful basis (legitimate interest is the usual route for recruitment, and it requires a balancing assessment), keep only what you need, set a retention period, and be ready to honour access and erasure requests. Several jurisdictions expect you to notify a candidate when you process their data for recruitment.
- Do not make automated decisions with it. This is a discovery aid: a four-field search snippet is nowhere near a basis for screening someone in or out, and automated candidate screening carries specific obligations under GDPR Art. 22 and rules such as NYC Local Law 144 for automated employment decision tools. Keep a human making the call, and keep the criteria job-related.
- Respect the platforms' terms. Check LinkedIn's and Brave's terms of service for your use case, keep volumes reasonable, and don't route around technical measures.
You are responsible for ensuring your use complies with all applicable employment, anti-discrimination and data-protection law.
๐ Support & Custom Pipeline Engineering
Need scheduled sourcing runs, ATS integration, contact enrichment on top of the shortlist, or a full recruiting data pipeline?
- Email: bhavin.shah@techforceglobal.com
- Custom Enterprise Integrations: Book a 15-Minute Technical Consultation
- Maintained by: Techforce Global โ Specialists in High-Performance Web Scrapers and Agentic Workflows.
Made with โค๏ธ by Techforce Global Specialists in High-Performance Talent & Lead Data Extraction and AI Automation.
๐ท๏ธ Structured data for search & AI discovery
{"@context": "https://schema.org","@type": "SoftwareApplication","name": "LinkedIn Candidate Search (No Cookies) โ Talent Sourcing API","applicationCategory": "BusinessApplication","operatingSystem": "Cloud (Apify platform)","description": "Builds a candidate shortlist of public LinkedIn profiles from structured hiring criteria โ job role, seniority, technologies, keywords, exclusions and locations โ by composing search operators and reading Brave Search results, with no LinkedIn account or session cookie required, and optional delivery to an ATS, Airtable, HubSpot, Notion, Slack, Google Sheets, or any authorized MCP connector.","url": "https://apify.com/techforce.global/linkedin-candidate-search","offers": {"@type": "Offer","price": "1.00","priceCurrency": "USD","description": "Per 1,000 results, pay-per-event"}}