LinkedIn Candidate Search (No Cookies) - Talent Sourcing API avatar

LinkedIn Candidate Search (No Cookies) - Talent Sourcing API

Pricing

from $1.00 / 1,000 results

Go to Apify Store
LinkedIn Candidate Search (No Cookies) - Talent Sourcing API

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

Techforce Global

Maintained by Community

Actor stats

2

Bookmarked

182

Total users

18

Monthly active users

19 hours

Issues response

12 days ago

Last modified

Share

LinkedIn Candidate Search (No Cookies) โ€” Talent Sourcing API for Recruiters

Apify Actor MCP Ready Pricing Target

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

โ–ถ๏ธ Watch: How To Search Candidate on LinkedIn | Automate Candidate Sourcing | LinkedIn Data Workflow Demo


Most LinkedIn tooling asks you to paste your li_at cookie, which means running searches as your own logged-in account.

Feature / CapabilityCookie-based LinkedIn scrapersThis 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 modelVariesPay-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: 5
description: "Values outside the range are clamped with a warning rather than rejected."
limits:
max_profiles_per_run: 50
free_plan_cap: 5
max_query_length_chars: 450
output_fields_per_profile: 4
queries_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 profiles
matching a role, seniority, tech stack and set of locations - without
handing any tool your LinkedIn cookie.
when_not_to_use: >
You need full profile exports, candidate emails or phone numbers, messaging
or connection requests, LinkedIn Recruiter data, or more than 50 profiles
per 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_keywords and location; the Actor builds the site:linkedin.com/in boolean 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 location as 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

ScenarioFit
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)

  1. max_profiles is 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.
  2. Give at least one search signal: job_role, technologies or keywords. With all three empty the Actor logs โš ๏ธ No structured search criteria provided and falls back to a legacy search_query / queries input, or โ€” failing that โ€” a hardcoded default query ("Python Developer" Ahmedabad). You will get results that have nothing to do with your brief.
  3. max_profiles is clamped, not validated. Below 1 becomes 1, above 50 becomes 50, each with a warning. Free Apify plans are capped at 5 regardless.
  4. location accepts a list or a comma-separated string. ["Pune","Mumbai"] and "Pune, Mumbai" both work โ€” the Actor splits on commas.
  5. seniority is 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.
  6. 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.
  7. 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 snippet before trusting a match.
  8. 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.
  9. title is the search-result headline, not a verified current job title, and snippet is the search engine's excerpt โ€” both can be stale relative to the live profile.
  10. There is no proxy input. The Actor has no proxyConfiguration field and uses the container's direct connection.
  11. deliveryMode defaults to perProfile, not none. With a connector selected, a 50-profile run makes 50 connector calls. For dataset-only runs leave mcpConnector empty or set deliveryMode: "none".
  12. 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: none
platform: "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

