LinkedIn Profile Scraper – Bulk, No Cookies avatar

LinkedIn Profile Scraper – Bulk, No Cookies

Pricing

from $0.80 / 1,000 successful profiles

Go to Apify Store
LinkedIn Profile Scraper – Bulk, No Cookies

LinkedIn Profile Scraper – Bulk, No Cookies

Enrich LinkedIn profiles in bulk from URLs, usernames or public tokens. Get available roles, companies, experience, education and skills as JSON for CRM, recruiting and SaaS workflows. No LinkedIn cookies required. $0.80 per 1,000 successful profiles.

Pricing

from $0.80 / 1,000 successful profiles

Rating

0.0

(0)

Developer

VirAP | LinkedIn Data API

VirAP | LinkedIn Data API

Maintained by Community

Actor stats

0

Bookmarked

5

Total users

3

Monthly active users

13 hours ago

Last modified

Categories

Share

LinkedIn profile data for CRM, recruiting and SaaS

Turn known LinkedIn profile URLs, usernames or public tokens into structured professional data. Enrich one profile or a batch with available names, current roles, companies, work experience, education, skills and languages. No LinkedIn cookies or password required.

  • Bulk input: up to 10,000 identifiers per run, processed in parallel.
  • Useful context: available employer and school details alongside profile sections.
  • Successful-profile pricing: $0.80 per 1,000 delivered profiles ($0.0008 each).
  • Your workflow: Console, API, saved tasks, schedules and dataset exports.

Try the Actor with one profile first, or copy the CRM example. You supply a profile you already know. This tool does not search for people by job title or return email addresses or phone numbers.

Who is this for?

TeamPractical useUseful output fields
B2B sales and automationAdd professional context to existing CRM contacts; build enrichment steps in n8n or Makecurrent_title, current_company_name, seniority, industry, current_companies[]
Recruiters and agenciesBring available experience, education and skills into candidate researchsections.experiences[], sections.educations[], sections.skills[], sections.languages[]
API developers and SaaS teamsAdd structured profile records to applications and data pipelinestoken, slug, headline, last_refreshed_at, served_from

Fields and nested company/school details depend on profile availability. Inspect the returned record before updating your system.

How to scrape LinkedIn profiles

  1. Open Input and paste profile URLs, usernames or public profile tokens into LinkedIn profile URLs or usernames, one per line.
  2. Start with one or a few profiles. Choose a maximum run cost and a timeout that fits the whole batch.
  3. Click Start. If data collection is pending, the Actor follows it automatically, up to eight minutes per profile.
  4. Open Output for the profile dataset and Run summary / final result for completion details. Download JSON, CSV or Excel; JSON preserves nested sections.

Input and processing modes

Replace the example slug with a profile you want to enrich:

{
"profileUrls": ["https://www.linkedin.com/in/your-profile-slug"],
"maxConcurrency": 10
}

For an existing single-profile task or API integration:

{"token":"https://www.linkedin.com/in/your-profile-slug"}

Accepted identifiers are personal linkedin.com/in/... URLs, bare usernames (slugs), or stable public profile tokens beginning ACoAA or ACwAA. A public profile token identifies a person; it is different from your Apify API token.

Mode or limitBehaviour
Batch Actor runA nonempty profileUrls list selects bulk mode. Only identical entries after trimming whitespace are deduplicated. A URL and a slug for the same person can be processed twice.
Batch sizeOnly the first 10,000 collected identifiers are processed. Split larger lists into batches.
Parallel requestsDefault 10; supported range 1–50. Actual speed depends on response time and rate limits; concurrency is not a profiles-per-minute guarantee.
Single Actor runLeave the list empty and provide exactly one of token or legacy profile_url.
Mixed inputWith a nonempty list, nonempty token / profile_url values are appended to the batch and share its 10,000 cap. Use one input style per run.
Standby APIOne identifier per HTTP request: token or profile_url. It does not accept profileUrls or maxConcurrency.
FreshnessCheck last_refreshed_at and served_from. This Actor does not expose a live input option.

The run timeout applies to the entire batch. A large batch may need smaller chunks or a longer timeout, even though collection can wait up to eight minutes for each profile. Avoid immediately retrying an interrupted request: it may already be processing.

