# LinkedIn Profile Scraper – Bulk, No Cookies (`linkedin_scraper/linkedin-profile-scraper`) Actor

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.

- **URL**: https://apify.com/linkedin_scraper/linkedin-profile-scraper.md
- **Developed by:** [VirAP | LinkedIn Data API](https://apify.com/linkedin_scraper) (community)
- **Stats:** 5 total users, 3 monthly users, 88.4% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 1,000 successful profiles

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### 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](https://console.apify.com/actors/SgkHsEfHzlPxLQScQ) with one profile first, or [copy the CRM example](https://apify.com/linkedin_scraper/linkedin-profile-scraper/examples/enrich-a-linkedin-profile-for-crm). 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?

| Team | Practical use | Useful output fields |
|---|---|---|
| B2B sales and automation | Add professional context to existing CRM contacts; build enrichment steps in n8n or Make | `current_title`, `current_company_name`, `seniority`, `industry`, `current_companies[]` |
| Recruiters and agencies | Bring available experience, education and skills into candidate research | `sections.experiences[]`, `sections.educations[]`, `sections.skills[]`, `sections.languages[]` |
| API developers and SaaS teams | Add structured profile records to applications and data pipelines | `token`, `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:

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

For an existing single-profile task or API integration:

```json
{"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 limit | Behaviour |
|---|---|
| Batch Actor run | A 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 size | Only the first 10,000 collected identifiers are processed. Split larger lists into batches. |
| Parallel requests | Default 10; supported range 1–50. Actual speed depends on response time and rate limits; concurrency is not a profiles-per-minute guarantee. |
| Single Actor run | Leave the list empty and provide exactly one of `token` or legacy `profile_url`. |
| Mixed input | With 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 API | One identifier per HTTP request: `token` or `profile_url`. It does not accept `profileUrls` or `maxConcurrency`. |
| Freshness | Check `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.**

| Situation | What to inspect |
|---|---|
| Completed batch | The `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 profile | The dataset is empty and `OUTPUT` is `{"found":false}`. No successful-profile event is charged. |
| Failed batch | Read any dataset records and `OUTPUT` if present. An all-error batch writes error details before ending as failed. |
| Timed-out or aborted run | Recover 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 run | `OUTPUT` 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.

```json
[
  {
    "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

| Data | Fields |
|---|---|
| Identity | `token`, `member_id`, `slug`, `first_name`, `last_name`, `headline`, `summary` |
| Current role and company | `current_title`, `current_company_name`, `current_company_id`, `current_companies[]` |
| Location | `location`, `location_country`, `region`, `geo_id` |
| Experience and education | `sections.experiences[]`, `sections.educations[]`, including available company and school objects |
| Skills and languages | `sections.skills[]`, `sections.languages[]` |
| Additional sections | `certifications[]`, `recommendations[]`, `volunteering[]`, `publications[]`, `test_scores[]` and other available sections |
| Audience and freshness | `num_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](https://apify.com/linkedin_scraper/linkedin-profile-scraper/pricing).

| Successful profiles | Profile-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](https://apify.com/pricing).

### 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

```bash
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

```bash
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

```bash
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

```bash
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.

```bash
pip install 'apify-client>=3.4,<4'
```

```python
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](https://docs.apify.com/api/client/python/reference/class/ActorClient) and [Apify REST API](https://docs.apify.com/api/v2).

### 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:

```bash
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](https://apify.com/linkedin_scraper/linkedin-profile-scraper/issues) 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.

# Actor input Schema

## `profileUrls` (type: `array`):

One per line: personal linkedin.com/in/ URLs, usernames or public profile tokens beginning ACoAA or ACwAA. Only identical entries after trimming whitespace are removed. Use up to 10,000 identifiers and one input style per run.

## `maxConcurrency` (type: `integer`):

Maximum simultaneous profile enrichments (1–50), default 10. Actual speed depends on response time and rate limits.

## `token` (type: `string`):

Alternative to the list above for one profile: a profile URL, username or public token beginning ACoAA or ACwAA. Existing tasks and API calls that use this field keep working.

## `profile_url` (type: `string`):

Compatibility input for existing tasks. Leave token empty when supplying this field.

## Actor input object example

```json
{
  "profileUrls": [
    "https://www.linkedin.com/in/shengdaliu"
  ],
  "maxConcurrency": 10,
  "token": "lea-martin"
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `result` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "profileUrls": [
        "https://www.linkedin.com/in/shengdaliu"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("linkedin_scraper/linkedin-profile-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "profileUrls": ["https://www.linkedin.com/in/shengdaliu"] }

# Run the Actor and wait for it to finish
run = client.actor("linkedin_scraper/linkedin-profile-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "profileUrls": [
    "https://www.linkedin.com/in/shengdaliu"
  ]
}' |
apify call linkedin_scraper/linkedin-profile-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,linkedin_scraper/linkedin-profile-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/SgkHsEfHzlPxLQScQ/builds/qhD7E9fDptTiYGD3T/openapi.json