FieldTypeRequiredDefaultAllowed Values / FormatExample
max_profilesintegerโœ… Yes101โ€“50, clamped; free plans capped at 520
job_rolestringโš ๏ธ Recommendedโ€”Free text โ€” any role, not a fixed listJava Developer
senioritystring (enum)โฌœ No"""" ยท Junior ยท Mid ยท Senior ยท Lead ยท Principal ยท Staff ยท DirectorSenior
technologiesarray of stringโฌœ No[]Skills/tools to require in the profile["Spring Boot","Kafka","AWS"]
keywordsstringโฌœ No""Extra free-text termsmicroservices
exclude_keywordsarray of stringโฌœ No[]Terms to exclude from results["recruiter","intern","manager"]
locationarray 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)

FieldNotes
search_queryA raw query string, used only when no structured signal is given
queriesSame, 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.

ParameterTypeDefaultDescription
mcpConnectorstring""Authorized MCP connector on your Apify account (your ATS, Airtable, HubSpot, Notion, Slack, Sheets, Linear, Jira, โ€ฆ). Leave empty for dataset-only runs.
deliveryModeenumperProfileperProfile (one call per candidate) ยท chunked (split a long shortlist across calls) ยท summary (one digest) ยท none.
mcpToolstring""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.
mcpArgumentsobject{}Arguments passed to the tool. String leaves support {placeholders}.
mcpMessageTemplatestring""Template rendered and exposed to mcpArguments as {message}.

Placeholders by mode:

ModePlaceholders
perProfile{name}, {title}, {profileUrl}, {snippet}, {profile} (formatted block), {message}
summary{profileCount}, {jobRole}, {location}, {profiles} (the whole shortlist), {message}
chunkedthe 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.

FieldTypeMeaning
namestringCandidate name as it appears in the search result
titlestringHeadline / job title from the search result, not a verified live title
profile_urlstringPublic LinkedIn /in/ URL โ€” use as the dedupe key
snippetstringThe 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 os
from apify_client import ApifyClient
client = 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().items
print(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'
# 1. Start execution with a hard spend limit
curl -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. Poll
curl -H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \
'https://api.apify.com/v2/actor-runs/<runId>'
# 3. Retrieve when SUCCEEDED
curl -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 CodeDetection ConditionUnderlying CauseDeterministic Agent Action
AUTH_INVALIDHTTP 401Missing or malformed APIFY_TOKENAbort & Prompt User: request a valid API token.
RATE_LIMITEDHTTP 429Apify API rate / concurrency limitsRetry: exponential backoff (2s, 4s, 8s).
SYNC_TIMEOUTHTTP 408 on the sync endpointRun exceeded the 300s synchronous ceilingReconfigure: switch to async POST /runs + polling.
MISSING_MAX_PROFILESInput validation rejectionmax_profiles is the one required fieldModify Input: send max_profiles.
NO_SEARCH_SIGNALLog โš ๏ธ No structured search criteria provided; results unrelated to the briefjob_role, technologies and keywords were all empty, so a legacy or default query ranModify Input: set at least one of the three. Discard the results.
CLAMPED_VOLUMELog exceeds limit. Setting to 50 / too low. Setting to 1max_profiles outside 1โ€“50Not an error. Send an in-range value to be explicit.
FREE_PLAN_CAPitemCount stops at 5, upgrade banner in the logFree Apify plan caps the run at 5 profilesPrompt User: upgrade for full-volume runs.
QUERY_TRUNCATEDLater technologies/locations appear ignoredThe composed query hit the 450-character ceilingReconfigure: shorten the brief, or split it across runs.
FALLBACK_USEDLog shows the fallback query ran; some matches look looseThe strict query was sparse, so a relaxed query ran automaticallyNot an error. Filter downstream on snippet.
EMPTY_RESULTSSUCCEEDED, itemCount: 0, log No LinkedIn profiles found.Nothing indexed matched the brief, or exclusions removed everythingModify Input: widen locations, drop a technology, or relax exclusions.
CAPTCHA_ENCOUNTEREDLog mentions CAPTCHA / PoW handlingBrave Search served its proof-of-work challengeRetry: the Actor attempts to handle it; re-run if results are thin.
NO_CONTACT_DATANo email / phone in any itemNever returned โ€” the output is 4 fieldsFix Expectation: enrich elsewhere; do not retry.
SHALLOW_PROFILENo skills / experience / education fieldsSearch-result depth by designFix Expectation: use a cookie-based tool if depth is required.
STALE_TITLEtitle disagrees with the live LinkedIn profileIt is the search-engine headline, which can lagVerify on the profile before acting on it.
NO_PARTIAL_DATAFAILED with an empty datasetResults are pushed once at the end of the runRetry with a lower max_profiles to shorten the run.
DELIVERY_SKIPPEDLog no tool name was providedmcpConnector set but mcpTool emptyModify Input: set mcpTool; the dataset is already saved.
DELIVERY_TOOL_UNKNOWNLog Tool 'x' is not available on this connectorWrong tool name for that connectorModify Input: pick a name from the Available tools: list in the same log line.
DELIVERY_EMPTYConnector called, body emptymcpArguments omitted the {message} placeholderModify Input: map {message} inside mcpArguments.
DELIVERY_FLOODDozens of connector calls / connector rate limitsdeliveryMode defaults to perProfileReconfigure: use summary or chunked.
MCP_PROXY_MISSINGLog APIFY_MCP_PROXY_URL is not setRunning locally instead of on the platformReconfigure: apify push and run on the platform.
CHARGE_LIMITRun aborted before max_profiles reachedmaxTotalChargeUsd spend threshold reachedPrompt User: confirm a higher spend cap.

๐Ÿ—ฃ๏ธ Natural Language โ†’ Actor Mapping

User saysIntentConstructed 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 signal
falls 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 maxTotalChargeUsd as a query parameter on the run endpoint for a hard per-execution ceiling.
Candidates returnedCost 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.json and the code makes no Actor.charge() calls, so billing is configured entirely on the Store listing โ€” confirm the live event configuration before publishing.


๐Ÿ” Companion machine-readable files

FilePurpose
.actor/actor.jsonIdentity, version, and an inline dataset view definition (name, title, profile URL, snippet)
.actor/input_schema.jsonAuthoritative typed input contract โ€” the search fields, seniority enum, max_profiles (1โ€“50, required), deliveryMode enum
.actor/dataset_schema.jsonStandalone dataset view; note that actor.json declares the dataset inline, so that inline block is what the platform reads
.actor/output_schema.jsonDeclares where results are stored
src/main.pyQuery composition, Brave Search navigation, CAPTCHA handling, result filtering, free-plan capping, delivery hand-off
src/delivery.pyMCP connector delivery modes and {placeholder} rendering
src/connector.pyMCP 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 seeMost likely causeFix
1Results have nothing to do with your briefAll of job_role, technologies, keywords were empty, so a legacy or hardcoded default query ranSet at least one search signal; check the log for โš ๏ธ No structured search criteria provided.
2Input rejectedmax_profiles is requiredAlways send it.
3Exactly 5 profiles every runFree Apify plan capUpgrade the plan; the log prints the upgrade banner.
4Asked for 200, got 50max_profiles is clamped to 50 with a warningLoop by location or seniority band for more.
5Later technologies or locations seem ignoredThe composed query hit the 450-character ceilingShorten the brief, or split it across runs.
6Some matches look weakA relaxed fallback query ran because the strict one was sparseExpected. Filter downstream on snippet.
7Zero resultsNothing indexed matched, or exclude_keywords removed everythingWiden locations, drop a technology, relax exclusions.
8No email or phone anywhereThe output is 4 fields โ€” contact data is never returnedEnrich with a separate tool; this isn't a bug.
9No skills / experience / educationSearch-result depth by designUse a cookie-based tool if you need full profiles.
10title disagrees with the live profileIt is the search-engine headline, which can be staleVerify on the profile before acting.
11Some profile_urls are country subdomains (in.linkedin.com)That is how the search engine indexed themNormalise downstream if your ATS needs a canonical form.
12Log mentions a CAPTCHABrave Search served its proof-of-work challengeThe Actor tries to handle it; re-run if results are thin.
13Empty dataset after a failed runResults are pushed once at the end โ€” nothing is streamedLower max_profiles and retry.
14Run is slow for few resultsA browser plus search navigation and CAPTCHA handling dominates the timeExpected; cost tracks page loads more than result count.
15HTTP 408 on run-sync-get-dataset-itemsThe synchronous endpoint has a hard 300-second ceilingUse async: POST /runs โ†’ poll โ†’ fetch dataset.
16Dozens of connector callsdeliveryMode defaults to perProfileSwitch to summary or chunked.
17Slack / Airtable / Notion received nothingDelivery needs both mcpConnector and mcpToolSet both. The dataset is still written in full.
18Connector fired but the message body is emptymcpArguments did not reference {message}Map {message} in mcpArguments and populate mcpMessageTemplate.
19Log: APIFY_MCP_PROXY_URL is not setYou ran locally; connectors only resolve on the platformDeploy with apify push and run on the platform.
20HTTP 401 / 403Missing, expired, or malformed APIFY_TOKENRegenerate in Apify Console โ†’ Settings โ†’ API & Integrations.
21HTTP 429Apify account concurrency / rate limitsRetry with exponential backoff (2s โ†’ 4s โ†’ 8s).

Diagnostic checklist before opening an issue

  1. Baseline run. job_role = a common title, one broad location, max_profiles: 5, no technologies or exclusions, deliveryMode: "none".
  2. 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.
  3. 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.
  4. Compare the query against your brief โ€” if terms are missing, you hit the 450-character ceiling.
  5. 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.


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.

ActorAnchored onBest forWhy pick it over this one
This ActorA role / tech stack, across companies"Find senior Java developers in Pune"Candidate sourcing when you don't care where they work
LinkedIn Decision MakersOne 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 ScraperOne 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

ActorUse it for
Google Maps Business Leads & Sales IntelligenceSourcing companies rather than people โ€” with emails, website grades and a pitch playbook per lead
TopDevelopers Scraper โ€” IT Agency LeadsAgency and vendor sourcing with scraped company emails and client reviews
Advanced Website Crawling ActorCrawl 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?


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"
}
}