Understanding successful, partial and interrupted results

Every delivered profile is saved as a dataset item and charged individually. Run status SUCCEEDED does not mean every input produced a profile.

SituationWhat to inspect
Completed batchThe OUTPUT record has total, delivered, not_found, failed and skipped counts. Read it even when the run succeeded. skipped can indicate the maximum charge was reached.
Not-found single profileThe dataset is empty and OUTPUT is {"found":false}. No successful-profile event is charged.
Failed batchRead any dataset records and OUTPUT if present. An all-error batch writes error details before ending as failed.
Timed-out or aborted runRecover the dataset items already delivered and charged. The final OUTPUT summary may be missing because it is written only after batch completion.
Failed single-profile runOUTPUT may be missing. Use the run status message and log for diagnostics.

Detailed errors[] and not_found_inputs[] lists are capped at 500 entries each; counts can be higher. The summary is not a complete list of skipped inputs. Keep your original input list outside the Actor, reconcile it with delivered identities and available error details, and review unmatched inputs before retrying. Do not blindly rerun the entire batch.

Keep input-to-record matching explicit. Batch results are not guaranteed to follow input order and do not echo the original input. For row-specific CRM or ATS updates, use a one-profile run and retain the CRM row ID alongside the returned run ID in your workflow. In a batch, join only on a stable public token you already stored or an unambiguous normalized profile slug (the /in/ part of a URL). Keep ambiguous matches for review; never join by array position.

Output: complete JSON response example

A dataset export returns a JSON array of profile objects. This illustrative example preserves all 38 documented top-level fields and shows nested experience/company, education/school, skills, languages and current companies. Names, identifiers and URLs are fictitious. Real fields may be missing, null or empty.

