How to Scrape LinkedIn Profiles Without Logging In (Python, 2026 Guide)
Building a lead list or a candidate shortlist on LinkedIn by hand works for 20 people and falls apart at 500. This guide shows how to scrape LinkedIn profiles from Python by job title, company, location, school or name, without a LinkedIn account or Sales Navigator. You get one row per person with the name, headline, company, location and profile link.
Here's the whole LinkedIn scraper in Python. Install the client with pip install apify-client, paste your Apify token, and run:
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("automly/linkedin-people-search-scraper").call(run_input={
"jobTitles": ["Product Manager"],
"companies": ["Stripe"],
"locations": ["San Francisco"],
"maxResults": 50,
})
for person in client.dataset(run["defaultDatasetId"]).iterate_items():
print(person["name"], "|", person["headline"], "|", person["linkedinUrl"])
Below I go through the inputs, the output, the limits and the price.
Why not the official API?
LinkedIn does have APIs, but none that an ordinary developer can sign up for lets you search for people. I checked the official docs in October 2026:
| Option | Access needed | Cost | What you get |
|---|---|---|---|
| Sign In with LinkedIn (OpenID Connect) | Self-serve app, and each person must sign in to it | No fee at present (API Terms, 8.2) | The signed-in member's own name, photo and email |
| Profile API | Developers approved by LinkedIn | No fee at present (same API Terms) | Another member's profile only if you already have their Person ID, which comes from other limited-access APIs, and you may never store it |
| Sales Navigator Application Platform (SNAP) | Approved SNAP partner | Not published | Sales Navigator data, such as matched public profile info, inside a partner's app |
| This scraper | An Apify account | $1.50 per 1,000 profiles | People search by title, company, place, school or name |
According to LinkedIn's Getting Access page, the only permissions open to every developer are sign-in (profile, email) and posting on the member's behalf. Everything else goes through a partner program that LinkedIn has to approve. And nothing in that permissions list lets you find other members by job title or company.
Step 1: Get your Apify token
Create a free Apify account, then copy your API token from your Apify Console settings. Every account gets $5 of free usage each month, which is plenty for testing.
Step 2: Choose your filters
The actor reads these input fields:
| Field | What it does |
|---|---|
jobTitles |
List of titles, e.g. ["Product Manager", "Head of Sales"]
|
companies |
Company names or LinkedIn company links |
locations |
Cities, regions or countries |
schools |
Universities or schools |
searchQuery |
Any other words the profile should mention, e.g. "fintech"
|
firstName, lastName
|
Find a person by name |
maxResults |
Stop after this many profiles in the whole run (default 100) |
maxResultsPerSearch |
Cap each search, e.g. 50 per city |
fullProfiles |
Also read each profile page for more fields (default off) |
Every combination of one title, one company, one location and one school counts as one search. Two titles and three cities give you six searches, and a person found by more than one of them is saved once.
To find one person by name:
run_input = {"firstName": "Jane", "lastName": "Doe", "companies": ["Stripe"]}
The locations filter is strict. People whose profile shows another place, or no place at all, are left out. Add the country ("Berlin, Germany") to also keep people who list only their country.
Step 3: Read the results
Each row from the search looks like this (name and link anonymized):
{
"linkedinUrl": "https://www.linkedin.com/in/jane-doe-123",
"publicIdentifier": "jane-doe-123",
"name": "Jane D.",
"headline": "Product Manager @ Stripe",
"company": "Stripe",
"school": "University of Michigan",
"location": "San Francisco Bay Area",
"connections": "500+",
"query": "\"Product Manager\" \"Stripe\" \"San Francisco\""
}
query tells you which search found the person, which helps when one run covers many cities or titles.
Step 4: Add profile details
Set "fullProfiles": True and each row gets extra fields: about, countryCode, photo, followers, badges, currentCompany, currentPositions, pastCompanies, education, recentPostUrls and profileVisibility. A typical row looks like this (trimmed, anonymized):
{
"name": "John Smith",
"headline": "Data Scientist at Shopify",
"location": "Toronto, Ontario, Canada",
"countryCode": "CA",
"followers": 410,
"connections": "500+",
"currentCompany": "Shopify",
"education": [{"name": "University of Waterloo", "startDate": "2012", "endDate": "2016"}],
"experience": [],
"profileVisibility": "limited"
}
See the empty experience? That's normal for most people, which brings me to the limits.
Step 5: Know what a logged-out profile hides
LinkedIn decides how much a visitor who isn't logged in can see, and the scraper can only return that. Plan around it:
- Most profiles are limited.
profileVisibilityisfull,limitedorhidden. Work history (experience) andjobTitlesonly come back forfullprofiles, which are mostly creators, founders and executives. In the two detail runs I checked, 22 of 35 people were limited, 13 were hidden and none were full. On limited profiles,aboutwas filled in on only 9 of 22. - Hidden profiles keep only the search data. You still get the name, headline and, when listed, company, location, school and connections.
- Some last names are cut to an initial. About 1 in 10 rows in my runs had a name like "Jane D." LinkedIn shows those members that way to logged-out visitors, and that's all the scraper receives.
- There are no emails or phone numbers.
- Filters match words on the profile, not LinkedIn's internal fields, so someone who mentions Stripe as a past client can show up. In the runs I checked, 170 of 173 rows had the searched company in the headline or company field. Filter the
companycolumn if you need exact matches. - Seniority, function and company-size filters need an account. Put words like "Senior" or "Director" into the job title instead.
Step 6: Save to CSV
import csv
items = list(client.dataset(run["defaultDatasetId"]).iterate_items())
fields = ["name", "headline", "company", "location", "school",
"connections", "linkedinUrl", "query"]
with open("linkedin_profiles.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=fields, extrasaction="ignore")
writer.writeheader()
writer.writerows(items)
extrasaction="ignore" drops any fields you didn't list. With pandas, pd.DataFrame(items).to_csv("linkedin_profiles.csv", index=False) works too.
What else can you scrape?
The same code works for my other LinkedIn scrapers. Change the actor name and the input:
-
LinkedIn Profile Scraper + Email Finder: profiles from links or names, plus work emails. Feed it the
linkedinUrlcolumn from this guide. - LinkedIn Company Employees Scraper: the people who work at a company.
- LinkedIn Posts Scraper: posts from people and company pages.
- LinkedIn Post Search Scraper: posts about any topic.
Scaling
One search returns up to about 1,000 profiles. To get more, split it: add more cities, titles or schools, since each combination is its own search. Use maxResultsPerSearch when you want an even spread, for example 100 per city. The run report shows how many profiles each search found, and a search that stops near 1,000 has more to give if you split it further.
In my tests a run with several searches returned about 1,500 profiles per minute, and a single search about 500.
No code? Use the Apify Console
Open the actor page, click Try for free, type the titles, companies and cities one per line, and click Start. Results download as Excel, CSV, JSON, HTML or XML, or go straight to Google Sheets. Save the input as a task and put it on a schedule if you want the same search every week.
Cost
You pay per profile saved: $1.50 per 1,000 profiles from the search, and $2.50 per 1,000 with profile details. Limited and hidden profiles are charged at the lower price even when details are on. Each run also has a start fee of a fraction of a cent. The price is the same on every Apify plan.
The $5 of free monthly usage covers about 3,300 search profiles, or at least 2,000 with details. Check the Pricing tab on the actor page before a big run, since prices can change.
Responsible use
This is public data that LinkedIn shows to logged-out visitors, but a profile is still personal data. Under the GDPR and CCPA you need a lawful reason to collect it, should keep only what you need, and have to honor deletion requests. LinkedIn's User Agreement also restricts scraping, so read it and talk to a lawyer before you build a product or an outreach campaign on this data.
Links
- Actor: LinkedIn People Search Scraper (No Login)
- Apify Python client docs
- LinkedIn API access overview
If you want a LinkedIn profile scraper you can call from Python without logging in, try LinkedIn People Search Scraper on Apify. Your first runs fit in the free $5.
Top comments (0)