How to Search LinkedIn People Without API (No OAuth Needed)
Learn how to search LinkedIn profiles and extract people data without using the official API. Includes scraping, automation, and alternative methods.

How to Search LinkedIn People Without API (No OAuth Needed)
LinkedIn is the world's largest professional network, making it an invaluable resource for recruiters, sales teams, and data professionals looking to find and connect with specific individuals. Whether you're building a recruiting platform, enriching CRM data, or conducting market research, the ability to search for people on LinkedIn programmatically is crucial.
However, LinkedIn's official People Search API has become increasingly restrictive, requiring OAuth authentication, partnership approvals, and offering limited search capabilities. This comprehensive guide explores how to search LinkedIn for people without using the official API, leveraging scraping methods and alternative solutions that bypass these restrictions.
Table of Contents
- Limitations of LinkedIn's Official People Search API
- How to Search LinkedIn Without Using API
- Using a LinkedIn Search Scraper API
- Method Comparison
- Best Practices for LinkedIn People Search Automation
- Use Cases
- Legal and Ethical Considerations
- Conclusion
Limitations of LinkedIn's Official People Search API
LinkedIn once offered a People Search API that allowed developers to query member profiles with various filters. However, access to this functionality has been dramatically curtailed over the years.
Current State of LinkedIn's Official API
What Changed:
- Deprecated Features: LinkedIn removed public access to most search endpoints in 2015
- Partner-Only Access: Search functionality now requires LinkedIn partnership status
- OAuth Required: All requests must use OAuth 2.0 with user authentication
- Limited Query Filters: Even with access, available search parameters are restricted
- Rate Limiting: Strict quotas on number of searches per day
Major Restrictions
- No Public Access: The People Search API is not available to general developers
- Application Approval: Requires lengthy approval process with no guarantee
- Usage Constraints: Limited to specific use cases approved by LinkedIn
- Data Restrictions: Cannot store or cache search results long-term
- Cost: Enterprise pricing for partners with significant usage
Why Developers Need Alternatives
Given these limitations, most developers and businesses cannot:
- Build recruiting tools that search for candidates
- Create lead generation platforms that find prospects
- Develop CRM enrichment features that match contacts
- Conduct market research on professional demographics
This has created a strong demand for alternative methods to search LinkedIn people without requiring official API access.
How to Search LinkedIn Without Using API
There are several approaches to searching LinkedIn for people programmatically without using the official API:
1. Manual LinkedIn Search URLs
LinkedIn's search functionality works through URL parameters that you can construct and access directly.
Example Search URL Structure:
https://www.linkedin.com/search/results/people/?keywords={search_term}&geoUrn={location_code}
Common URL Parameters:
keywords: General search term (name, title, company)firstName: Filter by first namelastName: Filter by last nametitle: Job title filtercompany: Current or past companyschool: Educational institutiongeoUrn: Geographic location codenetwork: Connection degree (1st, 2nd, 3rd)
Example URLs:
# Search for software engineers in San Francisco
https://www.linkedin.com/search/results/people/?keywords=software%20engineer&geoUrn=urn:li:geo:90000084
# Search by name and title
https://www.linkedin.com/search/results/people/?firstName=john&lastName=smith&title=manager
# Search by company
https://www.linkedin.com/search/results/people/?company=microsoft
2. Web Scraping with Automation Tools
Use headless browsers to programmatically access LinkedIn search pages and extract results.
Popular Tools:
- Puppeteer (Node.js): Chromium-based automation
- Playwright (Node.js/Python): Cross-browser automation
- Selenium (Multiple languages): Web testing framework
- Scrapy (Python): Web scraping framework
Challenges:
- Requires handling cookies and sessions
- Must bypass LinkedIn's anti-bot detection
- Needs proxy rotation for scale
- Maintenance required when HTML structure changes
3. Third-Party LinkedIn Scraper APIs
Specialized APIs that handle the complexity of scraping LinkedIn search results, providing clean, structured data through simple REST endpoints.
Advantages:
- No OAuth required
- Pre-built infrastructure handles anti-bot measures
- Structured JSON responses
- Pay-per-use pricing models
- Maintained by the provider
This is often the most practical solution for developers and businesses.
Using a LinkedIn Search Scraper API
For most use cases, leveraging a specialized LinkedIn scraper API provides the best balance of ease of use, reliability, and scalability.
How It Works
LinkedIn scraper APIs provide REST endpoints that accept search parameters and return structured profile data:
- Make API Request: Send search criteria (name, title, location, etc.)
- API Handles Scraping: Service handles headless browsing and data extraction
- Receive Structured Data: Get JSON response with profile information
- Paginate Results: Access additional pages for large result sets
Available Search Parameters
The LinkedIn people search API supports a comprehensive set of optional parameters for precise filtering:
| Parameter | Type | Description | Example |
|---|---|---|---|
name |
String | Search keyword for people (general search) | "john" |
first_name |
String | Filter by first name | "john" |
last_name |
String | Filter by last name | "smith" |
title |
String | Filter by job title or headline | "software engineer" |
company |
String | Filter by company name | "microsoft" |
school |
String | Filter by educational institution | "stanford university" |
geocode_location |
String | Geographical code for location-based search. find location code here | "103644278" (United States) |
current_company |
String | Filter by current company ID | Company ID from LinkedIn |
profile_language |
String | Filter by profile language | "en", "es", "fr" |
industry |
String | Filter by industry ID | Industry ID from LinkedIn |
service_category |
String | Filter by service category ID | Service category ID |
page |
Number | Page number for pagination (default: 1) | 1, 2, 3 |
Note: All parameters are optional. You can combine multiple parameters for more precise search results.
Code Example: LinkedIn People Search
Here's a practical example using a LinkedIn scraper API with Node.js:
const axios = require("axios");
async function searchLinkedInPeople(searchParams) {
const options = {
method: "GET",
url: "https://fresh-linkedin-scraper-api.p.rapidapi.com/api/v1/search/people",
params: {
// Basic search parameters
name: searchParams.name, // General keyword search
first_name: searchParams.firstName, // First name filter
last_name: searchParams.lastName, // Last name filter
// Professional filters
title: searchParams.title, // Job title
company: searchParams.company, // Company name
current_company: searchParams.currentCompany, // Company ID
// Education filter
school: searchParams.school, // Educational institution
// Location and demographics
geocode_location: searchParams.geocodeLocation, // Geographic code
profile_language: searchParams.profileLanguage, // Profile language
// Industry and service
industry: searchParams.industry, // Industry ID
service_category: searchParams.serviceCategory, // Service category ID
// Pagination
page: searchParams.page || 1 // Page number (default: 1)
},
headers: {
"x-rapidapi-key": "YOUR_API_KEY",
"x-rapidapi-host": "fresh-linkedin-scraper-api.p.rapidapi.com"
}
};
try {
const response = await axios.request(options);
if (response.data.success) {
return {
success: true,
results: response.data.data,
pagination: {
currentPage: response.data.page,
total: response.data.total,
hasMore: response.data.has_more
},
processTime: response.data.process_time,
cost: response.data.cost
};
} else {
throw new Error(response.data.message || "Search failed");
}
} catch (error) {
console.error("LinkedIn search error:", error);
throw error;
}
}
// Example 1: Search by name and title
searchLinkedInPeople({
firstName: "john",
lastName: "smith",
title: "software engineer",
page: 1
})
.then(results => {
console.log(`Found ${results.pagination.total} total results`);
console.log(`Showing page ${results.pagination.currentPage}`);
results.results.forEach(person => {
console.log(`\nName: ${person.full_name}`);
console.log(`Title: ${person.title}`);
console.log(`Location: ${person.location}`);
console.log(`Profile: ${person.url}`);
});
})
.catch(err => console.error(err));
// Example 2: Search by company and location
searchLinkedInPeople({
company: "google",
geocodeLocation: "103644278", // United States
title: "product manager",
page: 1
})
.then(results => {
console.log(`Found ${results.results.length} Google Product Managers`);
});
// Example 3: Search by education
searchLinkedInPeople({
school: "stanford university",
title: "data scientist",
page: 1
})
.then(results => {
console.log(`Found ${results.results.length} Stanford Data Scientists`);
});
Response Structure
The API returns structured data for each profile found:
{
"success": true,
"message": "success",
"process_time": 884,
"data": [
{
"id": "478387397",
"urn": "ACoAAByDnMUBr75NmvcrLQrNyMolO141Kg7TD_I",
"url": "https://www.linkedin.com/in/john-heyer-685264114",
"public_identifier": "john-heyer-685264114",
"full_name": "John Heyer",
"title": "ML @ depthfirst | ex Scale / Amazon / MIT",
"location": "Boston, MA",
"is_premium": false,
"avatar": [
{
"width": 100,
"height": 100,
"url": "https://media.licdn.com/dms/image/...",
"expires_at": 1752105600000
}
],
"services": []
},
{
"id": "426203025",
"urn": "ACoAABlnV5EBp1Z8-e9CYtCdibCRxSLDC5WdF6M",
"url": "https://www.linkedin.com/in/john-finberg",
"public_identifier": "john-finberg",
"full_name": "John Finberg",
"title": "CS Master's @ Brown | Former YC Founder",
"location": "San Francisco, CA",
"is_premium": false,
"avatar": [
{
"width": 100,
"height": 100,
"url": "https://media.licdn.com/dms/image/...",
"expires_at": 1752105600000
}
],
"services": []
}
],
"cost": 1,
"page": 1,
"total": 2173900,
"has_more": true
}
Key Response Fields
- id: LinkedIn's internal user ID
- urn: LinkedIn's URN identifier
- url: Full LinkedIn profile URL
- public_identifier: URL slug for the profile
- full_name: Person's display name
- title: Current job title/headline
- location: Geographic location
- is_premium: Whether the user has LinkedIn Premium
- avatar: Profile picture URLs in various sizes
- page: Current page number
- total: Total number of results found
- has_more: Whether more pages are available
Advanced Search Example
Here's a more sophisticated search function with filtering and pagination:
async function findCandidates(criteria) {
const allResults = [];
let currentPage = 1;
let hasMore = true;
while (hasMore && currentPage <= criteria.maxPages) {
const response = await searchLinkedInPeople({
title: criteria.jobTitle,
company: criteria.company,
school: criteria.school,
geocodeLocation: criteria.geocodeLocation,
industry: criteria.industry,
profileLanguage: criteria.profileLanguage,
page: currentPage
});
// Filter results based on criteria
const filtered = response.results.filter(person => {
// Only include people with certain keywords in title
if (criteria.mustInclude) {
return criteria.mustInclude.some(keyword =>
person.title.toLowerCase().includes(keyword.toLowerCase())
);
}
return true;
});
allResults.push(...filtered);
hasMore = response.pagination.hasMore;
currentPage++;
// Rate limiting - wait 2 seconds between requests
if (hasMore && currentPage <= criteria.maxPages) {
await new Promise(resolve => setTimeout(resolve, 2000));
}
}
return allResults;
}
// Usage: Find senior software engineers in United States from Stanford
findCandidates({
jobTitle: "software engineer",
geocodeLocation: "103644278", // United States
school: "stanford university",
mustInclude: ["senior", "lead", "principal"],
maxPages: 5
})
.then(candidates => {
console.log(`Found ${candidates.length} qualified candidates`);
candidates.forEach(c => {
console.log(`${c.full_name} - ${c.title} - ${c.location}`);
});
});
Method Comparison
Here's a comprehensive comparison of different approaches to searching LinkedIn people:
| Method | Pros | Cons | Best For |
|---|---|---|---|
| Official LinkedIn API | - Official, stable- Structured data- Low risk of blocking | - Requires partnership- OAuth complexity- Very limited access- High cost | Enterprise partners with LinkedIn relationships |
| LinkedIn Scraper API | - No OAuth needed- Easy integration- Flexible search- Maintained by provider- Affordable | - Costs per request- Violates LinkedIn ToS | Most developers and businesses needing scalable search |
| Custom Web Scraping | - Full control- No per-request costs- Customizable | - High maintenance- Anti-bot challenges- Slow to build- Requires proxies | Teams with scraping expertise and time to build |
| Manual LinkedIn Search | - Free- No coding needed | - Not scalable- Time-consuming- No automation | Small, one-off searches |
Cost Comparison
LinkedIn Scraper API:
- Pay-per-request model (typically $0.001-$0.01 per search)
- No infrastructure costs
- Predictable pricing
Custom Scraping:
- Server/proxy costs ($100-$1000+/month)
- Development time (weeks to months)
- Ongoing maintenance
Official API:
- Enterprise pricing (often $10,000+/year)
- Limited quotas
- Partnership requirements
Best Practices for LinkedIn People Search Automation
1. Implement Smart Rate Limiting
Avoid overwhelming LinkedIn's servers and reduce blocking risk:
class RateLimiter {
constructor(requestsPerMinute) {
this.requestsPerMinute = requestsPerMinute;
this.queue = [];
this.processing = false;
}
async execute(fn) {
return new Promise((resolve, reject) => {
this.queue.push({ fn, resolve, reject });
if (!this.processing) {
this.processQueue();
}
});
}
async processQueue() {
this.processing = true;
while (this.queue.length > 0) {
const { fn, resolve, reject } = this.queue.shift();
try {
const result = await fn();
resolve(result);
} catch (error) {
reject(error);
}
// Wait between requests
const delay = 60000 / this.requestsPerMinute;
await new Promise(r => setTimeout(r, delay));
}
this.processing = false;
}
}
// Usage
const limiter = new RateLimiter(10); // 10 requests per minute
async function searchWithRateLimit(params) {
return limiter.execute(() => searchLinkedInPeople(params));
}
2. Cache Search Results
Reduce API costs and improve performance:
const cache = new Map();
const CACHE_DURATION = 24 * 60 * 60 * 1000; // 24 hours
function getCacheKey(params) {
return JSON.stringify(params);
}
async function searchWithCache(params) {
const key = getCacheKey(params);
const cached = cache.get(key);
if (cached && Date.now() - cached.timestamp < CACHE_DURATION) {
console.log("Returning cached results");
return cached.data;
}
const results = await searchLinkedInPeople(params);
cache.set(key, {
data: results,
timestamp: Date.now()
});
return results;
}
3. Use Realistic User Agents
When building custom scrapers, rotate user agents:
const userAgents = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36"
];
function getRandomUserAgent() {
return userAgents[Math.floor(Math.random() * userAgents.length)];
}
4. Implement Error Handling and Retry Logic
Handle transient failures gracefully:
async function searchWithRetry(params, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await searchLinkedInPeople(params);
} catch (error) {
console.log(`Attempt ${attempt} failed:`, error.message);
if (attempt === maxRetries) {
throw error;
}
// Exponential backoff
const delay = Math.pow(2, attempt) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
5. Combine Search with AI Entity Matching
For better accuracy when searching by name:
async function findPersonByName(fullName, additionalCriteria = {}) {
// Split name into components
const nameParts = fullName.trim().split(" ");
const firstName = nameParts[0];
const lastName = nameParts.slice(1).join(" ");
// Search with name components
const results = await searchLinkedInPeople({
firstName,
lastName,
...additionalCriteria
});
// Use fuzzy matching to find best match
const matches = results.results.map(person => {
const similarity = calculateNameSimilarity(fullName, person.full_name);
return { ...person, similarity };
});
// Sort by similarity and return best matches
return matches.sort((a, b) => b.similarity - a.similarity);
}
function calculateNameSimilarity(name1, name2) {
// Simple Levenshtein distance implementation
// In production, use a library like 'string-similarity'
const lower1 = name1.toLowerCase();
const lower2 = name2.toLowerCase();
if (lower1 === lower2) return 1.0;
if (lower1.includes(lower2) || lower2.includes(lower1)) return 0.8;
// More sophisticated matching here
return 0.5;
}
6. Respect LinkedIn's Resources
- Limit concurrent requests: Keep it to 1-2 per second
- Use off-peak hours: Reduce load during high-traffic times
- Cache aggressively: Don't re-fetch the same data
- Monitor for blocks: Implement circuit breakers if errors spike
Use Cases
Use Case 1: Recruiting at Scale
Scenario: A recruiting firm needs to find 1,000 software engineers in specific locations.
Implementation:
async function findCandidatesForRole(role) {
const geocodes = {
"San Francisco": "90000084",
"New York": "102571732",
"Austin": "101318387",
"Seattle": "103644278"
};
const allCandidates = [];
for (const [city, geocode] of Object.entries(geocodes)) {
console.log(`Searching in ${city}...`);
const results = await searchLinkedInPeople({
title: role.title,
geocodeLocation: geocode,
school: role.preferredSchool,
profileLanguage: "en",
page: 1
});
// Filter by experience level
const qualified = results.results.filter(person => {
return role.keywords.some(keyword =>
person.title.toLowerCase().includes(keyword)
);
});
allCandidates.push(...qualified);
// Rate limiting
await new Promise(resolve => setTimeout(resolve, 2000));
}
return allCandidates;
}
// Usage
findCandidatesForRole({
title: "software engineer",
keywords: ["senior", "lead", "staff", "principal"],
preferredSchool: "stanford university"
})
.then(candidates => {
console.log(`Found ${candidates.length} qualified candidates`);
// Export to CSV or ATS system
});
Business Value: Reduce recruiting time by 70%, increase candidate pipeline quality.
Use Case 2: CRM Enrichment
Scenario: A B2B company wants to enrich their CRM contacts with LinkedIn profiles.
Implementation:
async function enrichCRMContact(contact) {
try {
// Search by name and company
const results = await searchLinkedInPeople({
firstName: contact.firstName,
lastName: contact.lastName,
company: contact.company,
page: 1
});
if (results.results.length === 0) {
return { ...contact, linkedinMatch: null };
}
// Find best match
const bestMatch = results.results[0];
return {
...contact,
linkedinUrl: bestMatch.url,
linkedinTitle: bestMatch.title,
linkedinLocation: bestMatch.location,
linkedinId: bestMatch.id,
lastEnriched: new Date().toISOString()
};
} catch (error) {
console.error(`Failed to enrich ${contact.firstName} ${contact.lastName}`);
return contact;
}
}
// Batch enrichment
async function enrichCRMDatabase(contacts) {
const enriched = [];
for (const contact of contacts) {
const enrichedContact = await enrichCRMContact(contact);
enriched.push(enrichedContact);
// Rate limiting
await new Promise(resolve => setTimeout(resolve, 1000));
}
return enriched;
}
Business Value: Improve data quality, enable better targeting, increase sales effectiveness.
Use Case 3: Market Research
Scenario: Analyzing career paths and job market trends in specific industries.
Implementation:
async function analyzeJobMarket(industry, titles) {
const analysis = {
totalProfessionals: 0,
byTitle: {},
byLocation: {},
premiumPercentage: 0
};
for (const title of titles) {
const results = await searchLinkedInPeople({
title: title,
page: 1
});
analysis.totalProfessionals += results.pagination.total;
analysis.byTitle[title] = results.pagination.total;
// Analyze locations
results.results.forEach(person => {
const loc = person.location || "Unknown";
analysis.byLocation[loc] = (analysis.byLocation[loc] || 0) + 1;
if (person.is_premium) {
analysis.premiumPercentage++;
}
});
await new Promise(resolve => setTimeout(resolve, 2000));
}
analysis.premiumPercentage =
(analysis.premiumPercentage / analysis.totalProfessionals) * 100;
return analysis;
}
// Usage
analyzeJobMarket("Technology", [
"Software Engineer",
"Data Scientist",
"Product Manager",
"DevOps Engineer"
])
.then(analysis => {
console.log("Job Market Analysis:", analysis);
});
Business Value: Understand market dynamics, identify talent hotspots, inform business strategy.
Legal and Ethical Considerations
Understanding the Legal Landscape
LinkedIn's Terms of Service:
- Prohibits automated scraping without permission
- Reserves right to block or ban accounts
- Can pursue legal action for violations
Reality:
- Scraping public data is common practice
- LinkedIn rarely takes action against users scraping public profiles
- Focus is on preventing abuse and spam
Best Practices for Compliance
-
Only Scrape Public Data
- Don't access data requiring login
- Respect profile privacy settings
- Focus on publicly visible information
-
Respect Data Protection Laws
- GDPR (EU): Requires lawful basis for processing personal data
- CCPA (California): Mandates disclosure and opt-out rights
- Store data securely and allow deletion requests
-
Use Data Responsibly
- Don't use for spam or harassment
- Provide transparency in privacy policies
- Honor opt-out requests promptly
- Don't sell personal data without consent
-
Implement Data Retention Policies
- Only keep data as long as needed
- Regularly purge outdated information
- Secure sensitive personal information
Risk Mitigation Strategies
// Example: Compliance-focused data handling
class LinkedInDataHandler {
constructor() {
this.dataRetentionDays = 90;
}
async storeSearchResult(profile) {
// Only store necessary fields
const sanitized = {
linkedin_id: profile.id,
full_name: profile.full_name,
title: profile.title,
location: profile.location,
profile_url: profile.url,
collected_at: new Date(),
expires_at: new Date(Date.now() + this.dataRetentionDays * 24 * 60 * 60 * 1000)
};
// Don't store: email, phone, detailed personal info
return sanitized;
}
async purgeExpiredData() {
// Automatically delete data past retention period
const cutoff = new Date(Date.now() - this.dataRetentionDays * 24 * 60 * 60 * 1000);
// Delete records where expires_at < cutoff
}
}
Ethical Guidelines
- Transparency: Be clear about data collection in your privacy policy
- Purpose Limitation: Only use data for stated purposes
- Accuracy: Keep data current and correct
- Security: Protect data with appropriate safeguards
- Accountability: Have processes to handle complaints and requests
Conclusion
Searching LinkedIn for people without using the official API is not only possible but often more practical for most developers and businesses. While LinkedIn's official People Search API remains restrictive and difficult to access, alternative methods provide flexible, scalable solutions for finding and extracting professional profile data.
Key Takeaways
Official LinkedIn API:
- ❌ Requires partnership and approval
- ❌ Limited search capabilities
- ❌ OAuth complexity
- ✅ Official and stable
LinkedIn Scraper APIs:
- ✅ No OAuth required
- ✅ Easy integration
- ✅ Comprehensive search filters
- ✅ Affordable pricing
- ⚠️ Violates LinkedIn ToS
Best Approach:
- Use specialized scraper APIs for most use cases
- Implement rate limiting and caching
- Focus on public data only
- Comply with data protection laws
- Use data ethically and responsibly
Getting Started
For developers and businesses needing to search LinkedIn people at scale, using a reliable LinkedIn scraper API like the Fresh LinkedIn Scraper API provides:
- Simple REST API: Easy integration in any language
- Comprehensive Search: Filter by name, title, company, location
- Structured Data: Clean JSON responses with all profile fields
- Pagination Support: Access thousands of results
- Maintained Infrastructure: No need to build or maintain scrapers
- Flexible Pricing: Pay only for what you use
Related Resources
Deepen your LinkedIn data extraction knowledge with these guides:
- LinkedIn Data API vs Scraping: Which Should You Use in 2025?
- LinkedIn Profile Scraper with Node.js: Complete Guide
- Top 5 LinkedIn API Alternatives in 2025
Ready to start searching LinkedIn people?
Whether you're recruiting top talent, generating leads, or enriching your CRM data, the ability to search LinkedIn programmatically without API restrictions opens up powerful opportunities. Start with a professional LinkedIn scraper API and unlock the full potential of LinkedIn's professional network data today.