[
{
"token": "ACoAA_EXAMPLE_PUBLIC_PROFILE_TOKEN",
"member_id": 1234567,
"slug": "alex-example",
"first_name": "Alex",
"last_name": "Example",
"headline": "VP of Engineering | Building scalable software systems",
"current_company_id": 123456,
"location_country": "FR",
"last_refreshed_at": "2026-11-02T13:00:00+00:00",
"served_from": "live",
"current_title": "VP of Engineering",
"seniority": "vp",
"job_function": null,
"sub_role": "of engineering",
"summary": "Engineering leader focused on reliable backend systems.\nExperienced in team leadership, data platforms and software architecture.",
"current_company_name": "Example Company",
"industry": "Software Development",
"industry_id": null,
"location": "Paris, France",
"region": "fr",
"geo_id": null,
"photo_root_url": "https://example.com/images/",
"photo_segment": "profile-photo.jpg",
"num_connections": 500,
"follower_count": 750,
"personal_website": "https://example.com",
"photo_url": "https://example.com/images/profile-photo.jpg",
"recommendations": [
{
"recommender_urn": "urn:li:ts_profile:ACoAA_EXAMPLE_RECOMMENDER",
"text": "Alex brings strong engineering leadership and clear communication.",
"relationship": "RECOMMENDER_STUDIED_WITH_RECOMMENDEE",
"created": 1760000000000
}
],
"certifications": [
{
"position": 0,
"name": "Example Technical Certification",
"authority": "Example Academy",
"url": null,
"company_id": null,
"started_on": "06/2024",
"license_number": null,
"ended_on": null
}
],
"courses": [],
"honors": [],
"volunteering": [],
"publications": [],
"websites_extra": [],
"patents": [],
"test_scores": [
{
"name": "Example Language Test",
"score": "945",
"id": 7654321,
"$type": "com.linkedin.talent.common.TestScore"
}
],
"sections": {
"skills": [
{
"name": "TypeScript",
"endorsement_count": 9,
"assessment_verified": true
},
{
"name": "SQL",
"endorsement_count": 7,
"assessment_verified": null
}
],
"languages": [
{
"name": "English",
"proficiency": "FULL_PROFESSIONAL"
},
{
"name": "French",
"proficiency": "NATIVE_OR_BILINGUAL"
}
],
"experiences": [
{
"title": "VP of Engineering",
"company_name": "Example Company",
"company_id": 123456,
"location": "Paris, France",
"description": "Lead the engineering team and technical strategy.",
"started_on": "2024-01-01",
"ended_on": null,
"is_current": true,
"employment_status": "Permanent",
"started_on_display": "2024-01",
"ended_on_display": null,
"is_top_card": true,
"entreprise": {
"website_url": "https://example.com",
"slug": "example-company",
"name": "Example Company",
"industry": "Software Development",
"staff_count": 42,
"staff_count_range": "11-50",
"country_code": "FR",
"region": "fr",
"founded_year": 2020,
"revenue_range": null,
"company_type": "Privately Held",
"follower_count": 1200,
"hq_city": "Paris",
"hq_region": null,
"description": "Example Company develops software for business teams.",
"tagline": "Software that helps teams work together",
"specialties": [
"Software",
"Data"
],
"logo_url": "https://example.com/images/company-logo.png",
"claimable": false,
"linkedin_url": "https://www.linkedin.com/company/example-company/"
}
}
],
"educations": [
{
"school_name": "Example University",
"school_id": 654321,
"degree_name": "Master's Degree",
"field_of_study": "Computer Science",
"started_on": "2014-09-01",
"ended_on": "2016-06-01",
"grade": null,
"activities": null,
"description": "An illustrative computer science degree.",
"started_on_display": "2014-09",
"ended_on_display": "2016-06",
"is_top_card": true,
"ecole": {
"slug": "example-university",
"description": "An illustrative university record.",
"logo_url": "https://example.com/images/university-logo.png",
"website": "https://example.edu",
"slogan": "Learning through practice",
"staff_count": 500,
"student_count": null,
"school_type": "Organization",
"address_street": "1 Example Street",
"address_locality": "Paris",
"address_region": null,
"address_postal": "75001",
"address_country": "FR",
"linkedin_url": "https://www.linkedin.com/school/example-university/"
},
"kind": "institution"
}
]
},
"current_companies": [
{
"company_id": 123456,
"company_name": "Example Company",
"titles": [
"VP of Engineering"
],
"entreprise": {
"website_url": "https://example.com",
"slug": "example-company",
"name": "Example Company",
"industry": "Software Development",
"staff_count": 42,
"staff_count_range": "11-50",
"country_code": "FR",
"region": "fr",
"founded_year": 2020,
"revenue_range": null,
"company_type": "Privately Held",
"follower_count": 1200,
"hq_city": "Paris",
"hq_region": null,
"description": "Example Company develops software for business teams.",
"tagline": "Software that helps teams work together",
"specialties": [
"Software",
"Data"
],
"logo_url": "https://example.com/images/company-logo.png",
"claimable": false,
"linkedin_url": "https://www.linkedin.com/company/example-company/"
},
"is_top_card": true
}
]
}
]

Main output fields

DataFields
Identitytoken, member_id, slug, first_name, last_name, headline, summary
Current role and companycurrent_title, current_company_name, current_company_id, current_companies[]
Locationlocation, location_country, region, geo_id
Experience and educationsections.experiences[], sections.educations[], including available company and school objects
Skills and languagessections.skills[], sections.languages[]
Additional sectionscertifications[], recommendations[], volunteering[], publications[], test_scores[] and other available sections
Audience and freshnessnum_connections, follower_count, last_refreshed_at, served_from

The Standby endpoint returns the same profile object directly, without the surrounding dataset array. A Standby not-found response is {"found":false}. No account-credit or quota envelope is included in profile output.

Pricing

$0.80 per 1,000 successfully delivered profiles ($0.0008 per profile event). The current rate is always shown in the Pricing tab.

Successful profilesProfile-event cost
100$0.08
1,000$0.80
10,000$8.00

Invalid input, not-found outcomes, rate limits, timeouts and service errors do not themselves incur a successful-profile event. Profiles delivered earlier in a partially completed or interrupted run can already be charged. Standard run event pricing includes platform usage under the current Actor configuration. Standby platform usage is charged separately by Apify, including active server resources.

Start small and set a maximum run charge. Available trial credits and account limits depend on your Apify plan.

API integration: asynchronous runs

Use asynchronous runs for batches and workflows with short HTTP timeouts. The Actor's stable ID continues to identify it if a display name changes. Replace the input slug and set your own APIFY_TOKEN environment variable. These cURL examples use Bash syntax.

1. Start a run

curl --request POST \
'https://api.apify.com/v2/acts/SgkHsEfHzlPxLQScQ/runs?timeout=1200&maxTotalChargeUsd=0.02' \
--header "Authorization: Bearer $APIFY_TOKEN" \
--header 'Content-Type: application/json' \
--data '{"profileUrls":["https://www.linkedin.com/in/your-profile-slug"],"maxConcurrency":1}'

Save data.id from the response as your run ID, together with your original inputs or CRM row reference.

2. Wait for a terminal status

curl --header "Authorization: Bearer $APIFY_TOKEN" \
'https://api.apify.com/v2/actor-runs/YOUR_RUN_ID?waitForFinish=60'

Repeat while the status is transitional. Terminal statuses include SUCCEEDED, FAILED, TIMED-OUT and ABORTED. From the returned data, save defaultDatasetId and defaultKeyValueStoreId. For every terminal status, perform both recovery steps below; failure must not discard data already delivered.

3. Retrieve delivered profiles, including partial results

curl --header "Authorization: Bearer $APIFY_TOKEN" \
'https://api.apify.com/v2/datasets/YOUR_DEFAULT_DATASET_ID/items?format=json&clean=true'

This returns an array, which can be empty. Save the delivered records before reading the summary or deciding whether to retry unmatched inputs.

4. Read the final result or summary when available

curl --header "Authorization: Bearer $APIFY_TOKEN" \
'https://api.apify.com/v2/key-value-stores/YOUR_DEFAULT_KEY_VALUE_STORE_ID/records/OUTPUT'

Inspect failed, skipped and not_found for a batch. A missing OUTPUT record (HTTP 404) is expected for some interrupted runs or failed single-profile runs. Keep the dataset already saved and inspect the run log/status. Other HTTP errors need attention; they are not a not-found profile or proof that the summary is absent.

Python example

This example uses the current apify-client 3.4 interface. It records the run ID before waiting and writes delivered profiles to a run-specific JSONL file, including on terminal failure. To recover an existing run, set APIFY_RUN_ID to its ID before running the script; it will not start another run.

$pip install 'apify-client>=3.4,<4'
import json
import os
import sys
from datetime import timedelta
from decimal import Decimal
from pathlib import Path
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
actor_id = "SgkHsEfHzlPxLQScQ"
run_id = os.environ.get("APIFY_RUN_ID")
if not run_id:
started = client.actor(actor_id).start(
run_input={
"profileUrls": ["https://www.linkedin.com/in/your-profile-slug"],
"maxConcurrency": 1,
},
run_timeout=timedelta(minutes=20),
max_total_charge_usd=Decimal("0.02"),
restart_on_error=False,
)
run_id = started.id
print(f"Run ID: {run_id} (resume with APIFY_RUN_ID)", flush=True)
Path(f"run-{run_id}.txt").write_text(run_id, encoding="utf-8")
run = client.run(run_id).wait_for_finish()
if run is None:
raise RuntimeError(f"Inspect existing run {run_id} in Console before retrying.")
if run.act_id != actor_id:
raise RuntimeError("APIFY_RUN_ID belongs to a different Actor.")
status = getattr(run.status, "value", run.status)
# Save delivered records before attempting the optional summary.
destination = Path(f"profiles-{run.id}.jsonl")
staging = destination.with_suffix(".jsonl.part")
delivered = 0
with staging.open("w", encoding="utf-8") as result_file:
for profile in client.dataset(run.default_dataset_id).iterate_items():
result_file.write(json.dumps(profile, ensure_ascii=False) + "\n")
delivered += 1
staging.replace(destination)
print(f"Run {run.id}: {status}; saved {delivered} profiles to {destination}")
if status != "SUCCEEDED":
print("Inspect run status/logs; reconcile saved profiles before retrying.",
file=sys.stderr)
try:
record = client.key_value_store(run.default_key_value_store_id).get_record("OUTPUT")
output = record["value"] if record else None
Path(f"output-{run.id}.json").write_text(
json.dumps({"run_id": run.id, "status": status, "output": output},
ensure_ascii=False, indent=2),
encoding="utf-8",
)
except Exception as error:
print(f"Profiles saved; OUTPUT could not be read/saved ({type(error).__name__}). "
"Inspect Console; do not treat this as a not-found profile.", file=sys.stderr)
else:
if output is None:
print("OUTPUT record absent; retain inputs and inspect run status/logs.",
file=sys.stderr)
elif output.get("found") is False:
print("Profile not found; no successful-profile event.", file=sys.stderr)
elif any(output.get(key, 0) for key in ("failed", "skipped", "not_found")):
print("Some inputs were not delivered; review the saved summary and inputs.",
file=sys.stderr)

If the initial start request is interrupted before it returns an ID, inspect Console before starting another run. For interruptions after an ID is saved, resume with APIFY_RUN_ID. A .jsonl.part file retains fetched lines if a dataset download is interrupted. See the Python client reference and Apify REST API.

n8n and Make workflow guide

Use an HTTP request step in either tool with your Apify Bearer token stored in its credential settings. This is a configuration recipe, rather than an importable template.

  1. Keep each source CRM/ATS row ID and its profile identifier in your workflow. For predictable row matching, use one profile per run and associate the returned run ID with that row.
  2. POST the batch-shaped one-profile JSON above to the standard runs endpoint. Save the run ID.
  3. Wait and poll run status. Handle all terminal statuses; a success-only webhook or filter misses failed, aborted and timed-out runs with partial results. If you use webhooks, configure terminal failure paths as well.
  4. On every terminal status, fetch dataset items and attempt to fetch OUTPUT. Allow a missing summary record for interrupted runs, while keeping delivered data. Review failed, skipped, not_found and the run status before completing the workflow.
  5. Map useful fields into your CRM, ATS or application: current_title, current_company_name, sections.experiences[], sections.educations[] and sections.skills[]. Retain nested JSON where appropriate.
  6. For larger batches, match by stable token or an unambiguous normalized slug. Do not match by output position. Keep unmatched inputs for review and reconcile delivered profiles before a selective retry.

Use Apify saved tasks and schedules for repeatable runs. The standard API flow is generally easier to manage than a long HTTP connection when collection takes several minutes.

Standby API: one profile per request

For an HTTP response containing one profile object, use the hostname verified in the Actor's Server tab:

curl --request POST \
'https://linkedin-scraper-2--linkedin-profile-scraper.apify.actor/v1/enrich/single' \
--header "Authorization: Bearer $APIFY_TOKEN" \
--header 'Content-Type: application/json' \
--data '{"token":"https://www.linkedin.com/in/your-profile-slug"}'

The legacy route /v1/people/enrich accepts {"profile_url":"https://www.linkedin.com/in/your-profile-slug"}. GET /health checks service availability. A Standby request waits up to 150 seconds for enrichment; use an asynchronous Actor run for longer collections. Standby does not write results to the standard dataset. Save the response in your caller. Standby platform usage is additional to successful-profile events.

FAQ and support

Do I need a LinkedIn account, cookies or a CLI? No LinkedIn login, password, cookies or CLI installation is required to run the Actor in Apify Console.

Is the data always complete or live? Availability and freshness vary. Inspect last_refreshed_at, served_from and missing/null fields. There is no fixed response-time or throughput guarantee.

Why did a successful run return fewer profiles? Check OUTPUT for not-found, failed or skipped inputs. A charge limit can leave remaining profiles skipped. Exact trimmed-string deduplication also reduces duplicates; different identifier formats for the same person are not deduplicated by identity.

Can I get email addresses, phone numbers or search results? This Actor enriches known profile identifiers with professional profile data. It does not provide those features.

How do I get help? Open the Issues tab with the run ID and a concise description. Leave credentials out of the report.

This is an independent tool, not affiliated with LinkedIn or Microsoft. Use the tool and returned data in accordance with applicable laws, privacy requirements and platform terms.